Bash
新行和 bash 變數
我有一個文本文件(或管道輸出,在這裡沒關係)
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
請注意,它仍然會跳過空行,並且會在開始循環之前讀取文件內容並將其儲存在記憶體中(多次)。