如何在Bash脚本中将DOS / Windows换行符(CRLF)转换为Unix换行符(LF)?


336

如何以编程方式(即不使用vi)将DOS / Windows换行符转换为Unix?

dos2unixunix2dos命令不可用在某些系统上。如何使用sed/ awk/ 这样的命令模拟这些tr


9
通常,仅dos2unix使用软件包管理器进行安装,它实际上要简单得多,并且在大多数平台上都存在。
Brad Koch

1
同意!@BradKoch在Mac OSX上简单为“ brew install dos2unix”
SmileIT '18

Answers:


322

您可以tr用来将DOS转换为Unix。但是,只有当CR仅作为CRLF字节对的第一个字节出现在文件中时,您才能安全地执行此操作。通常是这种情况。然后,您使用:

tr -d '\015' <DOS-file >UNIX-file

请注意,名称与名称DOS-file不同UNIX-file;如果尝试两次使用相同的名称,则文件中最终将没有数据。

您不能反过来做(使用标准的“ tr”)。

如果您知道如何输入回车,请返回一个脚本(control-Vcontrol-M输入control-M),然后:

sed 's/^M$//'     # DOS to Unix
sed 's/$/^M/'     # Unix to DOS

其中“ ^ M”是控制M字符。您还可以使用bash ANSI-C报价机制指定回车符:

sed $'s/\r$//'     # DOS to Unix
sed $'s/$/\r/'     # Unix to DOS

但是,如果您将不得不经常执行此操作(粗略地说,不止一次),则安装转换程序(例如dos2unixunix2dos,或者也许dtouutod)并使用它们就更加明智了。

如果需要处理整个目录和子目录,可以使用zip

zip -r -ll zipfile.zip somedir/
unzip zipfile.zip

这将创建一个zip存档,其行尾从CRLF更改为CR。unzip然后将转换后的文件放回原处(并逐个文件询问您-您可以回答:是全部)。感谢@vmsnomad指出了这一点。


9
使用tr -d '\015' <DOS-file >UNIX-file其中DOS-file== UNIX-file在一个空文件只是结果。不幸的是,输出文件必须是其他文件。
Buttle Butkus

3
@ButtleButkus:是的,是的。这就是为什么我使用两个不同的名称。如果在程序读取所有输入文件之前先对输入文件进行换档,就像您两次使用相同的名称一样,最终将得到一个空文件。那是类Unix系统上的统一行为。它需要特殊的代码来安全地覆盖输入文件。按照说明进行操作,即可。
乔纳森·莱夫勒

我似乎还记得文件内的搜索替换功能。
Buttle Butkus

4
有地方 您必须知道在哪里可以找到它们。在限定范围内,GNU sed选项-i(就地使用)有效;限制是链接文件和符号链接。该sort命令“始终”(自1979年以来,如果不是更早的话)支持-o可以列出输入文件之一的选项。但是,这部分是因为sort必须先读取其所有输入,然后才能写入其任何输出。其他程序偶尔会支持覆盖其输入文件之一。您可以在Kernighan&Pike 的“ UNIX编程环境”中找到通用程序(脚本)来避免问题。
Jonathan Leffler

3
第三种选择对我有用,谢谢。我确实使用了-i选项: sed -i $'s/\r$//' filename-在适当位置进行编辑。我正在无法访问互联网的机器上工作,因此软件安装是一个问题。
沃伦·露2014年

64
tr -d "\r" < file

这里看看使用示例sed

# IN UNIX ENVIRONMENT: convert DOS newlines (CR/LF) to Unix format.
sed 's/.$//'               # assumes that all lines end with CR/LF
sed 's/^M$//'              # in bash/tcsh, press Ctrl-V then Ctrl-M
sed 's/\x0D$//'            # works on ssed, gsed 3.02.80 or higher

# IN UNIX ENVIRONMENT: convert Unix newlines (LF) to DOS format.
sed "s/$/`echo -e \\\r`/"            # command line under ksh
sed 's/$'"/`echo \\\r`/"             # command line under bash
sed "s/$/`echo \\\r`/"               # command line under zsh
sed 's/$/\r/'                        # gsed 3.02.80 or higher

