多変数Forループ

多変数Forループ

forループ内で整数だけでなく複数の変数を指定する方法はありますかbash?処理する必要がある任意のテキストを含むファイルが2つある場合があります。

私の機能は次のようになります。

for i in $(cat file1) and j in $(cat file2); do command $i $j; done

どんなアイデアがありますか?

答え1

まず、行を読むときにforを使用しないでくださいこれは、単語の分割を介して行を読み取るには多くの避けられない問題があるためです。

ファイルの長さが同じであると仮定するか、2つのファイルのうち短いファイルを読むまで繰り返す場合は、簡単な解決策があります。

while read -r x && read -r y <&3; do
    ...
done <file1 3<file2

falseが返される場合やその他のさまざまな理由により、readより一般的なソリューションを構成することは困難です。この例では、ストリームをいくらでも読み取ることができ、最短または長い入力の後に返すことができます。

#!/usr/bin/env bash

# Open the given files and assign the resulting FDs to arrName.
# openFDs arrname file1 [file2 ...]
openFDs() {
    local x y i arr=$1

    [[ -v $arr ]] || return 1
    shift

    for x; do
        { exec {y}<"$x"; } 2>/dev/null || return 1
        printf -v "${arr}[i++]" %d "$y"
    done
}

# closeFDs FD1 [FD2 ...]
closeFDs() {
    local x
    for x; do
        exec {x}<&-
    done
}

# Read one line from each of the given FDs and assign the output to arrName.
# If the first argument is -l, returns false only when all FDs reach EOF.
# readN [ -l ] arrName FD1 [FD2 ...]
readN() {
    if [[ $1 == -l ]]; then
        local longest
        shift
    else
        local longest=
    fi

    local i x y status arr=$1
    [[ -v $arr ]] || return 1
    shift

    for x; do
        if IFS= read -ru "$x" "${arr}[i]" || { unset -v "${arr}[i]"; [[ ${longest+_} ]] && return 1; }; then
            status=0
        fi
        ((i++))
    done
    return ${status:-1}
}

# readLines file1 [file2 ...]
readLines() {
    local -a fds lines
    trap 'closeFDs "${fds[@]}"' RETURN
    openFDs fds "$@" || return 1

    while readN -l lines "${fds[@]}"; do
        printf '%-1s ' "${lines[@]}"
        echo
    done
}

{
    readLines /dev/fd/{3..6} || { echo 'error occured' >&2; exit 1; }
} <<<$'a\nb\nc\nd' 3<&0 <<<$'1\n2\n3\n4\n5' 4<&0 <<<$'x\ny\nz' 5<&0 <<<$'7\n8\n9\n10\n11\n12' 6<&0

# vim: set fenc=utf-8 ff=unix ts=4 sts=4 sw=4 ft=sh nowrap et:

readNしたがって、インポートするかどうかに応じて、-l出力は次のようになります。

a 1 x 7
b 2 y 8
c 3 z 9
d 4 10
5 11
12

または

a 1 x 7
b 2 y 8
c 3 z 9

すべてを複数の配列に保存せずに複数のストリームを繰り返す必要はほとんどありません。単に配列を読みたい場合はを見てくださいmapfile

答え2

完了しました。完了しました。 2回のforと2回の完了が必要です。

for i in $(< file1); do for j in $(< file2); do echo $i $j;done ; done

もちろん、ファイル2の各単語は一度だけ処理される。

ほとんどの場合、catの代わりに<を使用してもパフォーマンスが大幅に向上するわけではありません。子プロセスは含まれません。(最後の文章はよくわかりません。)

非圧縮:

for i in $(< file1)
do
  for j in $(< file2)
  do
    echo $i $j
  done
done

行だけを除いて単語だけを読み、空白を維持したくない場合は、whileとreadを使用してください。

while read line; do while read second; do echo "${line}${second}"; done <file2 ; done <file1

2つのファイルを入れ子にする代わりに追加するには:

cat file1 file2 | while read line; do echo "${line}"; done

2つのファイルを圧縮するには、貼り付けを使用してください。

paste file1 file2

答え3

while興味深い構文があります。ループの前に複数のコマンドを入れることができ、do ... while問題の状況では、特定の要件(最も長いファイルの最後まで読んでいるのか、最も短いファイルの終わりまで読んでいるのか)によっては、この機能を妥協する必要があります。

例えば、read || read単に動作しません(質問で尋ねるように)最初のファイルを読み取ったときにtrue2番目のファイルを読み取ったのは次のとおりです。飛び越える最初のファイルを最初から最後まで読み取るまで...次に状態がstillなので、truewhileループが続き、2番目のファイルを最初から最後まで読み込みます。

read && read最短ファイルのみを読み取る場合は、ファイルは同時に(同期的に)読み取られます。ただし、両方のファイルを読み取るには構文要件をeof満たす必要があります。while'sすぐにコマンドを渡します。今後ループは、do whilewhile ループから出るためにゼロ以外の戻りコードを生成します。

以下は、2つのファイルを読み取る方法の例です。エブ

while IFS= read -r line3 <&3 || ((eof3=1))
      IFS= read -r line4 <&4 || ((eof4=1))
      !((eof3 & eof4))
do
    echo "$line3, $line4"
done 3<file3 4<file4

(読む前にeof3とeof4をテストしたいかもしれませんが、特に最終的な真/偽の条件に関する一般的なアイデアがあります。

関連情報