Я хочу скопировать последний файл на основе имени файла в (filename_config) и в каждом каталоге на основе файла конфигурации (dir_config) в каталог резервного копирования.
В одном каталоге может быть > 1 имя файла для копирования.
реж_конфигурация:
/XXX/DIR1
/XXX/DIR2
/XXX/DIRB
/XXX/DIRY7
имя_файла_config:
ABCD
123AB
E142
Однако мой сценарий bash копирует только 1 имя файла в каталог резервной копии, хотя в dir_config имеется несколько имен файлов для копирования.
Пример вывода:
[Wed Jul 10 13:55:09 UTC 2024] Copy latest file from /srv/XXX/DIR1/12-ABCD_1.0_20231103082747609.zip to /srv/backup_data/XXX/DIR1
[Wed Jul 10 13:55:09 UTC 2024] Copy latest file from /srv/XXX/DIR2/E142_1.0_20231103082747609.tar to /srv/backup_data/XXX/DIR2
[Wed Jul 10 13:55:09 UTC 2024] Copy latest file from /srv/XXX/DIRB/17-ABCD_1.0_20231103082747609.tar to /srv/backup_data/XXX/DIRB
[Wed Jul 10 13:55:09 UTC 2024] Copy latest file from /srv/XXX/DIRY7/XY-123AB_1.0_20231103082747609.csv to /srv/backup_data/XXX/DIRY7
Это мой сценарий:
for i in $(<$dir_config)
do
cd $Source_Dir
for y in $(<$filename_config)
do
latest_file=$(LC_ALL=C ls $i *$y* -cr | tail -1)
done
cp "$Source_Dir$i/$latest_file" $Backup_Dir$i
WriteLog "Copy latest file from $Source_Dir$i/$latest_file to $Backup_Dir$i "
done
Я ожидаю, как показано ниже:
Выход:
[Wed Jul 10 13:55:09 UTC 2024] Copy latest file from /srv/XXX/DIR1/12-ABCD_1.0_20231103082747609.zip to /srv/backup_data/XXX/DIR1
[Wed Jul 10 13:55:09 UTC 2024] Copy latest file from /srv/XXX/DIR1/E142_1.0_20231103082747601.zip to /srv/backup_data/XXX/DIR1
[Wed Jul 10 13:55:09 UTC 2024] Copy latest file from /srv/XXX/DIR1/ZA-123AB_1.0_20231103082747601.zip to /srv/backup_data/XXX/DIR1
[Wed Jul 10 13:55:09 UTC 2024] Copy latest file from /srv/XXX/DIR2/E142_1.0_20231103082747607.tar to /srv/backup_data/XXX/DIR2
[Wed Jul 10 13:55:09 UTC 2024] Copy latest file from /srv/XXX/DIR2/12-ABCD_1.0_20231103082747602.tar to /srv/backup_data/XXX/DIR2
[Wed Jul 10 13:55:09 UTC 2024] Copy latest file from /srv/XXX/DIRB/17-ABCD_1.0_20231103082747604.tar to /srv/backup_data/XXX/DIRB
[Wed Jul 10 13:55:09 UTC 2024] Copy latest file from /srv/XXX/DIRY7/XY-123AB_1.0_20231103082747605.csv to /srv/backup_data/XXX/DIRY7
Кажется, что -c
подразумевает, что вы ищете не просто имена файлов. Если бы вы могли отредактировать вопрос и показать несколько примеров имен файлов и объяснить, как отличить новые файлы от старых, это помогло бы большему количеству людей понять, также см. Shellcheck для проверки/рекомендации.
@Barmar, спасибо за ваш совет, после изменения этого ls $i/*$y* скрипт работает как положено :)
Ваш сценарий довольно подозрительный: вы делаете cd $Source_Dir
без возврата компакт-диска. Вы не говорите, что хранится в переменной, но если это относительный путь к каталогу, cd
будет хорошо работать в первой итерации, но не в последующих. Дальше делаешь потом cp "$Source_Dir$i/$latest_file" $Backup_Dir$i
; опять же, это не удастся для относительного пути к каталогу. На данный момент у вас есть абсолютный путь, так что это не повредит, но рано или поздно он вас укусит.
Кроме того, не используйте ls
, если у вас нет четкого представления о том, какие имена файлов вы можете ожидать. См. здесь для получения дополнительной информации. Наконец, ваш код сломается, если в имени BackupDir
или i
есть пробел.
Ваш вопрос довольно неясен; но если вы хотите скопировать последний файл для каждого шаблона в файле конфигурации, вам нужно поместить cp
внутри цикла. В настоящее время вы просто устанавливаете latest_file
последовательно на последний файл для каждого шаблона в файле конфигурации (кроме см. ниже), а затем сразу же забываете и заменяете его результатом для следующего шаблона, в конечном итоге копируя только последний файл (якобы для последний шаблон в файле, но снова смотрите следующий абзац).
Кроме того, ls $i *$y*
будет искать все файлы в $i
и любой файл в текущем рабочем каталоге, соответствующий *$y*
. Я думаю, вы действительно имеете в виду ls "$i"/*"$y"*
, но вам, вероятно, стоит просмотреть это.
Кроме того, не читайте строки с и цитируйте переменные.
while read -r i
do
while read -r y
do
latest_file=$(LC_ALL=C ls -cr "$i"/*"$y"* | tail -1)
done<"$filename_config
cp "$Source_Dir$i/$latest_file" "$Backup_Dir$i"
WriteLog "Copy latest file from $Source_Dir$i/$latest_file to $Backup_Dir$i"
done<"$dir_config"
Более тщательная перезапись также позволит избегать ls в скриптах и, возможно, однократно читать файл шаблона в массив перед основным циклом, вместо того, чтобы неэффективно перечитывать одни и те же данные с диска снова и снова. (Конечно, это, вероятно, будет достаточно быстро для небольшого файла с современным кэшированием.)
Я полностью удалил cd "$Source_Dir"
в ответ на комментарий, так как похоже, что это не принесло ничего полезного. Что именно работает и как именно работает, также зависит от того, абсолютные это пути или относительные, но мы не можем увидеть, что содержат эти переменные. Возможно, см. Также Что такое текущий рабочий каталог?
Могу ли я предложить переместить cd "$Source_Dir"
за пределы цикла и удалить ссылку на него из команды cp
?
Спасибо. В общем, я его удалил полностью.
Полностью удалять его опасно. Тогда вычисление latest_file
завершится неудачей, если i
является относительным путем.
Да, мой комментарий к этому изменению уже подчеркивает это.
Сделав несколько предположений - учитывая, что
$Source_Dir
, $filename_config
и $dir_config
устанавливаются с соответствующими полными или относительными путями,/XXX/
всегда один и тот же $Source_Dir
,dir_config
НЕ имеет полных путей, но имеет подпапки $Source_Dir
,$Source_Dir
может быть несколько совпадений с filename_config
затем немного рефакторинг.
while read -r d
do while read -r f
do read -r x x x latest < <(
stat -c "%z %n" "$Source_Dir/$d"/*"$f"* | sort -nr ) || continue
cp -put "$Backup_Dir/$d/" "$Source_Dir/$d/$latest"
WriteLog "$Source_Dir/$d/$latest => $Backup_Dir/$d/"
done < "$filename_config"
done < "$dir_config"
Ему не важно, где он запускается, и ему не нужно менять каталоги, при условии, что вы устанавливаете $Source_Dir
, $filename_config
и $dir_config
с соответствующими полными или относительными путями.
Он не пытается анализировать ls, цитирует все переменные, сохраняет атрибуты файлов, не копирует файлы, которые не новее, чем файлы с таким же именем, уже находящиеся в соответствующей подпапке $Backup_Dir
, и не делает Не пытайтесь скопировать файл, если он не существует в подкаталоге $Source_Dir
, хотя он сообщит об ошибке, если он отсутствует.
Это легко сделать, если вы предпочитаете проверить это и пропустить сообщения об ошибках, но, пожалуйста, не бросайте ошибки просто в битбакет...
Обратите внимание, что я поместил копию в цикл, который читает $filename_config
. Если в одном каталоге когда-либо были файлы, соответствующие более чем одному из перечисленных вами шаблонов, вы получали только последний — посмотрите на этот пример, который я создал.
$: ls -l
total 1
-rw-r--r-- 1 P2759474 1049089 8 Jul 11 09:49 filename_config
-rw-r--r-- 1 P2759474 1049089 0 Jul 11 09:48 foobar1
-rw-r--r-- 1 P2759474 1049089 0 Jul 11 09:48 foobar2
-rw-r--r-- 1 P2759474 1049089 0 Jul 11 09:48 other1
-rw-r--r-- 1 P2759474 1049089 0 Jul 11 09:48 other2
$: echo "$filename_config:"; cat $filename_config
filename_config:
foo
her
$: for y in $(<$filename_config); do latest_file=$(LC_ALL=C ls $i *$y* -cr | tail -1); echo "$latest_file"; done
foobar2
other2
$: echo "cp $latest_file \$somewhere"
cp other2 $somewhere
Это то, что делает ваш код. Он должен создать резервную копию foobar2
, но цикл присваивает его, затем переходит к следующему файлу и перезаписывает его, прежде чем копировать.
Вы можете сделать это с помощью массива, если хотите -
$: for y in $(<$filename_config); do latest_file+=( $(LC_ALL=C ls $i *$y* -cr | tail -1) ); done
$: echo cp "${latest_file[@]}" \$somewhere
cp foobar2 other2 $somewhere
Не проблема, если в данном каталоге может быть только одно совпадение, но тогда зачем вам два файла конфигурации? Если в одном каталоге всегда есть только одно совпадение, вы можете создать один файл с шаблоном и каталогом и сделать все это за один цикл.
Привет @Paul Hodges, все ваши предположения верны, также да, функцию WriteLog, которую я создаю для вывода в файл журнала. Спасибо за ваше четкое объяснение, я постараюсь его адаптировать :)
Я попробовал это, пока читал -r d, и все работает отлично, спасибо :)
Очень рад, что смог помочь. :) Подумайте о том, чтобы проголосовать за любые (все!) решения, которые дают полезную информацию и/или чему-то вас учат. Triplee указал на очень важный момент, который я на самом деле не упомянул явно, хотя и исправил его в своей версии. Стоит полюбить этот туалет!
Разве
ls $i *$y*
не должно бытьls $i/*$y*
? Кроме того, вы должны заключить в кавычки все переменные. И используйтеwhile read i
вместоfor i in $(<filename)
, если в именах есть пробелы.