如果换行符是文件中的最后一个字符,我该如何删除它?
- 2024-10-12 10:28:00
- admin 原创
- 93
问题描述:
我有一些文件,如果最后一个换行符是文件中的最后一个字符,我想删除它。 od -c
显示我运行的命令确实在文件中写入了尾随换行符:
0013600 n t >
我尝试过使用 sed 的一些技巧,但我能想到的最好的方法并没有奏效:
sed -e '$s/(.*)
$//' abc
有什么想法可以做到这一点吗?
解决方案 1:
perl -pe 'chomp if eof' filename >filename2
或者,现场编辑文件:
perl -pi -e 'chomp if eof' filename
[编者注:-pi -e
最初是-pie
,但正如几位评论者所指出的以及@hvd 所解释的,后者不起作用。]
在我看到的 awk 网站上,这被描述为“对 perl 的亵渎”。
但在测试中,它成功了。
解决方案 2:
您可以利用shell命令替换删除尾随换行符这一事实:
在 bash、ksh、zsh 中有效的简单形式:
printf %s "$(< in.txt)" > out.txt
可移植的(符合 POSIX 规范)替代方案(效率略低):
printf %s "$(cat in.txt)" > out.txt
笔记:
如果以多个换行符
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 脚本。
解决方案 3:
您可以使用 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
您还可以使用sponge
frommoreutils
进行“就地”编辑:
[[ $(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
并支持就地编辑。
解决方案 4:
head -n -1 abc > newfile
tail -n 1 abc | tr -d '
' >> newfile
编辑2:
这是一个不会累积可能非常大的数组的awk
版本(已更正) :
awk'{if(line)打印行;line=$0} END {printf $0}'abc
解决方案 5:
呆呆地
awk '{q=p;p=$0}NR>1{print q}END{ORS = ""; print p}' file
解决方案 6:
一个快速的解决方案是使用 gnu 实用程序truncate
:
[ -z $(tail -c1 file) ] && truncate -s-1 file
如果文件确实有一个尾随新行,则测试为真。
删除速度非常快,真正到位,不需要新文件,搜索也是从末尾读取一个字节(tail -c1
)。
解决方案 7:
对于单行文件来说,这是一个非常简单的方法,需要 coreutils 中的 GNU echo:
/bin/echo -n $(cat $file)
解决方案 8:
如果你想做对,你需要这样的东西:
use 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 "
"){
truncate $fh, $pos - 1;
}
我们打开文件进行读取和追加;打开文件进行追加意味着我们已经seek
到达文件末尾。然后我们使用 获取文件末尾的数字位置tell
。我们使用该数字向后搜索一个字符,然后读取该字符。如果是换行符,我们将文件截断到该换行符之前的字符,否则,我们什么也不做。
对于任何输入,这都会在恒定时间和恒定空间中运行,并且不需要任何更多磁盘空间。
解决方案 9:
这是一个简洁的 Python 解决方案。我并没有试图在这里简洁明了。
这将就地修改文件,而不是复制文件并从副本的最后一行中删除换行符。如果文件很大,这将比被选为最佳答案的 Perl 解决方案快得多。
如果最后两个字节是 CR/LF,则它会截断文件两个字节;如果最后一个字节是 LF,则它会截断一个字节。如果最后字节不是 (CR)LF,则它不会尝试修改文件。它会处理错误。在 Python 2.6 中测试。
将其放入名为“striplast”的文件中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("
"):
trunc(filename, end_pos)
elif line.endswith("
"):
trunc(filename, end_pos + 1)
PS 本着“Perl 高尔夫”的精神,这是我最短的 Python 解决方案。它将整个文件从标准输入读取到内存中,删除末尾的所有换行符,并将结果写入标准输出。不像 Perl 那样简洁;对于这种小而复杂的快速操作,Perl 是无可匹敌的。
从调用中删除“ n” .rstrip()
,它将删除文件末尾的所有空白,包括多个空白行。
将其放入“slurp_and_chomp.py”然后运行python slurp_and_chomp.py < inputfile > outputfile
。
import sys
sys.stdout.write(sys.stdin.read().rstrip("
"))
解决方案 10:
另一个 perl WTDI:
perl -i -p0777we's/
z//' filename
解决方案 11:
$ perl -e 'local $/; $_ = <>; s/
$//; print' a-text-file.txt
另请参阅匹配 sed 中的任何字符(包括换行符)。
解决方案 12:
perl -pi -e 's/
$// if(eof)' your_file
解决方案 13:
使用 dd:
file='/path/to/file'
[[ "$(tail -c 1 "${file}" | tr -dc '
' | wc -c)" -eq 1 ]] && \ n printf "" | dd of="${file}" seek=$(($(stat -f "%z" "${file}") - 1)) bs=1 count=1
#printf "" | dd of="${file}" seek=$(($(wc -c < "${file}") - 1)) bs=1 count=1
解决方案 14:
假设 Unix 文件类型并且您只想要最后一个换行符,那么这是可行的。
sed -e '${/^$/d}'
它不适用于多个换行符......
仅当最后一行是空行时才有效。*
解决方案 15:
如果您需要它与管道/重定向配合使用,而不是从文件读取/输出,那么这是一个很好的解决方案。它适用于单行或多行。无论是否有尾随换行符,它都可以工作。
# 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 默认head
不支持该-c
选项,你可以brew install coreutils
使用ghead
。
解决方案 16:
还有另一个答案 FTR(也是我最喜欢的!):echo/cat 您想要删除的内容并通过反引号捕获输出。最后的换行符将被删除。例如:
# Sadly, outputs newline, and we have to feed the newline to sed to be portable
echo thingy | sed -e 's/thing/sill/'
# No newline! Happy.
out=`echo thingy | sed -e 's/thing/sill/'`
printf %s "$out"
# Similarly for files:
file=`cat file_ending_in_newline`
printf %s "$file" > file_no_newline
解决方案 17:
红宝石:
ruby -ne 'print $stdin.eof ? $_.strip : $_'
或者:
ruby -ane 'q=p;p=$_;puts q if $.>1;END{print p.strip!}'
解决方案 18:
POSIX SED:
'${/^$/d}'
$ - 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.
解决方案 19:
我唯一一次想这样做是为了玩代码高尔夫,然后我只是将我的代码从文件中复制出来并将其粘贴到声明中echo -n 'content'>file
。
解决方案 20:
sed ':a;/^
*$/{$d;N;};/
$/ba' file
解决方案 21:
我遇到了类似的问题,但正在处理 Windows 文件并且需要保留那些 CRLF——我在 Linux 上的解决方案:
sed 's/
//g' orig | awk '{if (NR>1) printf("
"); printf("%s",$0)}' > tweaked
解决方案 22:
sed -n "1 x;1 !H
$ {x;s/
*$//p;}
" YourFile
应删除文件中最后一次出现的 n。不适用于大型文件(由于 sed 缓冲区限制)
解决方案 23:
这是一个使用 sed 的简单解决方案。您的 sed 版本需要支持该-z
选项。
-z, --null-data
separate lines by NUL characters
它既可以在管道中使用,也可以使用选项来编辑-i
文件
sed -ze 's/
$//' file
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件