zshで行配列を正しく収集する方法

zshで行配列を正しく収集する方法

以下は、出力をmy_command行配列にグループ化すると思います。

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

したがって、これは$array_of_lines[1]出力の最初の行my_command$array_of_lines[2]2番目の行などを参照します。

しかし、上記のコマンドはうまく動作しないようです。私が確認したように、my_command出力を文字ごとに分割するようです。これは、配列の要素を1行ずつ印刷すると考えられます。私もこれを確認しました: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で空白を含む要素を一覧表示するIFSまた、zshに入力を分割し、要素を配列に収集する方法を知らせる代わりに、パラメータ拡張フラグを使用してみました。つまり:

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

しかし、まだ同じ結果が得られます(分割はで発生しますn)。

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

Q1.何ですか「正しい」複数行のコマンド出力を収集する方法は?

Q2.IFS改行だけで分割を指定する方法は?

Q3.上記の3回目のように、パラメータ拡張フラグ(つまり使用)を使用して分割を指定した場合、@fzshは値を無視しますかIFS?上記の方法がなぜ機能しないのですか?

答え1

TL、博士:

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以外の値を保持するため、後続のコードを混乱させる可能性があります。ローカル設定を維持するには、IFS全体を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印刷コマンドを使用したようです(→Q3)。$array_of_lines

答え2

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

あなたの質問に答えることはできません3。

答え3

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

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

関連情報