Shell

BASH:使用 awk 過濾唯一行會產生 0 長度數組

  • June 22, 2016

注意:感謝 Jeff Schaller 和 steeldriver。但由於兩者都沒有作為答案發布,我不確定如何標記為已解決。我現在對管道/子殼有了更好的理解。我很確定我曾經知道這一點,但是自從我在 bash 中嘗試任何復雜的東西以來已經有很長時間了。

將過濾後的結果從 awk 分配給變數和程序替換都對我有用。我從以下位置讀取未排序的唯一行的最終程式碼stdin

while read -r FILE
do
   ...
done < <(awk '!x[$0]++')

對於那些發現這個問題並尋找類似問題的解決方案的人來說,更多關於流程替代的閱讀。

原始問題:

我搜尋了該網站,但找不到我的問題的答案。

我正在從標準輸入建構一個數組,需要過濾獨特的行。為此,我使用awk '!x[$0]++'的是我讀過的簡寫:

awk 'BEGIN { while (getline s) { if (!seen[s]) print s; seen[s]=1 } }'.

過濾器按需要工作,但問題是while read循環的結果數組為空。

例如($list用作 的代理stdin):

list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
while read -r line; do
   array[count++]=$line
done <<< "$list"
echo "array length = ${#array[@]}"
counter=0
while [  $counter -lt ${#array[@]} ]; do
   echo ${array[counter++]}
done

產生:

array length = 5
red apple
yellow banana
purple grape
orange orange
yellow banana

$list但是用 awk過濾:

list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
awk '!x[$0]++' <<< "$list" | while read -r line; do
   array[count++]=$line
done
echo "array length = ${#array[@]}"
counter=0
while [  $counter -lt ${#array[@]} ]; do
    echo ${array[counter++]}
done

產生:

array length = 0

但輸出awk '!x[$0]++' <<< "$list"看起來不錯:

red apple
yellow banana
purple grape
orange orange

我試過檢查while read循環中的每一行:

list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
i=0
awk '!x[$0]++' <<< "$list" | while read -r line; do
   echo "line[$i] = $line"
   let i=i+1
done

它看起來很好:

line[0] = red apple
line[1] = yellow banana
line[2] = purple grape
line[3] = orange orange

我在這裡想念什麼?

如果它很重要,我使用的是 bash 3.2.57:

GNU bash,版本 3.2.57(1)-release (x86_64-apple-darwin15) 版權所有 (C) 2007 Free Software Foundation, Inc.

awk '!x[$0]++' <<< "$list" | **同時讀取 -r 行;做**
   *數組***[count++]=$line
完畢**

在這種情況下, array斜體)是**subshell**(粗體)的一部分。

可以說,當子外殼還活著時,$lineand$array就具有價值。

一旦 subshel​​l 完成,也就是死亡,父(生成器)環境就會恢復。這包括刪除子shell 中設置的任何變數。

在這種情況下:

  • $array刪除,
  • $line刪除。

試試這個:

list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
awk '!x[$0]++' <<< "$list" | while read -r line; do
   array[count++]=$line
   printf "array[%d] { %s\n" ${#array[@]} # array[num_of_elements] {
   printf "       %s\n" "${array[@]}"     # elements
   printf "}\n"                           # } end of array

done

printf "\n[ %s ]\n\n" "END OF SUBSHELL (PIPE)"

printf "array[%d] {\n" ${#array[@]}
printf "       %s\n" "${array[@]}"
printf "}\n"

產量:

array[1] {
      red apple
}
array[2] {
      red apple
      yellow banana
}
array[3] {
      red apple
      yellow banana
      purple grape
}
array[4] {
      red apple
      yellow banana
      purple grape
      orange orange
}

[ END OF SUBSHELL (PIPE) ]

array[0] {

}

或者按照說明書。

我們可以從管道開始

$$ … $$管道中的每個命令都在其自己的子 shell中執行(請參閱命令執行環境)。$$ … $$

命令執行環境將冒險擴展如下:

$$ … $$在這個單獨的環境 中呼叫的命令不會影響 shell 的執行環境。 命令替換、用括號分組的命令和非同步命令在作為 shell 環境副本的子 shell 環境中呼叫,除了 shell 擷取的陷阱被重置為 shell 在呼叫時從其父 shell 繼承的值。作為管道的一部分呼叫的內置命令也在子 shell 環境中執行。對子 shell 環境所做的更改不會影響 shell 的執行環境。

$$ … $$

它不能影響:因此它不能設置。

但是,我們可以重定向並朝以下方向做一些事情:

list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'

while read -r line; do
   arr[count++]=$line
done <<<"$(awk '!x[$0]++' <<< "$list")"

echo "arr length = ${#arr[@]}"
count=0
while [[  $count -lt ${#arr[@]} ]]; do
   echo ${arr[count++]}
done

引用自:https://unix.stackexchange.com/questions/291208