Bashで文字列変数の行を繰り返します。

Bashで文字列変数の行を繰り返します。

USBデバイスのリストを表示するために使用したいスクリプトがありますlsblk

注文する:

$ 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つのライン全体に保存する方法はありますか?

結果をファイルにダンプしてから、読むよりも簡単な解決策があるかどうかを知りたいです。

答え1

これは良いユースケースです配列/マップファイルの読み取り:

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に設定されます。言葉(スペースで区切られます) in usbs。この引用はそれぞれに設定されますfor i in "${usbs[@]}"; doi要素usbs、予想通り。

答え2

これは主に詐欺です。新しい行と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/mapfileJesse_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

答え3

grepQ:コマンドを使用してコマンドの結果を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できますが、配列ではないと文句を言いません。デモ:xx

x=f
echo $x ${x[0]} ${x[@]}

出力:

f f f

しかし、x配列ではありません。その場合、このコード(使用bash パラメータ変換 A移動する演算子)、確かに配列を出力します:

echo "${x[@]@A}"

...そうではありません:

x='f'

比較のため、上記の様子とどのように見えるかを比較しましょう。はいソート。まず、非常に似た配列を作成してyから使用してください。A移動する違いを示す演算子:

y=(f)
echo "${y[@]@A}"

出力:

declare -a y=([0]="f")

関連情報