读取面向行的文件,该文件不能以换行符结尾


11

我有一个文件名/tmp/urlFile,其中每一行代表一个URL。我正在尝试从文件中读取内容,如下所示:

cat "/tmp/urlFile" | while read url
do
    echo $url
done

如果最后一行不以换行符结尾,则不会读取该行。我想知道为什么?

是否可以读取所有行,而不管它们是否以新行结尾?



2
哈@Stéphane我喜欢那里的待定;-)。
史蒂芬·基特

2
缺少尾随换行符的另一种方法;awk 1 /tmp/urlFile.. soawk 1 /tmp/urlFile | while ...
muru,

@muru,这比这里的其他答案更好。
通配符'18

1
因为您要问为什么不读它:stackoverflow.com/a/729795/1968
康拉德·鲁道夫

Answers:


13

您可以:

while IFS= read -r url || [ -n "$url" ]; do
  printf '%s\n' "$url"
done < url.list

(实际上,该循环将丢失的换行符添加回最后(非)行)。

也可以看看:


谢谢。我读了链接的文章,也许我错过了一些东西,为什么“该循环将丢失的换行符添加回最后(非)行”?
蒂姆(Tim)

1
@Tim Stephane的意思似乎是,由于printf此处的所有调用都具有,所以它将在输出中添加缺少的换行符\n
Sergiy Kolodyazhnyy

6

这似乎可以通过以下方式部分解决readarray -t

readarray -t urls "/tmp/urlFile"
for url in "${urls[@]}"; do
    printf '%s\n' "$url"
done

但是请注意,尽管这确实适用于大小合理的文件,但此解决方案会给大型文件带来潜在的新问题-它首先将文件读取到数组中,然后必须对其进行遍历。对于非常大的文件,这可能既浪费时间又消耗内存,甚至可能导致故障。


谢谢。它解决哪一部分而不解决?
蒂姆(Tim)

它解决了缺少尾随换行符的问题,但是由于文件很大,因此引入了潜在的新问题,因为它首先将文件读取到数组中,然后必须对其进行遍历。
DopeGhoti

1
@DopeGhoti很好的信息-我建议您直接将其添加到答案中吗?
RJHunter

Tha答案已被修改。
DopeGhoti

5

根据定义,文本文件由一系列行组成。甲线以换行符结束。因此,文本文件以换行符结尾,除非为空。

read内建只是为了阅读的文本文件。您没有传递文本文件,因此您不能希望它无缝运行。Shell会读取所有行-跳过的是最后一行之后的多余字符。

如果您有一个可能格式错误的输入文件,可能缺少最后一行,则可以确保在其中添加换行符。

{ cat "/tmp/urlFile"; echo; } | 

Windows编辑器通常会生成应为文本文件但缺少最后换行符的文件。这通常与Windows行尾CR LF结合使用,与Unix的LF相反。CR字符很少在任何地方有用,并且在任何情况下都不会出现在URL中,因此应将其删除。

{ <"/tmp/urlFile" tr -d '\r'; echo; } | 

如果输入文件格式正确且以换行符结尾,则echo添加额外的空白行。由于网址不能为空,因此只需忽略空白行。

还请注意,read这不会以直接的方式读取行。它忽略前导和尾随空格,这对于URL可能是理想的。它将行尾的反斜杠视为转义字符,导致下一行与第一行(减去反斜杠-换行符)连接在一起,这绝对是不希望的。因此,您应该将-r选项传递给readread做对而不是做是非常非常少的read -r

{ <"/tmp/urlFile" tr -d '\r'; echo; } | while read -r url
do
  if [ -z "$url" ]; then continue; fi
  
done

3

好吧,read如果它在换行符之前遇到文件结尾,则返回一个伪造的值,但是即使这样做,它仍然会分配它读取的值。因此,我们可以检查最终的return是否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

1

另一种方式是这样的:

当读取到达文件末尾而不是行尾时,它确实读入数据并将其分配给变量,但以非零状态退出。如果您的循环是“边读边做;做点东西;做完”的

因此,与其直接测试读取退出状态,不如测试一个标志,然后让读取命令从循环体内设置该标志。这样,无论读取退出状态如何,整个循环主体都会运行,因为read像其他循环一样只是循环中的命令列表之一,而不是循环是否将完全运行的决定性因素。

DONE=false
until $DONE ;do
read || DONE=true
echo $REPLY 
done < /tmp/urlFile

这里引用。


1
猫“ / tmp / urlFile” | 同时读取网址
做
    回声$ url
做完了

这是的无用用法cat

具有讽刺意味的是,您可以在cat这里用实际上有用的东西代替该过程:POSIX系统具有的一种工具,用于添加缺少的换行符,并将该文件制作为正确的POSIX文本文件。

sed -e'$ a \'“ / tmp / urlFile” | 同时读取-r url
做
    printf“%s \ n”“ $ {url}”
做完了

进一步阅读


1
当输入不以换行符结尾时,POSIX无法指定sed的行为。当行数大于LINE_MAX时,read也会指定行为。
斯特凡·查泽拉斯18'Jan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.