使用sed -i就地转化例如sed -i 's/..../' file


10
我使用了一个变体,因为我的文件只有\rtr "\r" "\n" < infile > outfile
Matt Todd 2010年

1
@MattTodd您可以将其发布为答案吗?该-d功能的使用频率更高,在“唯一\r”情况下无济于事。
n611x007 2013年

5
请注意,拟议\r\n映射具有文件双倍间距的作用;以DOS结尾的每条CRLF行都将成为\n\nUnix。
乔纳森·莱夫勒

我可以递归执行吗?
亚伦弗兰克

36

使用POSIX这样做很棘手:

  • POSIX Sed不支持\r\15。即使这样做,就位选项-i也不是POSIX

  • POSIX Awk确实支持\r\15,但是该-i inplace选项不是POSIX

  • d2udos2unix不是POSIX实用程序,但ex

  • POSIX前不支持\r\15\n或者\12

要删除回车:

ex -bsc '%!awk "{sub(/\r/,\"\")}1"' -cx file

要添加回车符:

ex -bsc '%!awk "{sub(/$/,\"\r\")}1"' -cx file

2
看起来像POSIX tr支持\r 因此,您也可以使用printf '%s\n' '%!tr -d "\r"' x | ex file(尽管被授予,但\r即使不是紧接在前面也已删除\n)。另外,POSIX未指定-b选项to ex
通配符

1
在POSIX中这样做很容易。通过键入CR文字将其嵌入脚本中(它是control-M)。
约书亚

28

您可以通过选项-c {command}以编程方式使用vim:

对Unix的用法:

vim file.txt -c "set ff=unix" -c ":wq"

Unix下要做的事情:

vim file.txt -c "set ff=dos" -c ":wq"

“ set ff = unix / dos”表示将文件的文件格式(ff)更改为Unix / DOS行尾格式

“:wq”表示将文件写入磁盘并退出编辑器(允许在循环中使用该命令)


3
这似乎是最优雅的解决方案,但是不幸的是,缺少关于wq含义的解释。
约里克·斯莱斯特

5
任何使用过的人vi都会知道这:wq意味着什么。对于那些不包含3个字符的字符,则表示1)打开vi命令区域,2)写入和3)退出。
David Newcomb

我不知道您可以从CLI交互式地向vim添加命令
Robert Dundon

您可以使用“:x”代替“:wq”
JosephConrad

25

使用AWK,您可以执行以下操作:

awk '{ sub("\r$", ""); print }' dos.txt > unix.txt

使用Perl,您可以执行以下操作:

perl -pe 's/\r$//' < dos.txt > unix.txt

2
一个不错的便携式 awk解决方案。
mklement0

23

要就地转换文件

dos2unix <filename>

要将转换后的文本输出到其他文件,请使用

dos2unix -n <input-file> <output-file>

您可以使用以下命令在Ubuntu或Debian上安装它

sudo apt install dos2unix

或在MacOS上使用自制软件

brew install dos2unix

1
我知道这个问题要求使用dos2unix的替代方法,但这是第一个Google结果。
鲍里斯(Boris)

18

可以使用标准工具解决此问题,但是我建议您安装该flip命令,该陷阱有足够多的陷阱,我建议您安装该命令,该命令由的作者Rahul Dhesi于20年前编写zoo。它在转换文件格式方面做得非常出色,例如,避免了二进制文件的无意破坏,如果您只是想改变一下看到的每个CRLF,这太容易了。


在不修改原始文件的情况下,是否可以通过流方式做到这一点?
2013年

@augurar,您可以检查“类似的软件包” packages.debian.org/wheezy/flip
n611x007 2014年

我只是通过使用错误的标志运行texxto而破坏了一半的操作系统。请特别注意如果要在整个文件夹上执行此操作。
A_P

14

到目前为止,发布的解决方案仅解决了部分问题,将DOS / Windows的CRLF转换为Unix的LF。他们缺少的部分是DOS使用CRLF作为行分隔符,而Unix使用LF作为行终止符。区别在于,DOS文件(通常)在文件的最后一行之后通常没有任何内容,而Unix则没有。为了正确进行转换,您需要添加最后的LF(除非文件的长度为零,即根本没有行)。我最喜欢的方法(添加一点逻辑来处理Mac样式的CR分隔文件,而不是破坏已经为unix格式的文件)有点perl:

