Подстановочный знак: путь возврата
Я пытаюсь переименовать каждый 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 и запустите эту последнюю строку в командной строке.