Зацикливание по парам значений в 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 и т.д.

Ответы

Ответ 1

Если вы хотите использовать одну переменную и выполнить с ней действие, вам просто нужно использовать один цикл:

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
...

Ответ 2

Я согласен с ответом, который в настоящее время предлагается fedorqui в контексте заданного в настоящее время вопроса. Ниже дается только предоставление более общих ответов.

Еще один общий подход (для 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

Ответ 3

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

Ответ 4

Вы можете использовать словарь /hashMap:

animals=(dog cat mouse)
declare -A sound=(
  [dog]=barks
  [cat]=purrs
  [mouse]=cheeps
)
declare -A size=(
  [dog]=big
  [cat]=medium
  [mouse]=small
)
for animal in "${animals[@]}"; do
  echo "$animal ${sound[$animal]} and it is ${size[$animal]}"
done

Таким образом, вы можете работать с кортежами (парами), "тройками" и т.д. - любые данные словаря /hashMap.

Кредиты: Основная идея взята из большого ответа @CharlesDuffy.