簡體   English   中英

"將命令的輸出讀入 Bash 中的數組"

[英]Reading output of a command into an array in Bash

我需要將腳本中命令的輸出讀入一個數組。 該命令例如:

ps aux | grep | grep | x 

它逐行給出輸出,如下所示:

10
20
30

我需要將命令輸出中的值讀取到一個數組中,然后如果數組的大小小於 3,我會做一些工作。

如果命令的輸出包含空格(相當頻繁)或像*? , [...]

要在數組中獲取命令的輸出,每個元素一行,基本上有 3 種方法:

  1. 隨着Bash≥4使用mapfile -它是最有效的:

     mapfile -t my_array < <( my_command )
  2. 否則,循環讀取輸出(較慢,但安全):

     my_array=() while IFS= read -r line; do my_array+=( "$line" ) done < <( my_command )
  3. 正如查爾斯·達菲 (Charles Duffy) 在評論中所建議的那樣(謝謝!),以下可能比第 2 條中的循環方法執行得更好:

     IFS=$'\\n' read -r -d '' -a my_array < <( my_command && printf '\\0' )

    請確保您完全使用此表格,即確保您擁有以下內容:

    • IFS=$'\\n'在同一行read聲明:此僅設置環境變量IFSread唯一的語句。 所以它根本不會影響腳本的其余部分。 此變量的目的是告訴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

這將創建一個包含所有 .js 文件的數組

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM