新行和 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

问题是:为什么???

答案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

相关内容