在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
將結果儲存在上面的兩行中。但是如果我執行:for i in ${usbs[@]}; do echo $i done
我把結果分成幾個詞:
sdb usb Kingston DataTraveler 2.0 sdc usb Kingston DT 101 G2
問題: 有沒有一種方法可以使用該
grep
命令將命令的結果儲存為整行兩行?我更願意知道是否有一個簡單的解決方案,而不是將結果轉儲到文件中然後讀取它。
這是使用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
。
這主要是新行和 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
Jesse_b 建議的數組
readarray/mapfile
是最簡單的,因為它已經在行處分割。但是您可以像上面那樣“手動”完成它: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