我有这行代码,可以逐行读取文本文件。
文本文件有时由 Windows 用户生成,有时由 Unix 用户生成。因此,有时我看到\r\n
行尾,有时我只看到\n
。
我希望我的脚本能够处理这两种情况并分别到达每一行,无论换行符是\r
, or \n
, or \r\n
, or \n\r
。
while read -r textFileLines; do ... something ...; done < text_file.txt
这段代码作品\n\r
每行末尾带有(LF CR),但确实不行当我已经排到\r\n
最后的时候!
测试
使用创建一个新的文本文件
Notepad++ v7.5.4
while read -r LINE; do echo "$LINE"; done < /cygdrive/d/test_text.txt
终端输出:
first_line second_line third_string
为什么没有fourth_output
显示该行?
答案1
如果您有一些文件是 DOS 文本文件,另一些文件是 Unix 文本文件,则脚本可以通过以下方式传递所有数据dos2unix
:
dos2unix <filename |
while IFS= read stuff; do
# do things with "$stuff"
done
Unix 文本文件不会因此被修改。
为了另外处理 Mac 换行问题,我相信你应该能够做到
dos2unix <filename | mac2unix |
while IFS= read stuff; do
# do things with "$stuff"
done
最后一行不是由read
循环输出的,因为它没有终止,因此根本不是一行。
要检测文件的最后一行是否没有终止换行符,如果没有则添加一个,在bash
:
if [ "$( tail -c 1 filename )" != $'\n' ]; then
printf '\n' >>filename
fi
有关的:
答案2
为什么没有
fourth_output
显示该行?
在您的图像中,文件在最后一行末尾缺少换行符。read
仅当它读取分隔符(换行符)时才返回 true,并且由于最后一行的末尾不存在该分隔符,因此read
返回 false,循环结束,并且不打印最后一个不完整的行。
这与回车无关,如果最后一行缺少 NL,即使只有 NL,行为也是相同的。
这里file1
有两行以 CRLF 行结尾:
$ cat -A file1
foo^M$
bar^M$
$ while read x ; do echo "<$x>"; done < file1
>foo
>bar
file2
缺少第二行结尾的行:
$ cat -A file2 ; echo
foo^M$
bar
$ while read x ; do echo "<$x>"; done < file2
>foo
如果您想让循环也处理最后一行片段,则必须在变量返回失败read
时检查变量是否包含任何数据:read
$ while read -r x || [ "$x" ] ; do echo "<$x>"; done < file2
>foo
<bar>
如果您想删除 CR,您可以在循环中删除它,例如(在 Bash/ksh/zsh 中),或者使用or等x=${x%$'\r'};
预处理文件。tr -d '\r'
dos2unix
答案3
执行:
$ [ -n "$(tail -c1 infile)" ] && echo >> infile
$ sed 's/\r$\|^\r//g;s/\r/\n/g' infile | while IFS= read -r line
> do echo "$line" ; done
DOS line
second DOS
old mac line
new mac line
end\n\rreverse
linux line
new linux line
所有问题都解决了。
描述:
要纠正丢失的最后一个换行符,请使用:
[ -n "$(tail -c1 infile)" ] && echo >> infile
仅在需要时才会添加尾随换行符(不会更改正确的文件)。
然后,你可以转换
\r\n
(DOS风格)到\n
(只需删除行尾的\r)\n\r
(无效的 DOS 风格?)到一\n
(删除行首的 \r)- 然后(对更正后的)将
\r
(旧的 MAC)转换为\n
只需一次调用 (GNU) sed 即可:
sed 's/\r$\|^\r//g;s/\r/\n/g' infile
如果文本文件像这样的测试文件:
$ cat infile
DOS line
second DOS
new mac line
end\n\rreverse
linux line
new linux line
no end line
$ cat -A infile
DOS line^M$
second DOS^M$
old mac line^Mnew mac line$
end\n\rreverse$
^Mlinux line$
new linux line$
no end line
$ od -An -tc infile
D O S l i n e \r \n
s e c o n d D O S \r \n
o l d m a c l i n e \r
n e w m a c l i n e \n
e n d \ n \ r r e v e r s e \n \r
l i n u x l i n e \n
n e w l i n u x l i n e \n
n o e n d l i n e
答案4
有明确的工具可用于执行此操作。可用于\r\n
从文件中删除的更常见的称为dos2unix
.
如果这在您的系统上不可用,您可以使用以下命令之一对您的textFileLines
变量执行类似的操作:
$ echo "$textFileLines" | awk 1 RS='\r\n' ORS=
sed 1
$ echo "$textFileLines" | sed -e 's/\r//g'
sed 2
$ echo $textFileLines | sed $'s/\r//'
t
$ echo "$textFileLines" | tr -d '\r'
当然还有很多其他方法可以做到这一点,这些只是其中一些较常见的方法。