提问人:Ed Morton 提问时间:8/19/2017 最后编辑:wjandreaEd Morton 更新时间:9/13/2023 访问量:2547
为什么我的工具输出会覆盖自身,我该如何解决?
Why does my tool output overwrite itself and how do I fix it?
问:
这个问题的目的是成为一个规范,涵盖各种问题,其答案归结为“你有DOS行尾被输入到Unix工具中”。任何有相关问题的人都应该找到一个明确的解释,说明为什么他们被指向这里,以及可以解决他们问题的工具,以及可能解决方案的利弊/警告。关于这个主题的一些现有问题已经接受了答案,这些答案只说“运行这个工具”,几乎没有解释,或者只是非常危险,永远不应该使用。
现在来讨论一个典型的问题,该问题将导致此处的推荐:
我有一个包含 1 行的文件:
what isgoingon
当我使用这个 awk 脚本打印它以颠倒字段的顺序时:
awk '{print $2, $1}' file
而不是看到我期望的输出:
isgoingon what
我让应该在行尾的字段出现在行的开头,覆盖了行首的一些文本:
whatngon
或者我将输出拆分为 2 行:
isgoingon
what
问题可能是什么,我该如何解决?
答:
问题在于您的输入文件使用的是 DOS 行尾,而不是 的 UNIX 行尾 ,并且您正在其上运行 UNIX 工具,因此 UNIX 工具正在操作的剩余部分数据。 在文件上运行时,通常用 和 表示为 control-M (),而 is 和 显示为 with 。CRLF
LF
CR
CR
\r
^M
cat -vE
LF
\n
$
cat -vE
因此,您的输入文件实际上不仅仅是:
what isgoingon
它实际上是:
what isgoingon\r\n
正如你所看到的:cat -vE
$ cat -vE file
what isgoingon^M$
和:od -c
$ od -c file
0000000 w h a t i s g o i n g o n \r \n
0000020
因此,当您在文件上运行像 awk(被视为行尾)这样的 UNIX 工具时,读取行的行为会消耗 ,但这会将 2 个字段保留为:\n
\n
<what> <isgoingon\r>
请注意第二个字段末尾的 。 表示回车,从字面上看,这是将光标返回到行首的指令。因此,当您这样做时:\r
\r
print $2, $1
awk 会将其打印到终端,终端将打印并将光标返回到行的开头,然后再打印一个空格,后跟 ,这就是为什么 似乎会覆盖 的开头。isgoingon
what
what
isgoingon
溶液
若要解决此问题,请执行以下任一操作:
dos2unix file
sed 's/\r$//' file
awk '{sub(/\r$/,"")}1' file
perl -pe 's/\r$//' file
显然,在某些 UNIX 变体(例如 Ubuntu)中又名。dos2unix
fromdos
如果您决定使用通常建议的使用,请小心,因为这将删除文件中的所有 s,而不仅仅是每行末尾的 s。(更多细节见下文。tr -d '\r'
\r
笔记
使用 awk 处理 DOS 行尾
GNU awk 将允许您通过简单地设置适当的设置来解析具有 DOS 行结尾的文件:RS
gawk -v RS='\r\n' '...' file
但其他 awk 不允许这样做,因为 POSIX 只需要 awks 支持单个字符 RS,而大多数其他 awk 会悄悄地截断为 .您可能需要添加 gawk 才能看到 s,因为底层 C 原语会在某些平台上剥离它们,例如 cygwin。RS='\r\n'
RS='\r'
-v BINMODE=3
\r
包含换行符的 CSV 数据
需要注意的一件事是,由 Excel 等 Windows 工具创建的 CSV 将用作行尾,但可以将 s 嵌入到 CSV 的特定字段中,例如:CRLF
LF
"field1","field2.1
field2.2","field3"
真的是:
"field1","field2.1\nfield2.2","field3"\r\n
因此,如果您只是将 s 转换为 s,那么您就无法再将字段中的换行符与换行符区分开来,因此,如果您想这样做,我建议您先将所有字段内换行符转换为其他内容,例如,这会将所有字段内转换为制表符并将所有行尾 s 转换为 s:\r\n
\n
LFs
CRLF
LF
gawk -v RS='\r\n' '{gsub(/\n/,"\t")}1' file
在没有 GNU awk 的情况下做类似的操作,但使用其他 awk,它涉及组合在读取时不会结束的行。CR
Awk 的默认 FS
另请注意,尽管 CR 是 POSIX 字符类的一部分,但在使用默认 FS 时,它不是作为分隔字段包含的空格字符之一,其空格字符仅为 tab、blank 和换行符。如果您的输入在 CRLF 之前有空格,这可能会导致令人困惑的结果:[[:space:]]
" "
$ printf 'x y \n'
x y
$ printf 'x y \n' | awk '{print $NF}'
y
$
$ printf 'x y \r\n'
x y
$ printf 'x y \r\n' | awk '{print $NF}'
$
这是因为在具有 LF 行结尾的行的开头/结尾处会忽略尾随字段分隔符空格,但如果前面的字符是空格,则该行的最后一个字段是 CRLF 行结尾:\r
$ printf 'x y \r\n' | awk '{print $NF}' | cat -Ev
^M$
评论
tr -d '\r'
'\r'
firstname\rlastname
first\nlast
\r
\r
\n
运行 dos2unix。虽然你可以用你自己编写的代码来操作行尾,但 Linux / Unix 世界中已经存在一些实用程序,它们已经为你做到了这一点。
如果在 Fedora 系统上将把工具放到位(如果不应该安装)。dnf install dos2unix
dos2unix
有一个类似的 deb 软件包可用于基于 Debian 的系统。dos2unix
从编程的角度来看,转换很简单。在文件中搜索序列的所有字符,并将其替换为 。\r\n
\n
这意味着有几十种方法可以使用几乎所有可以想象的工具从DOS转换为Unix。一种简单的方法是使用命令,只需将任何内容替换为任何内容!tr
\r
tr -d '\r' < infile > outfile
评论
tr -d '\r' < infile > outfile
\r
sed 's/\r$//'
可以在 PCRE 中将速记字符类用于行尾未知的文件。使用 Unicode 或其他平台需要考虑更多的行尾。该表单是 Unicode 联盟推荐的字符类,用于表示泛型换行符的所有形式。\R
\R
因此,如果您有一个“额外”,您可以找到并使用正则表达式删除它,它将规范化行尾的任意组合为 .或者,您可以使用捕获任何“行尾”的概念并标准化为字符。s/\R$/\n/
\n
s/\R/\n/g
\n
鉴于:
$ printf "what\risgoingon\r\n" > file
$ od -c file
0000000 w h a t \r i s g o i n g o n \r \n
0000020
Perl 和 Ruby 以及大多数 PCRE 风格都与字符串断言的结尾(多行模式下的行尾)相结合:\R
$
$ perl -pe 's/\R$/\n/' file | od -c
0000000 w h a t \r i s g o i n g o n \n
0000017
$ ruby -pe '$_.sub!(/\R$/,"\n")' file | od -c
0000000 w h a t \r i s g o i n g o n \n
0000017
(注意,这两个词之间正确地保留了)\r
如果没有,可以使用 in PCRE 中的等效项。\R
(?>\r\n|\v)
使用直接的 POSIX 工具,您最好的选择可能是这样的:awk
$ awk '{sub(/\r$/,"")} 1' file | od -c
0000000 w h a t \r i s g o i n g o n \n
0000017
有点工作的东西(但知道你的局限性):
tr
即使在其他上下文中使用也会全部删除(当然很少使用 OF,并且 XML 处理需要删除它,因此是一个很好的解决方案):\r
\r
\r
tr
$ tr -d "\r" < file | od -c
0000000 w h a t i s g o i n g o n \n
0000016
GNU可以工作,但POSIX不能工作,因为POSIX不支持。sed
sed
\r
\x0D
仅限 GNU sed:
$ sed 's/\x0D//' file | od -c # also sed 's/\r//'
0000000 w h a t \r i s g o i n g o n \n
0000017
Unicode 正则表达式指南可能是确定“换行符”的最佳选择。
评论
\R
\r\n
\v
\n
"foo\v\nbar"\r\n
\R
\r\n
\R\n
\v\n
\R
od -c < file
" f o o \v \n b a r " \r \n
perl -pe 's/\r$/\n/' file | od -c
" f o o \v \n b a r " \n \n
perl -pe 's/\R$/\n/' file | od -c1
" f o o \n \n b a r " \n
\R
\v\n
\r\n
\n\n
\r$
\n
\R$
\n
\n
\v
s/\R$/\n/
\n\n
\v\n
\n
\r\n
s/\R$/\n/
\r\n
\n
"foo\v\nbar"\r\n
\R
\R$
\R
\v
\v
\v
/[\n\cK\f\r\x85\x{2028}\x{2029}]/
评论
awk
sed