use*_*373 48 shell command-substitution newlines
我有一个名为 links.txt 的文本文件,它看起来像这样
link1
link2
link3
Run Code Online (Sandbox Code Playgroud)
我想逐行遍历这个文件并对每一行执行一个操作。我知道我可以使用 while 循环来做到这一点,但由于我正在学习,我想使用 for 循环。我实际上使用了这样的命令替换
a=$(cat links.txt)
Run Code Online (Sandbox Code Playgroud)
然后像这样使用循环
for i in $a; do ###something###;done
Run Code Online (Sandbox Code Playgroud)
我也可以做这样的事情
for i in $(cat links.txt); do ###something###; done
Run Code Online (Sandbox Code Playgroud)
现在我的问题是当我在变量 a 中替换 cat 命令输出时,link1 link2 和 link3 之间的换行符被删除并被空格替换
echo $a
Run Code Online (Sandbox Code Playgroud)
产出
链接1 链接2 链接3
然后我使用了 for 循环。当我们执行命令替换时,是否总是用空格替换新行?
问候
Joh*_*ith 44
换行符在某些时候会被换掉,因为它们是特殊字符。为了保留它们,您需要使用引号确保它们始终被解释:
$ a="$(cat links.txt)"
$ echo "$a"
link1
link2
link3
Run Code Online (Sandbox Code Playgroud)
现在,由于我在操作数据时使用引号,换行符 ( \n
) 总是由 shell 解释,因此保留下来。如果您在某些时候忘记使用它们,这些特殊字符将丢失。
如果您在包含空格的行上使用循环,则会发生完全相同的行为。例如,给定以下文件...
mypath1/file with spaces.txt
mypath2/filewithoutspaces.txt
Run Code Online (Sandbox Code Playgroud)
输出将取决于您是否使用引号:
$ for i in $(cat links.txt); do echo $i; done
mypath1/file
with
spaces.txt
mypath2/filewithoutspaces.txt
$ for i in "$(cat links.txt)"; do echo "$i"; done
mypath1/file with spaces.txt
mypath2/filewithoutspaces.txt
Run Code Online (Sandbox Code Playgroud)
现在,如果您不想使用引号,可以使用一个特殊的 shell 变量来更改 shell 字段分隔符 ( IFS
)。如果您将此分隔符设置为换行符,您将摆脱大多数问题。
$ IFS=$'\n'; for i in $(cat links.txt); do echo $i; done
mypath1/file with spaces.txt
mypath2/filewithoutspaces.txt
Run Code Online (Sandbox Code Playgroud)
为了完整起见,这里是另一个示例,它不依赖于命令输出替换。一段时间后,我发现由于该read
实用程序的行为,大多数用户认为此方法更可靠。
$ cat links.txt | while read i; do echo $i; done
Run Code Online (Sandbox Code Playgroud)
以下是read
's 手册页的摘录:
read 实用程序应从标准输入中读取一行。
由于read
逐行获取其输入,因此只要出现空格,您就可以确定它不会中断。只需通过cat
管道将输出传递给它,它就会很好地遍历您的行。
编辑:我可以从其他答案和评论中看出,人们在使用cat
. 正如jasonwryan在他的评论中所说,在 shell 中读取文件的更正确方法是使用流重定向 ( <
),正如您在val0x00ff 的回答中看到的那样。但是,由于问题不是“如何在 shell 编程中读取/处理文件”,我的回答更多地关注引号行为,而不是其他。
cuo*_*glm 40
换行符丢失了,因为 shell在命令替换后执行了字段拆分。
在 POSIX命令替换部分:
shell 应通过在子 shell 环境中执行命令来扩展命令替换(请参阅 Shell 执行环境)并将命令替换(命令文本加上封闭的“$()”或反引号)替换为命令的标准输出,删除替换结束时的一个或多个字符的序列。输出结束前的嵌入字符不得删除;但是,它们可能会被视为字段分隔符并在字段拆分期间被消除,具体取决于 IFS 的值和有效的引用。如果输出包含任何空字节,则行为未指定。
默认IFS
值(至少在bash
):
$ printf '%q\n' "$IFS"
$' \t\n'
Run Code Online (Sandbox Code Playgroud)
在您的情况下,您没有设置IFS
或使用双引号,因此在字段拆分期间将消除换行符。
您可以保留换行符,例如设置IFS
为空:
$ IFS=
$ a=$(cat links.txt)
$ echo "$a"
link1
link2
link3
Run Code Online (Sandbox Code Playgroud)
为了增加我的重点,for
循环遍历words。如果您的文件是:
one two
three four
Run Code Online (Sandbox Code Playgroud)
那么这将发出4线:
for word in $(cat file); do echo "$word"; done
Run Code Online (Sandbox Code Playgroud)
要遍历文件的行,请执行以下操作:
while IFS= read -r line; do
# do something with "$line" <-- quoted almost always
done < file
Run Code Online (Sandbox Code Playgroud)