Подстановочный знак: путь возврата
Я пытаюсь переименовать каждый access.log.gz
файл (примечание: точное имя файла, а не "начинающийся с" или "содержащий"), найденный в /home/*/logs
в date-access.log.gz
в той же папке.
for file in /home/*/logs/access.log.gz
do
mv -v "$file" /home/*/logs/old/`date +\%G-\%m-\%d`"-access.log.gz"
done
Конечно получаю /home/*/logs/2014-09-08-access.log.gz - No such file or directory
, Как я могу позвонить *
пути назад? Каждый файл должен быть переименован и перемещен в /old/ subdir исходной папки. Я пробовал с
mv -v "$file" /home/`$1`/logs/old/`date +\%G-\%m-\%d`"-access.log.gz"
... но не сработало.
3 ответа
Использование find
( Как я могу использовать команду find более эффективно?).
find /home/ -ipath */logs/access.log.gz
Это должно найти все интересующие вас файлы и распечатать их. Вы можете включить это в свой сценарий, но, вероятно, легче find
сделать всю тяжелую работу.
find /home/ -ipath */logs/access.log.gz -execdir mv "{}" "old/`date +\%G-\%m-\%d`-access.log.gz" \;
Особенности find
мы используем здесь:
-ipath
соответствует шаблонам в найденных именах файлов. Вы также можете использовать-name
если вы точно знали, что в каждом подпути есть только один файл access.log.gz (-name access.log.gz
).-execdir
выполняет данную команду для каждого найденного файла из подкаталога, содержащего соответствующий файл. Это эквивалентно выполнениюcd
где каждый файл, а затем запустив команду там. В этом случае это экономит много логики манипуляции пути. Найденный файл заменен на {}. Также обратите внимание, что "старый" задается как относительный путь (как вы сказали, чтоold/
существует вlogs/
)
Просто разберите и соберите $file
когда вы перебираете подходящие файлы: ${file%/*}
дает вам файл $ со всем после последнего /
удалены, и ${file##*/}
дает вам файл $ со всем до последнего /
удален; вместе взятые ${file%/*}/old/$date-${file##*/}
даст вам "новое" имя файла, соответствующее $file
,
Предположительно, дата не изменится в ходе выполнения вашего скрипта, поэтому нет смысла вызывать date
для каждого файла; скорее, сохраните дату в переменной один раз, а затем используйте ее во время цикла: date=$(date '+%G-%m-%d')
, (Обратите внимание, что современные сценарии должны использовать $(...)
а не клюшки. Также обратите внимание, что %
не является "специальным" символом и поэтому не нуждается в кавычках или экранировании, хотя в качестве хорошей практики вся строка формата должна быть заключена в кавычки.)
Если у вас есть новая версия Bash, вы можете полностью избежать подоболочки, используя printf -v date '%(%G-%m-%d)T' -1
вместо.
Вы также можете найти полезным использовать shopt -s globstar
так что вы можете искать на произвольной глубине в каталогах, и shopt -s nullglob
так что непревзойденный glob (подстановочный знак) расширяется до "ничего" вместо исходного шаблона glob.
Собрав все это вместе, мы получим:
shopt -s globstar nullglob
date=$( date '+%G-%m-%d' )
for file in /home/*/logs/**/access.log.gz
do
target=${file%/*}/old/$date-${file##*/}
mv -vi "$file" "$target"
done
Если у вас установлен zsh, используйте zmv
, В скрипте:
#!/bin/zsh
autoload zmv
zmv '/home/*/logs/access.log.gz' '$f:h/`date +\%G-\%m-\%d`-$f:t'
В командной строке zsh введите autoload zmv
в вашем ~/.zshrc
и запустите эту последнюю строку в командной строке.