perl -pe 'if ( s/\r\n?/\n/g ) { $f=1 }; if ( $f || ! $m ) { s/([^\n])\z/$1\n/ }; $m=1' PCfile.txt

请注意,这会将文件的Unixified版本发送到stdout。如果要用Unixified版本替换文件,请添加perl的-i标志。


@LudovicZenohateLagouardette是纯文本文件(即csv或制表符删除的文本)还是其他文件?如果它采用某种类似于数据库的格式,则像对待文本一样对其进行操作很可能会破坏其内部结构。
Gordon Davisson

纯文本csv,但是我认为结果很奇怪。因此,我认为它搞砸了。但是不用担心。我一直在收集备份,这甚至不是真正的数据集,只是一个1GB。真正的是26GB。
Ludovic Zenohate Lagouardette

14

如果您没有访问dos2unix的权限,但可以阅读此页面,则可以从此处复制/粘贴dos2unix.py

#!/usr/bin/env python
"""\
convert dos linefeeds (crlf) to unix (lf)
usage: dos2unix.py <input> <output>
"""
import sys

if len(sys.argv[1:]) != 2:
  sys.exit(__doc__)

content = ''
outsize = 0
with open(sys.argv[1], 'rb') as infile:
  content = infile.read()
with open(sys.argv[2], 'wb') as output:
  for line in content.splitlines():
    outsize += len(line) + 1
    output.write(line + '\n')

print("Done. Saved %s bytes." % (len(content)-outsize))

超级用户交叉发布。


1
该用法具有误导性。实数默认dos2unix转换所有输入文件。您的用法暗含-n参数。真正dos2unix的过滤器是从stdin读取的,如果未提供文件,则写入stdout。
jfs 2015年

8

借助PCRE轻松实现超级骗子;

作为脚本,或替换$@为文件。

#!/usr/bin/env bash
perl -pi -e 's/\r\n/\n/g' -- $@

这将覆盖您的文件!

我建议仅使用备份(版本控制或其他方式)进行此操作


谢谢!尽管我正在写文件名和no,但这是可行的--。我选择此解决方案是因为它很容易理解和适应我。仅供参考,这就是开关的作用:-p假定“ while input”循环,-i就地编辑输入文件,-e执行以下命令
Rolf

严格来说,PCRE是Perl的regex引擎的重新实现,而不是Perl的regex引擎。尽管名称相同,但它们也都具有这种功能,尽管也存在差异。
三胞胎

6

带有程序的更简单的awk解决方案:

awk -v ORS='\r\n' '1' unix.txt > dos.txt

从技术上讲,“ 1”是您的程序,给定选项时,b / c awk需要一个。

更新:在很长一段时间以来第一次重新访问此页面之后,我意识到还没有人发布内部解决方案,所以这里是一个:

while IFS= read -r line;
do printf '%s\n' "${line%$'\r'}";
done < dos.txt > unix.txt

这很方便,但要明确一点:这就是Unix-> Windows / DOS的翻译,这与OP要求的方向相反
mklement0

5
它是有目的的,留给作者练习。 眼球 awk -v RS='\r\n' '1' dos.txt > unix.txt
nawK 2015年

太好了(在教学技巧方面,您也感到很荣幸)。
mklement0年

1
“给定选项时,b / c awk需要一个。” - 无论是否指定选项,awk 始终需要一个程序。
mklement0年

1
纯bash的解决办法是有趣的,但比同等慢得多awksed解决方案。另外,您必须使用while IFS= read -r line来忠实地保留输入行,否则会修剪前导和尾随空格(或者,在read命令中不使用变量名并使用$REPLY)。
mklement0

5

有趣的是,我在Windows上的git-bash中sed ""已经完成了窍门:

$ echo -e "abc\r" >tst.txt
$ file tst.txt
tst.txt: ASCII text, with CRLF line terminators
$ sed -i "" tst.txt
$ file tst.txt
tst.txt: ASCII text

我的猜测是,sed在从输入中读取行时会忽略它们,并始终在输出中写入unix行尾。


