web-dev-qa-db-ja.com

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.txt5_1.txt5_2.txtなど.

17
Rish

1つの変数を使用して実行し、そのアクションを実行する場合は、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
...

現在質問されている質問に関して、フェドルキが現在提案している回答に同意します。以下は、より一般的な答えを提供するためにのみ提供されています。

(bash 4.0以降の)より一般的なアプローチの1つは、ペアを連想配列に格納することです。

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

もう1つ(以前のリリースの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
29
Charles Duffy

連想配列を使用できます。

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

これにより、ペア、トリプルなどをトラバースできます。

8
VasiliNovikov

ファイルのペアが存在する一般的なパターンがあり、ペアの名前の1つは他の名前から簡単に派生できます。名前がわかっているファイルが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

ここで重要なのは、yxからいくつかの単純な変数置換で導出できるという観察です。したがって、x値をループして、ループ内の対応するy値を見つけます。

ここでは、シェルの組み込み${variable#prefix}演算子と${variable%suffix}演算子を使用して、変数の値を返し、先頭のprefixまたは末尾のsuffixをそれぞれ切り捨てています。 (最短ではなく、最長の一致ではなく##%%もあります。#または%の後の式は、通常のシェルglobパターンです。)これらは通常必要なものすべてですが、この簡単なジョブ(実際には通常、外部プロセスを回避する必要がある場合)でも、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
tripleee

上記は私にとってはうまくいきませんでしたが、次は順序付きリストからペアで値を読み取ります

(余分な 'read-lines'を追加するペア以上になる場合があります:-)

while read x; do
  read y
  echo $x $y
done << '___HERE'
X1
Y1
X2
Y2
X3
Y3
___HERE

作り出す

X1 Y1
X2 Y2
X3 Y3
3
splaisan

read -r x yread 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で分割し、2番目の例はnewlineで分割します。

0
Vivek