为什么 echo "$out" 将输出分成多行,引号抑制分词?

Why does echo "$out" split output onto multiple lines, if quotes suppress word-splitting?

我有一个非常简单的目录,里面有 "directory1" 和 "file2"。

之后
out=`ls`

我想打印我的变量:echo $out 给出:

directory1   file2

echo "$out" 给出:

directory1
file2

所以使用引号让我在单独的行上输出每条记录。正如我们所知,ls 命令对所有 files/dirs 使用单行打印输出(如果行足够大以包含输出)所以我预计使用双引号会阻止我的 shell 将单词拆分为单独的省略引号的行会将它们分开。 请告诉我:为什么使用引号(用于防止分词)突然拆分输出?

关于 ls

的行为

ls 默认情况下仅在单行上打印多个文件名 当输出到 TTY 时。当输出到管道、文件或类似文件时,默认是将一行打印到文件。

引自 the POSIX standard for ls,并强调:

The default format shall be to list one entry per line to standard output; the exceptions are to terminals or when one of the -C, -m, or -x options is specified. If the output is to a terminal, the format is implementation-defined.


文字问题(回复:引用)

正是将您的命令拆分为单独参数的行为才导致它被放在一行中! 本地,你的值跨越多行,所以回显它未修改(没有任何分割)精确地打印它。

您的命令的结果类似于:

 out='directory1
 file2'

当您 运行 echo "$out" 时,将打印 确切的 内容。相比之下,当您 运行 echo $out 时,行为类似于:

echo "directory1" "file2"

...因为字符串被分成两个元素,每个元素都作为完全不同的参数传递给 echo,以便 echo 按照它认为合适的方式进行处理——在这种情况下,在同一行打印这两个参数。


分词的副作用

分词可能看起来 就像您在这里想要的那样,但通常情况并非如此!考虑一些特殊问题:

  • 分词扩展 glob 表达式:如果文件名包含一个被空格包围的 *,则 * 将替换为当前目录中的文件列表,从而导致重复结果。
  • 分词不支持引号或转义:如果文件名包含空格,则无法将该内部空格与分隔多个名称的空格区分开来。这与BashFAQ #50.
  • 中描述的问题密切相关

阅读目录

参见 Why you shouldn't parse the output of ls。简而言之——在您的 out=`ls` 示例中,out 变量(是一个字符串)无法以有用的、可解析的方式存储所有可能的文件名。

例如,考虑这样创建的文件:

touch $'hello\nworld"three words here"'

...该文件名包含空格和换行符,并且分词不会在 ls 的输出中将其正确检测为单个名称。但是,您可以在数组中存储和处理它:

# create an array of filenames
names=( * )
if ! [[ -e $names || -L $names ]]; then  # this tests only the FIRST name
  echo "No names matched" >&2            # ...but that's good enough.
else
  echo "Found ${#files[@]} files" # print number of filenames
  printf '- %q\n' "${names[@]}"
fi