How can I delete a newline if it is the last character in a file?
我有一些文件,如果它是文件中的最后一个字符,我想删除最后一个换行符。
1 | 0013600 n t > |
我已经尝试了一些与sed的技巧,但我能想到的最好的不是诀窍:
1 2 | sed -e '$s/\(.*\) $/\1/' abc |
任何想法如何做到这一点?
1 | perl -pe 'chomp if eof' filename >filename2 |
或者,编辑文件:
1 | perl -pi -e 'chomp if eof' filename |
[编者注:
这在我看到的awk网站上被描述为"perl blasphemy"。
但是,在测试中,它起作用了。
您可以利用shell命令替换删除尾随换行符的事实:
在bash,ksh,zsh中工作的简单形式:
便携式(POSIX兼容)替代方案(效率略低):
注意:
-
如果
in.txt 以多个换行符结尾,则命令替换将删除所有这些 - 谢谢,@ Sparhawk。 (除了尾随换行符之外,它不会删除空白字符。) - 由于此方法将整个输入文件读入内存,因此仅建议使用较小的文件。
-
printf %s 确保没有新行附加到输出(它是符合POSIX的非标准echo -n 的替代方法;请参阅http://pubs.opengroup.org/onlinepubs/009696799/utilities/echo.html和https: //unix.stackexchange.com/a/65819)
其他答案的指南:
-
如果Perl可用,请选择接受的答案 - 它简单且内存效率高(不会立即读取整个输入文件)。
-
否则,考虑一下ghostdog74的Awk答案 - 它模糊不清,而且内存效率高;更具可读性的等价物(POSIX兼容)是:
-
awk 'NR > 1 { print prev } { prev=$0 } END { ORS=""; print }' in.txt -
打印延迟一行,以便最终行可以在
END 块中处理,由于将输出记录分隔符(OFS )设置为空字符串,因此打印时不会显示尾部 。
-
-
如果你想要一个简单但快速而强大的解决方案,它可以真正编辑就地生成(而不是创建一个替换原始文件的临时文件),请考虑使用jrockway的Perl脚本。
您可以使用GNU coreutils中的
1 | head -c -1 |
要测试结束换行符,可以使用
1 2 3 4 | if [[ $(tail -c1 file | wc -l) == 1 ]]; then head -c -1 file > file.tmp mv file.tmp file fi |
您还可以使用
1 | [[ $(tail -c1 file | wc -l) == 1 ]] && head -c -1 file | sponge file |
您还可以通过将其填入
1 2 3 4 5 6 7 8 9 10 | # Example: remove-last-newline < multiline.txt function remove-last-newline(){ local file=$(mktemp) cat > $file if [[ $(tail -c1 $file | wc -l) == 1 ]]; then head -c -1 $file > $file.tmp mv $file.tmp $file fi cat $file } |
更新
正如KarlWilbur在评论中所指出并在Sorentar的回答中使用的那样,
1 2 3 |
编辑2: s>
这是
awk'{if(line)print line; line = $ 0} END {printf $ 0}'abc s>
呆子
1 | awk '{q=p;p=$0}NR>1{print q}END{ORS =""; print p}' file |
单行文件的一种非常简单的方法,需要来自coreutils的GNU echo:
1 | /bin/echo -n $(cat $file) |
如果你想做得对,你需要这样的东西:
1 2 3 4 5 6 7 8 9 10 11 12 |
我们打开文件进行阅读和追加;打开追加意味着我们已经
对于任何输入,它都以恒定的时间和恒定的空间运行,并且不需要任何更多的磁盘空间。
这是一个漂亮,整洁的Python解决方案。我没有试图在这里简洁。
这会就地修改文件,而不是复制文件并从副本的最后一行剥离换行符。如果文件很大,这将比选择作为最佳答案的Perl解决方案快得多。
如果最后两个字节是CR / LF,它会将文件截断两个字节,如果最后一个字节是LF,则截断一个字节。如果最后一个字节不是(CR)LF,它不会尝试修改文件。它处理错误。在Python 2.6中测试过。
把它放在一个名为"striplast"和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | #!/usr/bin/python # strip newline from last line of a file import sys def trunc(filename, new_len): try: # open with mode"append" so we have permission to modify # cannot open with mode"write" because that clobbers the file! f = open(filename,"ab") f.truncate(new_len) f.close() except IOError: print"cannot write to file:", filename sys.exit(2) # get input argument if len(sys.argv) == 2: filename = sys.argv[1] else: filename ="--help" # wrong number of arguments so print help if filename =="--help" or filename =="-h" or filename =="/?": print"Usage: %s <filename>" % sys.argv[0] print"Strips a newline off the last line of a file." sys.exit(1) try: # must have mode"b" (binary) to allow f.seek() with negative offset f = open(filename,"rb") except IOError: print"file does not exist:", filename sys.exit(2) SEEK_EOF = 2 f.seek(-2, SEEK_EOF) # seek to two bytes before end of file end_pos = f.tell() line = f.read() f.close() if line.endswith(" "): trunc(filename, end_pos) elif line.endswith(" "): trunc(filename, end_pos + 1) |
附:本着"Perl高尔夫"的精神,这是我最短的Python解决方案。它将整个文件从标准输入篡改到内存中,从最后删除所有换行符,并将结果写入标准输出。不像Perl那样简洁;你只是无法击败Perl这样的小巧琐事。
从
将其放入"slurp_and_chomp.py"然后运行
另一个perl WTDI:
1 2 | perl -i -p0777we's/ \z//' filename |
一个快速的解决方案是使用gnu实用程序truncate:
1 |
如果文件确实有一个尾随的新行,则测试将为true。
删除非常快,真正到位,不需要新文件,搜索也只从一个字节读取(tail -c1)。
1 2 | $ perl -e 'local $/; $_ = <>; s/ $//; print' a-text-file.txt |
另请参阅在sed中匹配任何字符(包括换行符)。
假设Unix文件类型,你只想要最后一个换行符。
1 | sed -e '${/^$/d}' |
它不适用于多个换行符......
*仅在最后一行为空行时才有效。
使用dd:
1 2 3 4 5 |
1 2 | perl -pi -e 's/ $// if(eof)' your_file |
POSIX SED:
'$ {/ ^ $ / d}'
1 2 3 4 | $ - match last line { COMMANDS } - A group of commands may be enclosed between { and } characters. This is particularly useful when you want a group of commands to be triggered by a single address (or address-range) match. |
又一个答案FTR(和我最喜欢的!):echo / cat你要剥离的东西并通过反引号捕获输出。最终换行将被删除。例如:
1 2 3 4 5 6 7 8 9 10 |
1 2 3 | sed ':a;/^ *$/{$d;N;};/ $/ba' file |
如果您需要使用管道/重定向而不是从文件读取/输出,这是一个很好的解决方案。这适用于单行或多行。无论是否有尾随换行,它都有效。
1 2 3 4 5 6 7 8 9 10 11 | # with trailing newline echo -en 'foo bar ' | sed '$s/$//' | head -c -1 # still works without trailing newline echo -en 'foo bar' | sed '$s/$//' | head -c -1 # read from a file sed '$s/$//' myfile.txt | head -c -1 |
细节:
-
无论字符是什么,
head -c -1 都会截断字符串的最后一个字符。因此,如果字符串不以换行符结束,那么您将失去一个字符。 -
因此,为了解决这个问题,我们添加了另一个命令,如果没有一个,则会添加一个尾随换行符:
sed '$s/$//' 。第一个$ 表示仅将命令应用于最后一行。s/$// 意味着将"行尾"替换为"无",这基本上什么都不做。但它有添加尾随换行符的副作用是没有一个。
注意:Mac的默认
我有一个类似的问题,但正在使用Windows文件,需要保留这些CRLF - 我的解决方案在Linux上:
1 2 3 4 | sed 's/ //g' orig | awk '{if (NR>1) printf(" "); printf("%s",$0)}' > tweaked |
我想要这样做的唯一时间是代码高尔夫,然后我只是将我的代码复制出文件并将其粘贴到
1 2 3 4 | sed -n"1 x;1 !H $ {x;s/ *$//p;} " YourFile |
应删除文件中 n的最后一次出现。不处理大文件(由于sed缓冲区限制)
红宝石:
1 | ruby -ne 'print $stdin.eof ? $_.strip : $_' |
要么:
1 | ruby -ane 'q=p;p=$_;puts q if $.>1;END{print p.strip!}' |