如何以变量作为一行而不是文本中的一个单词进行循环?

如何以变量作为一行而不是文本中的一个单词进行循环?

这是输入:

$ ls -1  # list folder name in current folder.
the cat
the dog
the rat

$ for var in $(ls -1); do echo $var; done # the command

这是它的输出:

the
cat
the 
dog
the 
rat

这是我需要的输出:

the cat
the dog
the rat

答案1

如果您ls仅使用示例来说明可能包含空格的输出,您可以尝试以下操作(ls用您的命令替换):

ls | while IFS= read -r line ; do
   echo "$line"
done

如果没有IFS=,该read命令会删除每行开头和结尾的空格。如果没有-r,它会将\输入行的末尾视为行继续符,并直接删除其他\字符。

请注意,ls即使您没有传递 ,管道本身在传输时也会默认为每行一个文件-1。(可以有每个文件多于一行,因为文件名可以包含换行符。)当通过管道传输时,ls如果通常启用颜色,也会禁用颜色。

要处理文件,您可以尝试:

while IFS= read -r line ; do
   echo "$line"
done < file.txt

如果您想循环文件名,则bash具有内置功能,无需解析ls

for file in * ; do
   echo "$file"
done

答案2

不用写$(ls -1),只要写*。这是一个全局

单独使用时,*会展开为当前目录中的文件列表(常规文件、目录等)。默认情况下,.会省略名称以 开头的文件,这也是 的行为ls,也是您通常想要的。

$ for f in *; do echo "$f"; done
the cat
the dog
the rat

通配符也被称为文件名扩展或路径名扩展。

一般来说,最好避免解析输出ls. 尝试这样做几乎总是不正确且通常相当复杂。当您的目标是提取文件名并将其用作命令的参数时,就像这里提出的问题一样,这不仅困难,而且在实践中不可能的编写正确的ls解析解决方案。该ls命令生成供人类阅读的输出。文件名可以包含除/和空字符之外的任何字符,甚至换行符。此外,人们尝试解析lsbreak 输出的最常见方法是解析极其常见的文件名,例如任何包含空格的文件名。在这种情况下,复杂性的差异是显而易见的:您所需要的只是*

另一件需要注意的大事是引用. 当你写壳循环(它本身通常最好避免),扩展在这个例子中,循环变量$f几乎总是应该被括在双引号,如上所示。否则,shell 会尝试执行单词拆分和(在这种情况下,是额外的和不必要的)通配符如果你不能清楚地解释为什么你没有遇到变量扩展必须取消引用的罕见情况,你应该引用它

最后,请注意,当参数以 开头时,许多命令在某些或所有位置会特殊处理参数-。主要原因是大多数命令接受以 引入的选项-,例如-l(尽管有些命令-本身也意味着标准输入或者标准输出)。但文件名可以以 开头-。许多(但不是全部)命令接受--指示选项的结束,因此即使后续参数以 开头,它们也将被解释为文件名-。一个更广泛适用的解决方案是确保文件指定的路径-例如,从扩大全球范围./*,而不是*。至于echo,它不会进行--特殊处理,并且您说您需要的输出没有尾随的./;为了在以 开头的文件名面前保持稳健-(例如-n,它告诉echo抑制最后的换行符),您可以echo用 替换printf '%s\n'

虽然*在本页的其他地方已经提到过,但我认为它值得有自己的答案。通配符(使用*)是解决此问题所提出的实际任务的唯一合理解决方案。它不是使用lswith 命令替换或将 的输出通过管道传输ls到 的替代方法while——因为这些技术根本做不到正确的事情,永远不应该使用。让其他方法与 一起正确工作根本不可行ls

答案3

您应该首先将 IFS (内部字段分隔符)设置为换行符,然后

IFS=$'\n' && for var in $(ls -1); do echo $var; done

将工作

答案4

您可以使用 mapfile 将标准输入中的行读入索引数组:

mapfile -t < <(ls -1); for i in "${MAPFILE[@]}"; do echo $i; done

相关内容