[英]Reading output of a command into an array in Bash
我需要將腳本中命令的輸出讀入一個數組。 該命令例如:
ps aux | grep | grep | x
它逐行給出輸出,如下所示:
10
20
30
我需要將命令輸出中的值讀取到一個數組中,然后如果數組的大小小於 3,我會做一些工作。
如果命令的輸出包含空格(相當頻繁)或像*
、 ?
, [...]
。
要在數組中獲取命令的輸出,每個元素一行,基本上有 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
。my_command
之后使用printf '\\0'
語句,以便read
返回0
; 如果你不這樣做,這實際上沒什么大不了的(你只會得到一個返回碼1
,如果你不使用set -e
也沒關系——無論如何你不應該這樣做),但請記住這一點。 它更干凈,在語義上更正確。 請注意,這與printf ''
不同,后者不輸出任何內容。 printf '\\0'
打印一個空字節, read
需要在那里愉快地停止讀取(還記得-d ''
選項嗎?)。如果可以,即,如果您確定您的代碼將在 Bash≥4 上運行,請使用第一種方法。 你也可以看到它更短。
如果您想使用read
,如果您想在read
$line
進行一些處理,則循環(方法 2)可能比方法 3 具有優勢:您可以直接訪問它(通過我給出的示例中的$line
變量) ),並且您還可以訪問已讀取的行(通過我給出的示例中的數組${my_array[@]}
)。
請注意, mapfile
提供了一種具有eval'd每行一個回調讀取,而事實上,你甚至可以告訴它只是調用這個回調每N行讀取; 查看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!
但是現在讓我們使用另一個帶有globs 的命令:
$ 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
的文件......並且這個文件名與glob [three four]
匹配......此時有些人會建議使用set -f
來禁用 globbing:但是看看它:你必須改變IFS
並使用set -f
能夠修復損壞的技術(您甚至沒有真正修復它)! 這樣做時,我們實際上是在與shell作斗爭,而不是與 shell 一起工作。
$ mapfile -t my_array < <( echo "* one two"; echo "[three four]")
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="[three four]")'
在這里,我們正在使用外殼!
您可以使用
my_array=( $(<command>) )
將命令<command>
的輸出存儲到數組my_array
。
您可以使用訪問該數組的長度
my_array_length=${#my_array[@]}
現在長度存儲在my_array_length
。
這是一個簡單的例子。 想象一下,您要將文件和目錄名稱(在當前文件夾下)放入一個數組並對其進行計數。 腳本應該是這樣的;
my_array=( `ls` )
my_array_length=${#my_array[@]}
echo $my_array_length
或者,您可以通過添加以下腳本來遍歷此數組:
for element in "${my_array[@]}"
do
echo "${element}"
done
請注意,這是核心概念,必須在處理之前對輸入進行清理,即刪除多余的字符、處理空字符串等(這超出了本線程的主題)。
假設您想將整個目錄列表復制到當前目錄到數組中,它一直對我有幫助
bucketlist=($(ls))
#then print them one by one
for bucket in "${bucketlist[@]}"; do
echo " here is bucket: ${bucket}"
done
output=$(find ./src/components/ -type f -name "*.js" -print)
#loop output_array
for i in "${output[@]}"
do
printf "$i"
done
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.