![读取可能不以换行符结尾的面向行的文件](https://linux22.com/image/125889/%E8%AF%BB%E5%8F%96%E5%8F%AF%E8%83%BD%E4%B8%8D%E4%BB%A5%E6%8D%A2%E8%A1%8C%E7%AC%A6%E7%BB%93%E5%B0%BE%E7%9A%84%E9%9D%A2%E5%90%91%E8%A1%8C%E7%9A%84%E6%96%87%E4%BB%B6.png)
我有一个名为的文件/tmp/urlFile
,其中每一行代表一个网址。我试图从文件中读取如下内容:
cat "/tmp/urlFile" | while read url
do
echo $url
done
如果最后一行不以换行符结尾,则不会读取该行。我想知道为什么?
是否可以读取所有行,无论它们是否以新行结束?
答案1
你会这样做:
while IFS= read -r url || [ -n "$url" ]; do
printf '%s\n' "$url"
done < url.list
(实际上,该循环在最后(非)行上添加了丢失的换行符)。
也可以看看:
答案2
好吧,read
如果它在换行符之前遇到文件结束符,则返回一个假值,但即使遇到这种情况,它仍然会分配它读取的值。因此,我们可以检查最终调用是否read
返回空行以外的内容,并正常处理它。所以,只有read
在返回 false后才退出循环和该行是空的:
#!/bin/sh
while IFS= read -r line || [ "$line" ]; do
echo "line: $line"
done
$ printf 'foo\nbar' | sh ./read.sh
line: foo
line: bar
$ printf 'foo\nbar\n' | sh ./read.sh
line: foo
line: bar
答案3
经过定义,文本文件由一系列行组成。 A线以换行符结尾。因此,文本文件以换行符结尾,除非它是空的。
内置read
函数仅用于读取文本文件。您没有传递文本文件,因此您不能希望它能够无缝工作。 shell 读取所有行——它跳过的是最后一行之后的额外字符。
如果您有一个可能格式错误的输入文件,可能缺少最后一行,您可以向其中添加换行符,以确保万无一失。
{ cat "/tmp/urlFile"; echo; } | …
应该是文本文件但缺少最后换行符的文件通常是由 Windows 编辑器生成的。这通常与 Windows 行结束符结合使用,即 CR LF,而不是 Unix 的 LF。 CR 字符在任何地方都很少有用,并且在任何情况下都不能出现在 URL 中,因此您应该将其删除。
{ <"/tmp/urlFile" tr -d '\r'; echo; } | …
如果输入文件格式正确并且以换行符结尾,则会echo
添加一个额外的空行。由于 URL 不能为空,因此只需忽略空行即可。
另请注意,它read
不会以直接的方式读取行。它忽略前导和尾随空白,这对于 URL 来说可能是理想的。它将行末尾的反斜杠视为转义字符,导致下一行与第一行减去反斜杠换行序列相连接,这绝对是不可取的。所以你应该将-r
选项传递给read
.这是非常非常罕见的,read
而不是正确的事情read -r
。
{ <"/tmp/urlFile" tr -d '\r'; echo; } | while read -r url
do
if [ -z "$url" ]; then continue; fi
…
done
答案4
另一种方式是这样的:
当读取到达文件末尾而不是行尾时,它会读入数据并将其分配给变量,但它以非零状态退出。如果你的循环是“while read;do stuff;done”构建的
因此,不要直接测试读取退出状态,而是测试一个标志,并让读取命令从循环体内设置该标志。这样,无论读取退出状态如何,整个循环体都会运行,因为读取只是循环中的命令列表之一,与其他命令一样,根本不是循环是否运行的决定因素。
DONE=false
until $DONE ;do
read || DONE=true
echo $REPLY
done < /tmp/urlFile
转介自这里。