4

这对我有用

tr "\r" "\n" < sampledata.csv > sampledata2.csv 

9
这将每转换一个 DOS的换行符到2 UNIX的换行。
Melebius

4

只是想想同样的问题(在Windows端,但同样适用于Linux。)令人惊讶的是,没有人提到使用非常好的旧zip -ll选项(Info-ZIP)对文本文件执行CRLF <-> LF转换的非常自动化的方法:

zip -ll textfiles-lf.zip files-with-crlf-eol.*
unzip textfiles-lf.zip 

注意:这将创建一个zip文件,保留原始文件名,但将行尾转换为LF。然后unzip将解压缩后的文件解压缩,即保留其原始名称(但带有LF结尾),从而提示覆盖本地原始文件(如果有)。

相关摘录zip --help

zip --help
...
-l   convert LF to CR LF (-ll CR LF to LF)

据我说,最好的答案是,它可以处理整个目录和子目录。我很高兴深入挖掘。
卡拉姆

2

对于Mac osx,如果已安装自制软件[ http://brew.sh/][1]

brew install dos2unix

for csv in *.csv; do dos2unix -c mac ${csv}; done;

确保已复制文件,因为此命令将在适当位置修改文件。-c mac选项使该开关与osx兼容。


这个答案确实不是原始海报的问题。
hlin117

2
OS X用户不应使用-c mac,这是用于转换OS X之前的CR仅换行符。您只想将该模式用于Mac OS 9或之前版本的文件。
askewchan '16

2

TIMTOWTDI!

perl -pe 's/\r\n/\n/; s/([^\n])\z/$1\n/ if eof' PCfile.txt

基于@GordonDavisson

必须考虑[noeol]... 的可能性


2

您可以使用awk。将记录分隔符(RS)设置为与所有可能的换行符匹配的正则表达式。并将输出记录分隔符(ORS)设置为Unix风格的换行符。

awk 'BEGIN{RS="\r|\n|\r\n|\n\r";ORS="\n"}{print}' windows_or_macos.txt > unix.txt

那就是为我工作的软件(MacOS,git diff显示^ M,在vim中编辑)
Dorian

2

在Linux上,使用sed将^ M(ctrl-M)转换为* nix换行符(^ J)很容易。

在CLI上会像这样,实际上文本中会有换行符。但是,\将^ J传递给sed:

sed 's/^M/\
/g' < ffmpeg.log > new.log

您可以通过在键入时使用^ V(ctrl-V),^ M(ctrl-M)和\(反斜杠)来获得此信息:

sed 's/^V^M/\^V^J/g' < ffmpeg.log > new.log

这对我有用,谢谢!
丹·曼蒂拉

2
sed --expression='s/\r\n/\n/g'

由于问题提到sed,因此这是使用sed实现此目的的最直接方法。表达式说的是仅用换行替换所有回车和换行。从Windows到Unix,这就是您所需要的。我验证了它的工作原理。


嗨,约翰·保罗(John Paul)-这个答案被标记为删除,因此出现在我的审查队列中。通常,当您遇到这样的问题已有8年的历史,并且有22个答案时,您需要解释一下您的答案是如何有用的,而其他现有答案则没有用。
zzxyz

0

作为对Jonathan Leffler的Unix to DOS解决方案的扩展,当不确定文件的当前行尾时可以安全地转换为DOS:

sed '/^M$/! s/$/^M/'

这会在转换为CRLF之前检查该行是否尚未以CRLF结尾。


0

我根据接受的答案制作了一个脚本,因此您可以直接将其转换,而无需最后添加其他文件,之后再删除和重命名。

convert-crlf-to-lf() {
    file="$1"
    tr -d '\015' <"$file" >"$file"2
    rm -rf "$file"
    mv "$file"2 "$file"
}

只要确保您拥有的文件“ file1.txt2”之类的文件(例如“ file1.txt”)被覆盖或将其覆盖,就可以将其用作存储文件的临时位置。


0

在bash 4.2及更高版本中,您可以使用类似以下的方法剥离尾随CR,该CR仅使用bash内置功能:

if [[ "${str: -1}" == $'\r' ]]; then
    str="${str:: -1}"
fi

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.