web-dev-qa-db-ja.com

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

次のようにすると、my_commandの出力が行の配列にグループ化されます。

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

そのため、$array_of_lines[1]my_commandの出力の最初の行を参照し、2番目の行は$array_of_lines[2]を参照します。

ただし、上記のコマンドはうまく機能しないようです。また、my_commandの出力を文字nで分割しているようです。print -l $array_of_linesで確認したところ、配列の要素が1行ずつ出力されると思います。私もこれをチェックしました:

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番目の試行のように(つまり、@fを使用して)パラメータ拡張フラグを使用して分割を指定した場合、zshはIFSの値?上記でうまくいかなかったのはなぜですか?

44

TL、DR:

_array_of_lines=("${(@f)$(my_command)}")
_

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

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

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

_IFS=$'\n' array_of_lines=($(my_command))
_

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

_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 localの設定を維持するには、IFS localを宣言する関数にすべてを入れます(ここでもコマンドの終了ステータスの保持に注意しています)。

_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)。

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にはお答えできません。私はそのような難解なことを決して知る必要がないことを願っています:)。

5
angus

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

lines=($(mycommand | tr '\n' ' '))
select line in lines; do
  echo $line
  break
done
0
Jimchao