我有一个文本文件(或管道输出,这里并不重要)
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
问题是:为什么???
答案1
在:
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
请注意,仍然会跳过空行,并在开始循环之前读取文件内容并将其存储在内存中(多次)。
答案2
答案是$(command)
扩展到命令的原始输出,然后您的 shell 将对其执行通常的单词分离。所述分离包括任何空格被视为单词分隔符。
您还对文本做了两件不同的事情;在其中一个你正在解析它read
(它适用于线输入而不单词$(cat)
输入,而在另一个中,您正在循环中迭代输出for
。您可能会得到类似的结果IFS='\n' for i in $(cat test.txt); do echo "$i"; done
。