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

我怎样才能以编程方式(即不使用vi )将DOS / Windows换行符转换成Unix?

dos2unixunix2dos命令在某些系统上不可用。 我如何用sed / awk / tr等命令来模拟这些命令?

你可以用tr从DOS转换到Unix; 不过,如果CR仅在CRLF字节对的第一个字节中出现在文件中,则只能安全地执行此操作。 这通常是这种情况。 然后你使用:

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

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

你不能这样做(使用标准的“tr”)。

如果您知道如何将回车input脚本( control-Vcontrol-Minputcontrol-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 )并使用它们要utod

 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

这样做与POSIX是棘手的:

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

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

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

  • POSIX ex不支持\r\15\n\12

删除回车:

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

要添加回车:

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

这个问题可以用标准的工具来解决,但是对于这个粗心大意的陷阱,我build议你安装flip命令,这个命令在20年前被zoo的作者Rahul Dhesi写过。 它在转换文件格式方面做得非常出色,例如,避免了二进制文件的无意破坏,如果您只是围绕改变您所看到的每个CRLF而竞争,那么这有点太简单了。

到目前为止发布的解决scheme只处理部分问题,将DOS / Windows的CRLF转换成Unix的LF; 他们缺less的部分是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 

请注意,这将文件的Unix化版本发送到标准输出。 如果你想用Unix化版本replace文件,添加perl的-i标志。

使用AWK你可以这样做:

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

使用Perl你可以做到:

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

如果您无权访问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)) 

跨超级用户发布。

一个更简单的awk解决schemew / o程序:

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

技术上'1'是你的程序,B / C awk需要一个给定的选项。

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

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

这对我有效

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

超级容易与PCRE;

作为一个脚本,或用您的文件replace$@

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

这将覆盖您的文件!

我build议只做备份(版本控制或其他)

有趣的是,在我的git-bash上windows 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在从input读取行时忽略它们,总是在输出中写入unix行尾。

对于Mac OSX,如果你已经安装了自制软件[ http://brew.sh/%5D [1 ]

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

确保你已经复制了这些文件,因为这个命令会修改这些文件。 -c mac选项使交换机与osx兼容。

TIMTOWTDI!

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

基于@GordonDavisson

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

你可以使用awk。 将logging分隔符( RS )设置为匹配所有可能的换行符或字符的正则expression式。 并将输出logging分隔符( ORS )设置为unix样式的换行符。

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

作为Jonathan Leffler的Unix to DOS解决scheme的扩展,当您不确定文件的当前行结束时,要安全地转换为DOS:

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

这将检查该行在转换为CRLF之前还没有在CRLF中结束。

只是想思考同样的问题(在Windows端,但同样适用于Linux)。令人惊讶的是,没有人提到使用良好的旧zip -ll选项(Info-压缩):

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

注:这将创build一个zip文件,保留原始文件名称,但将行结束符转换为LF。 然后, unzip文件将以unzipforms提取,即使用原始名称(但带有LF结尾),从而提示覆盖本地原始文件(如果有)。

相关摘录从zip --help

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

有很多的awk / sed / etc的答案作为补充(因为这是这个问题的热门search结果之一):

你可能没有dos2unix,但你有iconv

 iconv -f UTF-16LE -t UTF-8 [filename.txt] -f from format type -t to format type 

或者目录中的所有文件:

 find . -name "*.sql" -exec iconv -f UTF-16LE -t UTF-8 {} -o ./{} \; 

这将在当前文件夹中的所有.sql文件上运行相同的命令。 -o是输出目录,因此您可以将其replace为当前文件,或者出于安全/备份的原因,将其输出到单独的目录。

我在OSX上试过了sed的'/ M $ //'file.txt以及其他一些方法( http://www.thingy-ma-jig.co.uk/blog/25-11-2010/fixing- dos-line-endings或http://hintsforums.macworld.com/archive/index.php/t-125.html )。 没有工作,文件保持不变(顺便说一句Ctrl-V Enter需要重现^ M)。 最后我用了TextWrangler。 它不是严格的命令行,但它的工作原理,它不抱怨。