Как захватить выражение с помощью терминала bash?

Как захватить выражение с помощью терминала bash?

У меня есть файлы с одинаковой номенклатурой, log_<date>.txtразбросанные во вложенном каталоге root. Где <date>находится дата создания файлов в формате ГГГГммддЧЧММ. Для проблем совместимости кода я хотел бы переименовать все файлы в<date>_log.txt

Я знаю, что мне придется захватить <date>выражение, но как это сделать в bash? Кроме того, как это сделать во вложенном каталоге?

решение1

Допустим, у вас есть имя одного из этих файлов в переменной оболочки name. Удаление log_бита из значения переменной затем выполняется с помощью ${name#log_}, а удаление .txtбита выполняется с помощью ${name%.txt}. Остается дата, а в каком она формате, по сути, не интересно. Было бы легко взять удаленное значение и добавить _log_.txtв конец, чтобы создать новое имя.

Чтобы сделать это для файлов в одном каталоге, используйте цикл. Обратите внимание, что теперь нам также нужно удалить имя каталога для начала значения переменной.

shopt -s nullglob

for name in root/log_*.txt; do
    newname=root/${name#root/log_}    # remove prefix, add root/ back
    newname=${newname%.txt}_log.txt   # remove suffix, add _log.txt back

    printf 'Would move "%s" to "%s"\n' "$name" "$newname"
    # mv -i "$name" "$newname"
done

Я закомментировал саму mvкоманду для безопасности. Вам следует выполнить тестовый запуск один раз, чтобы увидеть, что произойдет в первую очередь.

Опция оболочки nullglobделает bashоболочкуудалятьнесовпадающие шаблоны, а не оставлять их неразвернутыми. Это означает, что цикл не будет запущен вообще, если log_*.txtв указанном каталоге не будет файлов.

Это можно применить к любому такому файлу в текущем каталоге (включая текущий каталог) рекурсивно с помощьюболее или менеепросто изменяем шаблон, по которому мы проходим цикл:

shopt -s globstar nullglob

for name in ./**/log_*.txt; do
    dirpath=${name%/*}                      # get path of directory
    newname=$dirpath/${name#$dirpath/log_}  # remove prefix, add directory path back
    newname=${newname%.txt}_log.txt         # remove suffix, add _log.txt back

    printf 'Would move "%s" to "%s"\n' "$name" "$newname"
    # mv -i "$name" "$newname"
done

Другие изменения включают включение globstarопции оболочки, которая позволяет нам использовать **для сопоставления вниз в подкаталоги. Также я выбираю путь к каталогу, чтобы иметь возможность добавить его к новому имени.

Вы также можете использовать findдля генерации входных данных для цикла:

find . -type f -name 'log_*.txt' -exec sh -c '
    for name do
        dirpath=${name%/*}                      # get path of directory
        newname=$dirpath/${name#$dirpath/log_}  # remove prefix, add directory path back
        newname=${newname%.txt}_log.txt         # remove suffix, add _log.txt back

        printf "Would move \"%s\" to \"%s\"\n" "$name" "$newname"
        # mv -i "$name" "$newname"
    done' sh {} +

Это позволит найти все обычные файлы, имена которых совпадают log_*.txtв текущем каталоге или ниже, а затем передать их пакетами во встроенный sh -cскрипт, который по сути выполнит ту же работу, что и цикл во втором варианте выше.

решение2

Лучше всего использовать пакетный инструмент переименования файлов, например, zsh's zmv, mmvили один из вариантов на основе Perl rename:

zsh << 'EOF'
autoload zmv
zmv 'log_(*).txt' '${1}_log.txt'
EOF

Или

prename 's/log_(.*)\.txt/${1}_log.txt/' log*.txt

Связанный контент