Множественный цикл с файлом конфигурации в bash

Я хочу скопировать последний файл на основе имени файла в (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

Разве ls $i *$y* не должно быть ls $i/*$y*? Кроме того, вы должны заключить в кавычки все переменные. И используйте while read i вместо for i in $(<filename), если в именах есть пробелы.

Barmar 10.07.2024 17:20

Кажется, что -c подразумевает, что вы ищете не просто имена файлов. Если бы вы могли отредактировать вопрос и показать несколько примеров имен файлов и объяснить, как отличить новые файлы от старых, это помогло бы большему количеству людей понять, также см. Shellcheck для проверки/рекомендации.

Jetchisel 10.07.2024 23:48

@Barmar, спасибо за ваш совет, после изменения этого ls $i/*$y* скрипт работает как положено :)

Miss aya 11.07.2024 09:05

Ваш сценарий довольно подозрительный: вы делаете cd $Source_Dir без возврата компакт-диска. Вы не говорите, что хранится в переменной, но если это относительный путь к каталогу, cd будет хорошо работать в первой итерации, но не в последующих. Дальше делаешь потом cp "$Source_Dir$i/$latest_file" $Backup_Dir$i; опять же, это не удастся для относительного пути к каталогу. На данный момент у вас есть абсолютный путь, так что это не повредит, но рано или поздно он вас укусит.

user1934428 11.07.2024 13:41

Кроме того, не используйте ls, если у вас нет четкого представления о том, какие имена файлов вы можете ожидать. См. здесь для получения дополнительной информации. Наконец, ваш код сломается, если в имени BackupDir или i есть пробел.

user1934428 11.07.2024 13:44
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
83
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ваш вопрос довольно неясен; но если вы хотите скопировать последний файл для каждого шаблона в файле конфигурации, вам нужно поместить 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?

user1934428 11.07.2024 13:46

Спасибо. В общем, я его удалил полностью.

tripleee 11.07.2024 14:18

Полностью удалять его опасно. Тогда вычисление latest_file завершится неудачей, если i является относительным путем.

user1934428 12.07.2024 07:54

Да, мой комментарий к этому изменению уже подчеркивает это.

tripleee 12.07.2024 07:55
Ответ принят как подходящий

Сделав несколько предположений - учитывая, что

  • $Source_Dir, $filename_config и $dir_config устанавливаются с соответствующими полными или относительными путями,
  • /XXX/ всегда один и тот же $Source_Dir,
  • dir_config НЕ имеет полных путей, но имеет подпапки $Source_Dir,
  • у каждого $Source_Dir может быть несколько совпадений с filename_config
  • WriteLog — это функция или что-то, что вы определили где-то еще.

затем немного рефакторинг.

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, которую я создаю для вывода в файл журнала. Спасибо за ваше четкое объяснение, я постараюсь его адаптировать :)

Miss aya 12.07.2024 02:59

Я попробовал это, пока читал -r d, и все работает отлично, спасибо :)

Miss aya 12.07.2024 10:42

Очень рад, что смог помочь. :) Подумайте о том, чтобы проголосовать за любые (все!) решения, которые дают полезную информацию и/или чему-то вас учат. Triplee указал на очень важный момент, который я на самом деле не упомянул явно, хотя и исправил его в своей версии. Стоит полюбить этот туалет!

Paul Hodges 15.07.2024 17:33

Другие вопросы по теме

Проблема с использованием оператора удаления для массива объектов, размещенных в куче, с использованием цикла FOR вместо использования ключевого слова delete[] array_of_objects
Являются ли побитовые операторы медленнее, чем обычные циклы, такие как цикл for?
Рекурсивная функция PS для генерации полного пути
Как перезапустить цикл переключения в Java
Функция Python Apply для создания новых строк в цикле
При циклическом просмотре кадра данных в поисках строковых значений. Как распечатать строки без дублирования, если поисковый запрос встречается несколько раз?
Как выполнить итерацию функции для нескольких значений (функция цикла)?
Код игры Yahtzee сообщает мне, что у меня неверный бросок, несмотря на отсутствие ввода, и я делаю первый бросок дважды
Как я могу оптимизировать проверку перестановок символов (10 букв) по списку слов?
Динамическое создание внутренних циклов