Цикл по парам значений в bash
У меня есть 10 текстовых файлов, и я хочу paste
каждый файл со своей парой, так что у меня всего 5 файлов.
Я попробовал следующее:
for i in 4_1 5_1 6_1 7_1 8_1
do
for j in 4_2 5_2 6_2 7_2 8_2
do
paste ${i}.txt ${j}.txt > ${i}.${j}.txt
done
done
Однако этот код объединяет все возможные комбинации, а не просто объединяет совпадающие пары.
Так что я хотел бы файл 4_1.txt
быть в паре с 4_2.txt
, 5_1.txt
с 5_2.txt
, так далее.
5 ответов
Если вы хотите использовать одну переменную и выполнять с ней действия, вам просто нужно использовать один цикл:
for file in 4 5 6 7 8
do
paste "${file}_1" "${file}_2"
done
Это будет делать
paste 4_1 4_2
paste 5_1 5_2
...
Я согласен с ответом, который в настоящее время предлагает Федорки в контексте заданного вопроса. Ниже дано только для того, чтобы дать более общие ответы.
Еще один общий подход (для bash 4.0 или новее) - хранить ваши пары в ассоциативном массиве:
declare -A pairs=( [4_1]=4_2 [5_1]=5_2 [6_1]=6_2 [7_1]=7_2 [8_1]=8_2 )
for i in "${!pairs[@]}"; do
j=${pairs[$i]}
paste "$i.txt" "$j.txt" >"${i}.${j}.txt"
done
Другой (совместимый со старыми выпусками bash) - использовать более одного обычного массива:
is=( 4_1 5_1 6_1 7_1 8_1 )
js=( 4_2 5_2 6_2 7_2 8_2 )
for idx in "${!is[@]}"; do
i=${is[$idx]}
j=${js[$idx]}
paste "$i.txt" "$j.txt" >"$i.$j.txt"
done
Самый простой пока:
for i in "1 a" "2 b" "3 c"; do a=( $i ); echo ${a[1]}; echo ${a[0]}; done
a
1
b
2
c
3
Вы можете использовать ассоциативный массив:
animals=(dog cat mouse)
declare -A size=(
[dog]=big
[cat]=medium
[mouse]=small
)
declare -A sound=(
[dog]=barks
[cat]=purrs
[mouse]=cheeps
)
for animal in "${animals[@]}"; do
echo "$animal is ${size[$animal]} and it ${sound[$animal]}"
done
Это позволяет обходить пары, тройки и т. Д. Кредиты: исходная идея взята из ответа @CharlesDuffy-s.
Вышеупомянутое не работало для меня, но следующее действительно читает значения в парах из упорядоченного списка
(может быть больше, чем пары, добавляющие дополнительные строки для чтения:-)
while read x; do
read y
echo $x $y
done << '___HERE'
X1
Y1
X2
Y2
X3
Y3
___HERE
производит
X1 Y1
X2 Y2
X3 Y3
Существует общая схема, где у вас есть пары файлов, где одно имя пары может быть легко получено из другого. Если файл, имя которого вы знаете, является X, а другой файл - Y, у вас есть следующие распространенные случаи использования.
- Для переименования Y - X с удаленным расширением и / или добавленной отметкой даты.
- Для транскодирования Y - это X с другим расширением и, возможно, с другим каталогом.
- Для многих задач анализа данных X и Y совместно используют некоторые части имени файла, но имеют разные параметры или расширения.
Все они поддаются одинаковому скелету грубого кода.
for x in path/to/base*.ext; do
dir=${x%/*} # Trim trailing file name, keep dir
base=${x##*/} # Trim any leading directory
# In this case, $y has a different subdirectory and a different extension
y=${dir%/to}/from/${base%.ext}.newext
# Maybe check if y exists? Or doesn't exist?
if [ -e "$y" ]; then
echo "$0: $y already exists -- skipping" >&2
continue
fi
mv or ffmpeg or awk or whatever "$x" and "$y"
done
Ключевым моментом здесь является наблюдение, что y
может быть получено из x
с некоторыми простыми заменами переменных. Итак, вы перебираете x
значения и выяснить соответствующие y
значение внутри цикла.
Здесь мы использовали встроенную оболочку ${variable#prefix}
а также ${variable%suffix}
операторы, чтобы вернуть значение переменной с любым ведущим prefix
или тянущийся suffix
соответственно обрезали. (Существует также ##
а также %%
соответствовать самому длинному, а не самому короткому возможному совпадению. Выражение после #
или же %
это обычный шаблон оболочки оболочки.) Обычно это должно быть все, что вам нужно, хотя вы часто видите sed
или же awk
сценарии даже для этой тривиальной работы (где на самом деле вы должны стараться избегать внешних процессов), а также, конечно, для более сложных преобразований.
Если вам нужно перебрать x
файлы разбросаны по разным каталогам, возможно, цикл должен начинаться с чего-то вроде
find dir1 dir2 etc/and/so/forth -type f -name 'x-files*.ext' -print |
while IFS='' read -r x; do
:
Часто встречающаяся проблема в подобных вопросах - это ответы, которые не цитируются $x
а также $y
правильно. Как правило, любая переменная, содержащая имя файла, всегда должна быть в двойных кавычках.
Там, где X и Y не связаны, общее решение состоит в том, чтобы перебрать здесь документ, содержащий отображение:
while read -r x y; do
: stuff with "$x" and "$y"
done <<'____HERE'
first_x_value first_y_value
another_x corresponding_y
random surprise
____HERE
Есть разница между использованием read -r x y
а также read x && read y
while read -r x y; do
echo "$x and $y"
done <<'____HERE'
A B
C D
____HERE
напечатает
A and B
C and D
в то время как,
while read x && read y; do
echo "$x and $y"
done <<'____HERE'
A B
C D
____HERE
напечатает
A B and C D
Первый пример разбивается на space
, а вторая разбивается на newline
.