web-dev-qa-db-ja.com

コマンド出力を改行で分割しますか?

複数の行を返すコマンドがあります。さらに処理するために、それらの行の各単一行を処理する必要があります。

私の現在のコードは、IFS( Internal Field Separator )を変更することで機能します。

ROWS=$(some command returning multiple lines)

O=$IFS #save original IFS
IFS=$(echo -en "\n\b") # set IFS to linebreak

for ROW in ${ROWS[@]}
do
  echo "$ROW"
done

IFS=$O #restore old IFS

IFSを変更せずに、複数行出力の単一行にアクセスするためのより良い方法はありますか?特に、スクリプトの読みやすさは、IFSを変更することによって悪化します。

更新:チョロバからのものなど、回答を機能させるのに問題があります:

while IFS= read -r line ; do
    let var+=line #line 42
done << $(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
echo "$var" # line 44

くれます

./bla.sh: row 44: Warning: here-document at line 43 delimited by end-of-file (wanted `$(sqlite3 -list -nullvalue NULL -separator , /var/log/asterisk/master.db ${QUERY})')
./bla.sh: row 42: let: echo "": syntax error: invalid arithmetic operator. (error causing character is \"""\").

誰でもこれを手伝ってくれる?ありがとう!

9
stefan.at.wpf

readループのwhileはどうですか?

some command returning multiple lines | while IFS= read -r line ; do
    echo "$line"
done

ただし、パイプはサブシェルで実行されるため、残りのスクリプトの変数値を変更できないことに注意してください。 whileループから生き残るために何かが必要な場合は、bashのプロセス置換を使用できます。

while IFS= read -r line ; do
    let var+=line
done < <(some command returning multiple lines)
echo "$var"
13
choroba

IFSを改行のみに設定するだけでは不十分です。 (ところで、なぜバックスペース文字でも分割するのですか?)

コードでは、${ROWS[@]}(これは$ROWSを書く奇妙な方法です— — ROWSは配列ではありません)は二重引用符で囲まれていません。 (二重引用符で囲まれている場合、ROWSは配列ではないため、単一の文字列を取得します。)したがって、シェルは変数の値を各IFS文字でフィールドに分割します。次に、各フィールドをグロブパターンとして扱います。たとえば、コマンドによって出力された行の1つに単一文字*が含まれている場合、これは現在のディレクトリのファイル名に置き換えられます。

set -fを使用すると、グロビングをオフにできます。シェルのフィールド分割機能を使用するようにIFSを設定するほとんどの場合、グロビングをオフにする必要もあります。 set +fで元に戻します。

コマンドの出力を1行ずつ読み取るための信頼できるイディオムは while IFS= read -r です。

some command returning multiple lines |
while IFS= read -r ROW; do
  …
done

ほとんどのシェルは、パイプラインの各コマンドを個別のサブシェルで実行することに注意してください。したがって、変数を設定してループの後で使用する必要がある場合は、これらのコマンドをループと一緒にグループにラップします。 (Kshとzshは例外で、親シェルでパイプラインの最後のコマンドを実行します。)

some command returning multiple lines | {
  while IFS= read -r ROW; do
    …
    row_count=$((row_count+1))
  done
  echo "There were $row_count rows."
}

更新の質問で、ヒアドキュメントとヒア文字列の構文を混在させています。

ヒアドキュメントを使用する:

while IFS= read -r line ; do
    let var+=line #line 42
done <<ENDMARK
$(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
ENDMARK

またはこちらの文字列:

while IFS= read -r line ; do
    let var+=line #line 42
done <<< $(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
1
manatwork