zshで行の配列を適切に収集する方法


42

私は次のように出力my_commandを行の配列にグループ化すると思いました:

IFS='\n' array_of_lines=$(my_command);

その結果は、$array_of_lines[1]出力の最初の行を参照することになるmy_command$array_of_lines[2]等々第二に、そして。

ただし、上記のコマンドはうまく機能しないようです。で確認したようにmy_command、文字の周りの出力も分割するようです。私もこれをチェックしました:nprint -l $array_of_lines

echo $array_of_lines[1]
echo $array_of_lines[2]
...

2回目の試みでは、追加するevalことが役立つと考えました:

IFS='\n' array_of_lines=$(eval my_command);

しかし、それがない場合とまったく同じ結果が得られました。

最後に、zshスペースがあるList要素に関する回答に続いてIFS、入力を分割して要素を配列に収集する方法をzshに指示する代わりに、パラメーター拡張フラグを使用することも試みました。

array_of_lines=("${(@f)$(my_command)}");

しかし、私はまだ同じ結果を得ました(分割が起こっていますn

これに関して、次の質問があります。

Q1。何で「適切な」行の配列内のコマンドの出力を収集する方法は?

Q2。IFS改行のみで分割するように指定するにはどうすればよいですか?

Q3。上記の3番目の試み(つまりを使用@f)でパラメータ拡張フラグを使用して分割を指定する場合、zshはIFS?の値を無視しますか?なぜそれが上で機能しなかったのですか?

回答:


71

TL、DR:

array_of_lines=("${(@f)$(my_command)}")

最初の間違い(→Q2):2文字とにIFS='\n'設定IFSします。改行を設定するには、を使用します。\nIFSIFS=$'\n'

2番目の間違い:変数を配列値に設定するには、要素を括弧で囲む必要がありますarray_of_lines=(foo bar)

これは、連続する空白が単一のセパレータとしてカウントされるため、空行を削除することを除いて機能します。

IFS=$'\n' array_of_lines=($(my_command))

以下の空白文字を2倍にすることで、最後を除いて空行を保持できますIFS

IFS=$'\n\n' array_of_lines=($(my_command))

空行も追跡し続けるには、コマンドの出力に何かを追加する必要があります。これは、解析ではなくコマンド置換自体で発生するためです。

IFS=$'\n\n' array_of_lines=($(my_command; echo .)); unset 'array_of_lines[-1]'

(の出力がmy_command区切られていない行で終了しないと仮定します;また、の終了ステータスを失うことに注意してくださいmy_command

上記のスニペットはすべてIFSデフォルト以外の値のままなので、後続のコードを台無しにする可能性があることに注意してください。IFSlocal の設定を維持するには、localを宣言する関数にすべてを入れますIFS(ここでもコマンドの終了ステータスを保持します):

collect_lines() {
  local IFS=$'\n\n' ret
  array_of_lines=($("$@"; ret=$?; echo .; exit $ret))
  ret=$?
  unset 'array_of_lines[-1]'
  return $ret
}
collect_lines my_command

しかし、混乱しないことをお勧めしIFSます。代わりに、f展開フラグを使用して改行で分割します(→Q1):

array_of_lines=("${(@f)$(my_command)}")

または、末尾の空行を保持するには:

array_of_lines=("${(@f)$(my_command; echo .)}")
unset 'array_of_lines[-1]'

の値はIFS重要ではありません。テストでIFS印刷$array_of_linesするために分割するコマンドを使用したと思われます(→Q3)。


7
これはとても複雑です!"${(@f)...}"はと同じですが、${(f)"..."}方法が異なります。(@)二重引用符の内側は「配列要素ごとに1つの単語を生成する」ことを意味し、「(f)改行で配列に分割する」ことを意味します。PS:ドキュメントに
空飛ぶ羊

3
@flyingsheep、${(f)"..."}空の行をスキップせずに"${(@f)...}"保存します。それは間の同じ区別だ$argv"$argv[@]"。その"$@"配列のすべての要素を維持する事は70年代後半には、Bourneシェルから来ています。
ステファンシャゼラス

4

2つの問題:最初に、明らかに二重引用符もバックスラッシュエスケープを解釈しません(ごめんなさい:)。$'...'引用符を使用します。そしてman zshparam、配列に従って単語を収集するには、それらを括弧で囲む必要があります。だからこれは動作します:

% touch 'a b' c d 'e f'
% IFS=$'\n' arr=($(ls)); print -l $arr
a b
c
d
e f
% print $arr[1]
a b

Q3に答えられません。私はそのような難解なことを知る必要がないことを願っています:)。


-2

trを使用して、改行をスペースに置き換えることもできます。

lines=($(mycommand | tr '\n' ' '))
select line in ("${lines[@]}"); do
  echo "$line"
  break
done

3
行にスペースが含まれている場合はどうなりますか?
don_crissti

2
それは意味がありません。SPCとNLの両方は、デフォルト値の$IFSです。一方を他方に翻訳しても違いはありません。
ステファンシャゼラス

編集は合理的でしたか?そのままでは動作しませんでした
ジョンP

(タイムアウト)意図が何であるかを本当に理解せずに編集したことは認めますが、翻訳は文字列操作に基づいた分離の良い出発点だと思います。期待される動作がのようなものである場合を除き、スペースに変換してそれらを分割することはありませんecho
ジョンP
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.