Bash

新行和 bash 變數

  • May 16, 2019

我有一個文本文件(或管道輸出,在這裡沒關係)

memcached.uptime 1061374
memcached.curr_connections 480
memcached.cmd_get 478962548
memcached.cmd_set 17641364
memcached.cmd_flush 0

如果我使用命令cat test.txt | while read i; do echo $i; done,它會產生非常預期的輸出:

memcached.uptime 1061374
memcached.curr_connections 480
memcached.cmd_get 478962548
etc

但是,如果我循環使用,for i in $(cat test.txt); do echo $i; done我會看到一些不同的東西:

memcached.uptime
1061374
memcached.curr_connections
480
memcached.cmd_get
478962548
etc

問題是:為什麼???

答案是$(command)擴展為命令的原始輸出,然後您的 shell 將對其執行通常的分詞。所述分隔包括任何被視為單詞分隔符的空格。

您還對文本做了兩件不同的事情;一方面,你正在解析它read(它適用於輸入而不是單詞$(cat)輸入,另一方面,你正在循環中迭代輸出for。你可能會得到類似的結果IFS='\n' for i in $(cat test.txt); do echo "$i"; done

在:

cat test.txt | while read i; do echo $i; done

你設法塞進了很多 shell 腳本的不良做法:

雖然我可能應該首先提到為什麼使用 shell 循環處理文本被認為是不好的做法?.

例如嘗試如下輸入:

-n
/*/*/*/../../../*/*/*
 foo\
bar

如果您確實需要使用 shell 循環,那可能必須是這樣的:

{
while IFS= read <&3 -r i; do
 printf '%s\n' "$i" || exit
done
[ -z "$i" ] || printf %s "$i" || exit
} 3< test.txt

for i in $(cat test.txt); do echo $i; done

這用另一種方​​法代替了一種不好的做法。在這裡,您有充分的理由不$(cat test.txt)加引號:您想要 split+glob 運算符的拆分部分,但是您忘記指定要拆分的內容並禁用 glob 部分。

IFS='
' # split on newline only. The default value of $IFS
 # contains space, tab and newline which explains why you see
 # one word per line
set -o noglob # disable glob
for i in $(cat test.txt); do
 printf '%s\n' "$i" || exit
done

請注意,它仍然會跳過空行,並且會在開始循環之前讀取文件內容並將其儲存在記憶體中(多次)。

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