У меня есть файлы с одинаковой номенклатурой, 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