for
ループでbash
に複数の変数(整数だけでなく)を指定する方法はありますか?私が使用する必要がある任意のテキストを含む2つのファイルがある場合があります。
私が機能的に必要なのは次のようなものです:
for i in $(cat file1) and j in $(cat file2); do command $i $j; done
何か案は?
まず、Word-splittingによる行の読み取りにはいくつかの避けられない問題があるため、 forで行を読み取らないでください です。
同じ長さのファイルを想定している場合、または2つの短いファイルが読み取られるまでループする場合は、簡単な解決策が考えられます。
while read -r x && read -r y <&3; do
...
done <file1 3<file2
read
がfalseを返す場合やその他のいくつかの理由により、より一般的なソリューションをまとめることは困難です。この例では、任意の数のストリームを読み取り、最短または最長の入力の後に戻ります。
#!/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 i in $(< file1); do for j in $(< file2); do echo $i $j;done ; done
もちろん、File2はfile1内のWordごとに1回処理されます。
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ファイルをZipしたい場合は、pasteを使用します。
paste file1 file2
while
には、interesitng構文があります。 do ... while
ループの前に複数のコマンドを置くことができ、問題のケースでは、特定の要件に応じて、この機能を調整する必要がある場合があります。最も長いファイルの最後まで読み取るか、または最短。
たとえば、read || read
は単純に機能しません(質問の要件に従って)、最初のファイルの読み取りがtrue
の場合、2番目のファイルの読み取りは- スキップ最初のファイルが最初から最後まで読み取られるまで...その後、ステータスがtrue
のままであるため、whileループが継続し、2番目のファイルを最初から最後まで読み取ります。
read && read
は、最短のファイルまで読み取りたい場合に、ファイルを同時に(同期的に)読み取ります。ただし、両方のファイルをeof
に読み取る場合は、while's
構文要件を使用する必要があります。コマンドによってすぐにbeforeゼロ以外の戻りコードを生成するdo while
ループが、whileループから抜け出します。
以下は、両方のファイルをeofに読み取る方法の例です。
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をテストすることもできますが、一般的な考え方は、特に最終的なtrue/false条件にあります。