如果换行符是文件中的最后一个字符,我该如何删除它?

2024-10-12 10:28:00
admin
原创
93
摘要:问题描述:我有一些文件,如果最后一个换行符是文件中的最后一个字符,我想删除它。 od -c显示我运行的命令确实在文件中写入了尾随换行符:0013600 n t > 我尝试过使用 sed 的一些技巧,但我能想到的最好的方法并没有奏效:sed -e '$s/(.*) $/...

问题描述:

我有一些文件,如果最后一个换行符是文件中的最后一个字符,我想删除它。 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

笔记:


其他答案的指南:

  • 如果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

要测试结尾换行符,可以使用tailwc。以下示例将结果保存到临时文件,然后覆盖原始文件:

if [[ $(tail -c1 file | wc -l) == 1 ]]; then
  head -c -1 file > file.tmp
  mv file.tmp file
fi

您还可以使用spongefrommoreutils进行“就地”编辑:

[[ $(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
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用