我有一些文件,如果文件的最后一个字符是换行符,我希望将其删除。通过运行od -c
命令,我可以看到该命令确实会在文件的末尾添加一个换行符:
0013600 n t > \n
我尝试了几种用sed的技巧,但我能想到的最好的方法并没有起作用:
sed -e '$s/\(.*\)\n$/\1/' abc
有什么办法可以做到这一点吗?
我有一些文件,如果文件的最后一个字符是换行符,我希望将其删除。通过运行od -c
命令,我可以看到该命令确实会在文件的末尾添加一个换行符:
0013600 n t > \n
我尝试了几种用sed的技巧,但我能想到的最好的方法并没有起作用:
sed -e '$s/\(.*\)\n$/\1/' abc
perl -pe 'chomp if eof' filename >filename2
或者,要原地编辑文件:
perl -pi -e 'chomp if eof' filename
[编辑注:原本是 -pie
,但是多位评论者指出这个命令不可行,在 @hvd 的解释下得知此处应为 -pi -e
。]
我在 awk 网站上看到过这被称为 'perl 渎神'。
但是,在测试中它起作用了。
chomp
可以使其更安全。而且这比把文件 slurping 要好。 - Sinan Ünürperl -pi -e 'chomp if eof' 文件名
来原地编辑文件,而无需创建临时文件。 - Romuald Brunetperl -pie 'chomp if eof' filename
翻译为:无法打开 perl 脚本 "chomp if eof":没有这个文件或目录;
perl -pi -e 'chomp if eof' filename
翻译为:有效。 - aditsu quit because SE is EVILprintf %s "$(< in.txt)" > out.txt
便携式(符合POSIX标准)替代方案(效率略低):
printf %s "$(cat in.txt)" > out.txt
in.txt
以多个换行符结尾,则命令替换将删除所有这些换行符。感谢,Sparhawk(它不会删除除尾随换行符之外的空格字符。)printf %s
确保输出不附加任何换行符(它是非标准echo -n
的POSIX兼容替代方法;请参见http://pubs.opengroup.org/onlinepubs/009696799/utilities/echo.html和https://unix.stackexchange.com/a/65819)其他答案指南:
如果有可用的Perl,那么选择accepted answer - 它是简单且内存高效的(不会一次性读取整个输入文件)。否则,请考虑ghostdog74的Awk答案 - 它很难懂,但也是内存高效的;一个更易读的等价物(符合POSIX标准)是:awk 'NR>1 {print prev} {prev=$0} END {ORS=""; print}' in.txt。打印被延迟一行,以便可以在END
块中处理最后一行,在该块中由于将输出记录分隔符(OFS
)设置为空字符串而不带有尾随的\n
进行打印。如果您想要一个详细的、快速和健壮的解决方案,真正地进行原地编辑(而不是创建一个临时文件,然后替换原始文件),请考虑jrockway的Perl脚本。您可以使用GNU coreutils中的head
命令来实现此操作,它支持相对于文件末尾的参数。因此,要去掉最后一个字节,请使用:
head -c -1
要测试是否存在结束换行符,可以使用tail
和wc
。以下示例将结果保存到临时文件中,然后覆盖原始文件:
if [[ $(tail -c1 file | wc -l) == 1 ]]; then
head -c -1 file > file.tmp
mv file.tmp file
fi
你也可以使用moreutils
中的来进行“原地”编辑:[[ $(tail -c1 file | wc -l) == 1 ]] && head -c -1 file | sponge file
你还可以将这个代码插入到你的.bashrc
文件中,创建一个通用的可重复使用函数:
# 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的答案中使用的那样,truncate --size=-1
可以替换head -c-1
并支持原地编辑。
truncate --size=-1
而不是 head -c -1
,因为它只调整输入文件的大小而不是读取输入文件,将其写入另一个文件,然后用输出文件替换原始文件。 - Karl Wilburhead -c -1
会删除最后一个字符,无论它是否为换行符,因此您需要在删除之前检查最后一个字符是否为换行符。 - wisbuckyhead -n -1 abc > newfile
tail -n 1 abc | tr -d '\n' >> newfile
编辑2:
这里是一个已经纠正过的awk
版本,它不会累积可能很大的数组:
awk '{if (line) print line; line=$0} END {printf $0}' abc
head -n -1 abc | cat <(tail -n 1 abc | tr -d '\n') | ...
- BCoatesgawk
awk '{q=p;p=$0}NR>1{print q}END{ORS = ""; print p}' file
awk '{ prev_line = line; line = $0; } NR > 1 { print prev_line; } END { ORS = ""; print line; }' file
这样应该更容易阅读。 - Yevhen Pavliukawk 'NR>1 {print p} {p=$0} END {printf $0}' file
。 - user8017719printf
的第一个参数是“格式”参数。因此,如果输入文件中有类似于 %d
的格式说明符,就会出现错误。修复方法是将其更改为 printf "%s" $0
。 - Robin A. Meade一种快速的解决方案是使用gnu实用程序truncate
:
[ -z $(tail -c1 file) ] && truncate -s-1 file
如果文件末尾存在换行符,则测试结果为真。
删除非常快速,真正地就地进行,无需新文件,并且搜索也只读取一个字节的末尾(tail -c1
)。
[ -z $(tail -c1 filename) ] && truncate -s -1 filename
(另外,针对其他评论,truncate
命令不能使用 stdin,需要提供文件名)。 - michael针对单行文件的一种非常简单的方法,需要使用核心工具包中的GNU echo:
/bin/echo -n $(cat $file)
\n
存在时,这会出现问题。因为它会被转换成一个新行。 - Chris Stryczynski$(...)
。 - Thor/bin/echo -n "$(cat infile)"
另外,我不确定 echo
或 shell 在不同的操作系统、shell 版本或发行版中的最大长度是多少(我只是在谷歌上搜索了一下,结果陷入了一个兔子洞),所以我不确定它除了小文件之外是否具有可移植性(或性能)--但对于小文件来说,非常好。 - michaeluse autodie qw(open sysseek sysread truncate);
my $file = shift;
open my $fh, '+>>', $file;
my $pos = tell $fh;
sysseek $fh, $pos - 1, 0;
sysread $fh, my $buf, 1 or die 'No data to read?';
if($buf eq "\n"){
truncate $fh, $pos - 1;
}
seek
移动到了文件的末尾。然后,我们使用tell
获取文件末尾的数字位置。我们使用该数字向后移动一个字符,然后读取该字符。如果它是换行符,则将文件截断为该换行符之前的字符,否则不执行任何操作。 chmod + x striplast
赋予其执行权限。#!/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("\r\n"):
trunc(filename, end_pos)
elif line.endswith("\n"):
trunc(filename, end_pos + 1)
import sys
sys.stdout.write(sys.stdin.read().rstrip("\n"))
又一个关于Perl的WTDI:
perl -i -p0777we's/\n\z//' filename