如何将DOS / Windows换行符(CRLF)转换为Unix换行符(\ n)在Bash脚本中?
我怎样才能以编程方式(即不使用vi
)将DOS / Windows换行符转换成Unix?
dos2unix
和unix2dos
命令在某些系统上不可用。 我如何用sed
/ awk
/ tr
等命令来模拟这些命令?
你可以用tr
从DOS转换到Unix; 不过,如果CR仅在CRLF字节对的第一个字节中出现在文件中,则只能安全地执行此操作。 这通常是这种情况。 然后你使用:
tr -d '\015' <DOS-file >UNIX-file
请注意,名称DOS-file
不同于名称UNIX-file
; 如果您尝试使用相同的名称两次,您将最终没有文件中的数据。
你不能这样做(使用标准的“tr”)。
如果您知道如何将回车input脚本( control-V , control-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
但是,如果你经常这样做(不止一次,粗略地说),安装转换程序(例如dos2unix
和unix2dos
,或者dtou
和utod
)并使用它们要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 -
d2u和dos2unix不是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
文件将以unzip
forms提取,即使用原始名称(但带有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。 它不是严格的命令行,但它的工作原理,它不抱怨。