コマンドlsblk
を使用してUSBデバイスを一覧表示するスクリプトがあります。
コマンド:
$ lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb
その結果
sdb usb Kingston DataTraveler 2.0
sdc usb Kingston DT 101 G2
後で動作するように結果を変数に保存したいので、
$ usbs=$(lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb)
私が期待していたのは、変数usbs
が上記のように2つの行全体に結果を格納することです。しかし、実行すると:
for i in ${usbs[@]}; do
echo $i
done
結果を単語に分割します:
sdb
usb
Kingston
DataTraveler
2.0
sdc
usb
Kingston
DT
101
G2
質問:grep
コマンドを使用して、コマンドの結果を2つの行全体として保存する方法はありますか?
結果をファイルにダンプしてから読み取るのではなく、簡単な解決策があるかどうかを知りたいです。
質問:
grep
コマンドを使用して、コマンドの結果を2つの行全体として保存する方法はありますか?
はい、あなたの割り当てコードは正しかったです:
usbs=$(lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb)
これは正確に必要なことを行います。 (配列ではなく)単一の変数に、改行で区切られたlsblk
からの2行を格納します。しかし、for
ループは、その変数を読み取るための適切なツールではありません。 while
ループの方がはるかに優れています。一部のリーダーにはUSBデバイスが接続されていない可能性があるため、データの例を以下に示します。
t=$(echo foo bar; echo baz bing;)
while read i ; do echo "$i" ; done <<< "$t"
出力:
foo bar
baz bing
これはさらに短い方法です:
xargs -L 1 <<< "$t"
注:プレーン[〜#〜] posix [〜#〜]スタイル変数x
は配列ではありませんが、bash
はx
を配列表記を使用して識別できるため、文句を言うことはありませんx
が配列でないことについて。デモ:
x=f
echo $x ${x[0]} ${x[@]}
出力:
f f f
ただし、x
は配列ではありません。あった場合、このコードは、(bash
パラメータ変換を使用してA
ssignment演算子)、間違いなく配列を出力します:
echo "${x[@]@A}"
...それはしません:
x='f'
対照的に、上記をが配列である場合の外観と比較してみましょう。最初に非常によく似た配列y
を作成し、次にA
ssignment演算子を使用して違いを示します。
y=(f)
echo "${y[@]@A}"
出力:
declare -a y=([0]="f")
これは readarray/mapfile を使用するのに適した状況です。
readarray -t usbs < <(lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb)
これにより、各行が独自の要素に区切られた出力を持つ配列が作成されます。
あなたの場合、それは次のような配列になります:
usbs=(
'sdb usb Kingston DataTraveler 2.0'
'sdc usb Kingston DT 101 G2'
)
同様に、出力全体を単一の変数(配列ではない)に割り当てます。これは基本的にこれを行います。
usbs='sdb usb Kingston DataTraveler 2.0
sdc usb Kingston DT 101 G2 '
それを配列にするためには、次のようにします:
usbs=($(lsblk -o NAME,TRAN,VENDOR,MODEL | grep usb))
しかし、これは空白で区切られた各単語を独自の要素に分割します。
usbs=(
sdb
usb
Kingston
DataTraveler
2.0
sdc
usb
Kingston
DT
101
G2
)
複数のコメンテーターによって指摘されており、通常は常にベストプラクティスであるように、変数は引用符で囲む必要があります。この場合、通常は常に変数を引用する必要があります。
まず、それは問題に対処する正しい方法ではないと思います。 「人を殺してはいけないので、刑務所に行くから」と言ったようなものです。
同様に、変数を引用符で囲まないでください。そうしないと、セキュリティの脆弱性が発生します。変数を引用しないのは間違っているためです(しかし、刑務所への恐怖が助けになるなら、なぜそうしないのか)。
- ステファンシャゼラス
for i in ${usbs[@]}; do
の場合、i
はusbs
。 for i in "${usbs[@]}"; do
のように引用すると、i
はusbs
のすべてのelementに設定されます。望まれるように。
これは主に 改行とbash変数 の複製ですが、配列は対象外です。そこから、複数行を含む変数を使用するには、パラメーター展開を改行で分割し、グロビングをスキップする必要があります。データによっては、他のマングリングを回避できる場合があります。
usbs=$( lsusb ... )
IFS=$'\n' # ksh bash zsh; in other shells you may need to quote an actual newline
set -o noglob # or more tersely set -f
for i in $usbs; do
printf '%s\n' "$i" # not echo which sometimes modifies some data
done
# if you do further things in the same script (or function) you may
# need to re-set IFS and/or glob, which may require saving them first
配列の場合、readarray/mapfile
Jesse_bによって提案されているように、行で分割されているため、最も単純です。しかし、あなたはできる上記のように「手動」でそれを行う:
set -o noglob # ditto
IFS=$'\n' usbs=( $( lsusb ... ) ) # only ksh up has arrays so $'' safe
# set +o noglob or set +f if needed
for i in "${usbs[@]}"; do # quoted array[@] forces splits equal to array elements only
printf '%s\n' "$i"
done