Как остановить цикл bash от зацикливания файлов, созданных во время цикла?

#bash #loops

Вопрос:

Я хочу запустить цикл по всем файлам определенного расширения в каталоге:

 for i in *.bam
do
...
done
 

Однако, если команда, которую я выполняю внутри цикла, создает временный файл с тем же расширением, цикл также пытается обработать этот новый файл tmp. Это нежелательно. Итак, я подумал, что проблема решится следующим образом: сначала перечислите все файлы *.bam в каталоге, сохраните этот список в переменной, а затем выполните цикл по этому сохраненному списку:

 list_bam=$(for i in *.bam; do echo $i; done)

for i in $list_bam
do
...
done
 

К моему удивлению, это сталкивается с той же проблемой! Не мог бы кто-нибудь, пожалуйста, объяснить логику этого и как это исправить, чтобы цикл обрабатывал только существующие файлы .bam?

Комментарии:

1. echo $list_bam не дает списка. Это просто дает *.bam . Вероятно, вам придется сохранить список в файл и прочитать из него.

2. Когда я запускаю list_bam=$(for i in *.bam; do echo $i; done) в своем каталоге , а затем звоню echo $list_bam , я получаю список имен файлов.

3. Однако, если команда, которую я выполняю внутри цикла, создает временный файл с тем же расширением, цикл также пытается обработать этот новый файл tmp. Это невозможно.

4. Мне это тоже кажется невозможным, вот почему я так растерян.

5. Я не могу воспроизвести эту проблему. Пример команды for i in *.bam; do cp $i bu.$i; echo $i; sleep 0.1; done работает только для исходных файлов, как и следовало ожидать.

Ответ №1:

Вместо цикла вы можете использовать find и xargs

 find . -maxdepth 1 -type f -name "*.bam" -print0 | 
   xargs -0 -I{} bash -c 'echo "{}" > "{}.new.bam"'
 

или

 find . -maxdepth 1 -type f -name "*.bam" -print0 | 
   xargs -0 -I{} bash -c 'echo "$1" > "$1.new.bam"' -- {}
 

пример:

 $ touch a.bam b.bam
$ ls 
a.bam  b.bam
$ find . -maxdepth 1 -type f -name "*.bam" -print0 | 
    xargs -0 -I{} bash -c 'echo "{}" > "{}.new.bam"' 
$ ls
a.bam  a.bam.new.bam  b.bam  b.bam.new.bam
 

Комментарии:

1. Спасибо! Я постараюсь это реализовать 🙂

Ответ №2:

Возможно, вам следует убедиться, что ваше выражение *.bam шара не может быть впоследствии интерпретировано чем-то вроде:

 list_bam=$(ls *.bam)

...
 

но, как заметил @glenn в комментариях, это плохая идея.
Что-то подобное должно быть сделано с использованием шаблона find ... -print0 | xargs -0 ... команды.

Комментарии:

1. Почему вы не должны анализировать выходные данные ls list_bam=(*.bam) тогда for bam_file in "${list_bam[@]}" ...

2. Хорошо подмечено. Я соответствующим образом уточнил ответ.

3. Спасибо! На данный момент это фактически решило мою проблему, я попытаюсь реализовать find ... | xargs ... версию позже.