スクリプト内のコマンドの出力を配列に読み込む必要があります。コマンドの例は次のとおりです。
ps aux | grep | grep | x
次のように行ごとに出力されます。
10
20
30
コマンド出力からの値を配列に読み込む必要があり、配列のサイズが 3 未満の場合は何らかの処理を実行します。
ベストアンサー1
コマンドの出力にスペース (かなり頻繁に発生します) や 、 、 などの glob 文字が含まれている場合、他の回答は機能しなく*
なり?
ます[...]
。
コマンドの出力を要素ごとに 1 行ずつ配列で取得するには、基本的に次の 3 つの方法があります。
Bash≥4 を使用すると
mapfile
最も効率的です。mapfile -t my_array < <( my_command )
それ以外の場合は、出力を読み取るループ(遅いですが安全です):
my_array=() while IFS= read -r line; do my_array+=( "$line" ) done < <( my_command )
コメントで Charles Duffy が示唆したように (ありがとう!)、次の方法の方が 2 番目のループ メソッドよりもパフォーマンスが向上する可能性があります。
IFS=$'\n' read -r -d '' -a my_array < <( my_command && printf '\0' )
必ずこのフォームを正確に使用してください。つまり、次の内容が含まれていることを確認してください。
IFS=$'\n'
次の文と同じ行にread
:これは環境変数のみを設定しますIFS
声明read
のみ。したがって、スクリプトの残りの部分にはまったく影響しません。この変数の目的は、read
EOL 文字でストリームを中断するように指示することです\n
。-r
: これは重要です。read
バックスラッシュをエスケープシーケンスとして解釈しないようにします。-d ''
-d
:オプションとその引数の間にスペースを入れてください''
。ここにスペースを入れないと、オプションは''
表示されず、引用削除Bash がステートメントを解析するときのステップ。これはread
nil バイトで読み取りを停止するように指示します。 と書く人もいます-d $'\0'
が、実際には必要ありません。 の-d ''
方が良いでしょう。-a my_array
ストリームを読み取りながらread
配列を設定するように指示します。my_array
printf '\0'
次の文を使用する必要があります後my_command
なので、 がread
返されます0
。 実際に使用しなくても大きな問題にはなりません ( という戻りコードが返されるだけですが1
、 を使用しない場合は問題ありませんset -e
。そもそも は使用すべきではありません)。ただし、その点に注意してください。 の方がよりクリーンで、意味的に正しいです。 これは とは異なりますprintf ''
。 は何も出力しません。 は、がそこで読み取りを停止するためprintf '\0'
に必要な null バイトを出力します(オプションを覚えていますか?)。read
-d ''
可能であれば、つまり、コードが Bash 4 以上で実行されることが確実な場合は、最初の方法を使用してください。また、この方法の方が短いこともわかります。
を使用する場合read
、行が読み取られるときに何らかの処理を実行したい場合、ループ (方法 2) は方法 3 よりも有利である可能性があります。行に直接アクセスでき ($line
例の変数経由)、すでに読み取られた行にもアクセスできます (${my_array[@]}
例の配列経由)。
は、mapfile
各行の読み取り時にコールバックを評価する方法を提供しており、実際には、このコールバックを次の行ごとに呼び出すように指示することもできます。いいえ行を読み、とその中のhelp mapfile
オプションを見てください。(これについての私の意見は、少し扱いにくいですが、単純なことだけを行う場合に時々使用できるということです。そもそもなぜこれが実装されたのか、私にはよくわかりません!)。-C
-c
ここで、次の方法を採用する理由を説明します。
my_array=( $( my_command) )
スペースがあると壊れます:
$ # I'm using this command to test:
$ echo "one two"; echo "three four"
one two
three four
$ # Now I'm going to use the broken method:
$ my_array=( $( echo "one two"; echo "three four" ) )
$ declare -p my_array
declare -a my_array='([0]="one" [1]="two" [2]="three" [3]="four")'
$ # As you can see, the fields are not the lines
$
$ # Now look at the correct method:
$ mapfile -t my_array < <(echo "one two"; echo "three four")
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # Good!
その後、一部の人々はIFS=$'\n'
それを修正するために以下を使用することを推奨します:
$ IFS=$'\n'
$ my_array=( $(echo "one two"; echo "three four") )
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # It works!
しかし、今度は別のコマンドを使ってみましょう。グロブ:
$ echo "* one two"; echo "[three four]"
* one two
[three four]
$ IFS=$'\n'
$ my_array=( $(echo "* one two"; echo "[three four]") )
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="t")'
$ # What?
t
これは、現在のディレクトリに…というファイルがあり、このファイル名がグロブ [three four]
…この時点で、グロブを無効にするために を使用することを推奨する人もいます。しかし、考えてみてください。壊れたテクニックを修正するには、 を変更して を使用するset -f
必要があります(実際には修正していません)。これを行うと、IFS
set -f
戦うシェルではなくシェルの操作。
$ mapfile -t my_array < <( echo "* one two"; echo "[three four]")
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="[three four]")'
ここではシェルを操作しています。