
Предположим, у меня есть 2 отдельных цикла.
for file1 in `ls Dir1/` ; do
echo $file1
done
for file2 in `ls Dir2/` ; do
echo $file2
done
Я хочу, чтобы один цикл итерировал оба псевдокода каталогов
for file1 , file2 in `ls Dir1` , `ls Dir2`
do
echo $file1
echo file2
done
Является ли это возможным
решение1
Цикл while
с подходящими входными данными может выполнить эту работу, предполагая, что в именах файлов нет символов новой строки.
paste -d/ <(ls /var) <(ls /usr) |
while IFS=/ read -r e u; do
printf '%s\n' "$e $u"
done
решение2
shopt -s nullglob
dir1names=( Dir1/* )
dir2names=( Dir2/* )
while [ "${#dir1names[@]}" -gt 0 ] &&
[ "${#dir2names[@]}" -gt 0 ]
do
printf '%s\n' "${dir1names[0]##*/}"
printf '%s\n' "${dir2names[0]##*/}"
dir1names=( "${dir1names[@]:1}" )
dir2names=( "${dir2names[@]:1}" )
done
Это используется bash
для получения всех путей из каждого каталога в два массива. Затем он печатает часть имени файла этих путей (как ls
это было бы), чередуя массивы, и удаляет напечатанные целые части из массивов. Он останавливается, когда один из массивов полностью пуст.
Опция nullglob
shell заставляет несовпадающие шаблоны подстановки расширяться до нуля, а не оставаться нерасширенными.
Тестирование:
$ tree
.
|-- Dir1
| |-- file-1
| |-- file-2
| |-- file-3
| `-- file-4
|-- Dir2
| |-- otherfile-1
| |-- otherfile-2
| |-- otherfile-3
| |-- otherfile-4
| |-- otherfile-5
| `-- otherfile-6
`-- script.sh
2 directories, 11 files
$ bash script.sh
file-1
otherfile-1
file-2
otherfile-2
file-3
otherfile-3
file-4
otherfile-4
Если этона самом делео парном расположении файлов в соответствии с их именами, так что файл в одном каталоге будет парным с соответствующим файлом в другом (как это иногда делается, например, в приложениях биоинформатики, кажется), то вам лучше просто выполнить итерацию по одному из наборов, а затемпостроениеимя соответствующих файлов из другого набора.
Предположим, что файлы называются something_R1_more
и something_R2_more
где something
и more
идентифицирует конкретную R1
пару R2
файлов.
for r1 in dir1/*_R1_*; do
r2=${r1##*/} # dir1/something_R1_more --> something_R1_more
r2=dir2/${r2/_R1_/_R2_} # something_R1_more --> dir2/something_R2_more
if [ ! -f "$r2" ]; then
printf '%s not found\n' "$r2" >&2
exit 1
fi
# process "$r1" and "$r2" here
done
решение3
Вы вообщене хочу анализировать выводls
.
Здесь zsh
(вы уже используете zsh
синтаксис, а не bash
потому, что не заключаете переменные в кавычки и не используете подстановку команд без отключения подстановки символов), вы можете сделать:
dir1_file_names=(dir1/*(N:t))
dir2_file_names=(dir2/*(N:t))
for f1 f2 (${dir1_file_names:^dir2_file_names})
printf '%s\n' "f1: $f1, f2: $f2"
zsh
может выполнять цикл с несколькими переменными ${a:^b}
.сжатие массиваоператор. Если в одном из массивов элементов меньше, чем в другом, то это будет равносильно тому, что они будут усечены до длины наименьшего.
См. также операторы пересечения и вычитания массивов, если речь идет о сравнении имен файлов в двух каталогах:
file_names_in_both_dir1_and_dir2=(${dir1_file_names:*dir2_file_names})
file_names_only_in_dir1=(${dir1_file_name:|dir2_file_names})
file_names_only_in_dir2=(${dir2_file_name:|dir1_file_names})
решение4
Вывод нескольких переменных можно сохранить в одной переменной, как указано ниже. Проверьте, работает ли это для вас
#!/bin/bash
cd /
for file1 in $(ls /usr/ ; echo "::::::NEXT:::::::" ; ls /sys/)
do
echo $file1
done