简体   繁体   English

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

[英]How can I delete a newline if it is the last character in a file?

I have some files that I'd like to delete the last newline if it is the last character in a file.我有一些文件,如果它是文件中的最后一个字符,我想删除最后一个换行符。 od -c shows me that the command I run does write the file with a trailing new line: od -c向我展示了我运行的命令确实在文件中写入了一个尾随的新行:

0013600   n   t  >  \n

I've tried a few tricks with sed but the best I could think of isn't doing the trick:我已经用 sed 尝试了一些技巧,但我能想到的最好的方法是没有做到这一点:

sed -e '$s/\(.*\)\n$/\1/' abc

Any ideas how to do this?任何想法如何做到这一点?

perl -pe 'chomp if eof' filename >filename2

or, to edit the file in place:或者,就地编辑文件:

perl -pi -e 'chomp if eof' filename

[Editor's note: -pi -e was originally -pie , but, as noted by several commenters and explained by @hvd, the latter doesn't work.] [编者注: -pi -e最初是-pie ,但是,正如一些评论者所指出的和@hvd 所解释的,后者不起作用。]

This was described as a 'perl blasphemy' on the awk website I saw.在我看到的 awk 网站上,这被描述为“perl 亵渎”。

But, in a test, it worked.但是,在测试中,它奏效了。

You can take advantage of the fact that shell command substitutions remove trailing newline characters :您可以利用shell 命令替换删除尾随换行符这一事实:

Simple form that works in bash, ksh, zsh:适用于 bash、ksh、zsh 的简单形式:

printf %s "$(< in.txt)" > out.txt

Portable (POSIX-compliant) alternative (slightly less efficient):便携式(符合 POSIX 标准)替代方案(效率稍低):

printf %s "$(cat in.txt)" > out.txt

Note:笔记:


A guide to the other answers :其他答案的指南

  • If Perl is available, go for the accepted answer - it is simple and memory-efficient (doesn't read the whole input file at once).如果Perl可用,请寻找公认的答案- 它简单且节省内存(不会一次读取整个输入文件)。

  • Otherwise, consider ghostdog74's Awk answer - it's obscure, but also memory-efficient ;否则,请考虑ghostdog74 的Awk答案- 它晦涩难懂,但也节省了内存 a more readable equivalent (POSIX-compliant) is:一个更易读的等价物(符合 POSIX 标准)是:

  • awk 'NR > 1 { print prev } { prev=$0 } END { ORS=""; print }' in.txt

  • Printing is delayed by one line so that the final line can be handled in the END block, where it is printed without a trailing \\n due to setting the output-record separator ( OFS ) to an empty string.打印延迟一行,以便可以在END块中处理最后一行,由于将输出记录分隔符 ( OFS ) 设置为空字符串,因此打印时没有尾随\\n

  • If you want a verbose, but fast and robust solution that truly edits in-place (as opposed to creating a temp. file that then replaces the original), consider jrockway's Perl script .如果您想要一个真正就地编辑冗长但快速且强大的解决方案(而不是创建一个临时文件然后替换原始文件),请考虑jrockway 的Perl script

You can do this with head from GNU coreutils, it supports arguments that are relative to the end of the file.您可以使用来自 GNU coreutils 的head来执行此操作,它支持与文件末尾相关的参数。 So to leave off the last byte use:所以不要使用最后一个字节:

head -c -1

To test for an ending newline you can use tail and wc .要测试结束换行符,您可以使用tailwc The following example saves the result to a temporary file and subsequently overwrites the original:以下示例将结果保存到临时文件并随后覆盖原始文件:

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

You could also use sponge from moreutils to do "in-place" editing:您还可以使用来自moreutils sponge进行“就地”编辑:

[[ $(tail -c1 file | wc -l) == 1 ]] && head -c -1 file | sponge file

You can also make a general reusable function by stuffing this in your .bashrc 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
}

Update更新

As noted by KarlWilbur in the comments and used in Sorentar's answer , truncate --size=-1 can replace head -c-1 and supports in-place editing.正如KarlWilbur在评论中指出并在Sorentar 的回答中使用的那样truncate --size=-1可以替换head -c-1并支持就地编辑。

head -n -1 abc > newfile
tail -n 1 abc | tr -d '\n' >> newfile

Edit 2:编辑2:

Here is an awk version (corrected) that doesn't accumulate a potentially huge array:这是一个awk版本(已更正) ,它不会累积潜在的巨大数组:

awk '{if (line) print line; awk '{if (line) 打印行; line=$0} END {printf $0}' abc line=$0} END {printf $0}' abc

呆呆的

   awk '{q=p;p=$0}NR>1{print q}END{ORS = ""; print p}' file

A fast solution is using the gnu utility truncate :一个快速的解决方案是使用 gnu 实用程序truncate

[ -z $(tail -c1 file) ] && truncate -s-1 file

The test will be true if the file does have a trailing new line.如果文件有尾随换行符,则测试为真。

The removal is very fast, truly in place, no new file is needed and the search is also reading from the end just one byte ( tail -c1 ).删除非常快,真正到位,不需要新文件,搜索也从末尾读取一个字节( tail -c1 )。

单行文件的一种非常简单的方法,需要来自 coreutils 的 GNU echo:

/bin/echo -n $(cat $file)

If you want to do it right, you need something like this:如果你想把它做对,你需要这样的东西:

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 "\n"){
    truncate $fh, $pos - 1;
}

We open the file for reading and appending;我们打开文件进行读取和追加; opening for appending means that we are already seek ed to the end of the file. open for append 意味着我们已经seek到文件的末尾。 We then get the numerical position of the end of the file with tell .然后我们使用tell获取文件末尾的数字位置。 We use that number to seek back one character, and then we read that one character.我们使用这个数字来寻找一个字符,然后我们读取那个字符。 If it's a newline, we truncate the file to the character before that newline, otherwise, we do nothing.如果是换行符,我们将文件截断为该换行符之前的字符,否则,我们什么都不做。

This runs in constant time and constant space for any input, and doesn't require any more disk space, either.这对于任何输入都以恒定的时间和恒定的空间运行,并且也不需要更多的磁盘空间。

Here is a nice, tidy Python solution.这是一个不错的、整洁的 Python 解决方案。 I made no attempt to be terse here.我没有试图在这里简洁。

This modifies the file in-place, rather than making a copy of the file and stripping the newline from the last line of the copy.这会就地修改文件,而不是制作文件的副本并从副本的最后一行剥离换行符。 If the file is large, this will be much faster than the Perl solution that was chosen as the best answer.如果文件很大,这将比被选为最佳答案的 Perl 解决方案快得多。

It truncates a file by two bytes if the last two bytes are CR/LF, or by one byte if the last byte is LF.如果最后两个字节是 CR/LF,则将文件截断两个字节,如果最后一个字节是 LF,则截断一个字节。 It does not attempt to modify the file if the last byte(s) are not (CR)LF.如果最后一个字节不是 (CR)LF,它不会尝试修改文件。 It handles errors.它处理错误。 Tested in Python 2.6.在 Python 2.6 中测试。

Put this in a file called "striplast" and chmod +x striplast .把它放在一个名为“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("\r\n"):
    trunc(filename, end_pos)
elif line.endswith("\n"):
    trunc(filename, end_pos + 1)

PS In the spirit of "Perl golf", here's my shortest Python solution. PS 本着“Perl 高尔夫”的精神,这是我最短的 Python 解决方案。 It slurps the whole file from standard input into memory, strips all newlines off the end, and writes the result to standard output.它将整个文件从标准输入拖入内存,去掉末尾的所有换行符,并将结果写入标准输出。 Not as terse as the Perl;不像 Perl 那样简洁; you just can't beat Perl for little tricky fast stuff like this.你就是无法在像这样一些棘手的快速东西上击败 Perl。

Remove the "\\n" from the call to .rstrip() and it will strip all white space from the end of the file, including multiple blank lines.从对.rstrip()的调用中删除“\\n”, .rstrip()文件末尾的所有空白,包括多个空行。

Put this into "slurp_and_chomp.py" and then run python slurp_and_chomp.py < inputfile > outputfile .将其放入“slurp_and_chomp.py”,然后运行python slurp_and_chomp.py < inputfile > outputfile

import sys

sys.stdout.write(sys.stdin.read().rstrip("\n"))

另一个 perl WTDI:

perl -i -p0777we's/\n\z//' filename
$  perl -e 'local $/; $_ = <>; s/\n$//; print' a-text-file.txt

See also Match any character (including newlines) in sed .另请参阅匹配 sed 中的任何字符(包括换行符)

perl -pi -e 's/\n$// if(eof)' your_file

Using dd:使用 dd:

file='/path/to/file'
[[ "$(tail -c 1 "${file}" | tr -dc '\n' | wc -c)" -eq 1 ]] && \
    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

Assuming Unix file type and you only want the last newline this works.假设 Unix 文件类型并且您只需要最后一个换行符。

sed -e '${/^$/d}'

It will not work on multiple newlines...它不适用于多个换行符...

* Works only if the last line is a blank line. *仅当最后一行是空行时才有效。

This is a good solution if you need it to work with pipes/redirection instead of reading/output from or to a file.如果您需要使用管道/重定向而不是从文件读取/输出文件,这是一个很好的解决方案。 This works with single or multiple lines.这适用于单行或多行。 It works whether there is a trailing newline or not.无论是否有尾随换行符,它都有效。

# with trailing newline
echo -en 'foo\nbar\n' | sed '$s/$//' | head -c -1

# still works without trailing newline
echo -en 'foo\nbar' | sed '$s/$//' | head -c -1

# read from a file
sed '$s/$//' myfile.txt | head -c -1

Details:细节:

  • head -c -1 truncates the last character of the string, regardless of what the character is. head -c -1截断字符串的最后一个字符,无论该字符是什么。 So if the string does not end with a newline, then you would be losing a character.因此,如果字符串不以换行符结尾,那么您将丢失一个字符。
  • So to address that problem, we add another command that will add a trailing newline if there isn't one: sed '$s/$//' .所以为了解决这个问题,我们添加了另一个命令,如果没有换行符,它将添加一个尾随换行符: sed '$s/$//' The first $ means only apply the command to the last line.第一个$表示只将命令应用于最后一行。 s/$// means substitute the "end of the line" with "nothing", which is basically doing nothing. s/$//表示将“行尾”替换为“nothing”,这基本上是什么都不做。 But it has a side effect of adding a trailing newline is there isn't one.但是它具有添加尾随换行符的副作用,因为它没有。

Note: Mac's default head does not support the -c option.注意:Mac 的默认head不支持-c选项。 You can do brew install coreutils and use ghead instead.您可以执行brew install coreutils并使用ghead代替。

Yet another answer FTR (and my favourite!): echo/cat the thing you want to strip and capture the output through backticks.另一个答案 FTR(也是我最喜欢的!): echo/cat 你想要剥离的东西,并通过反引号捕获输出。 The final newline will be stripped.最后的换行符将被删除。 For example:例如:

# 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

ruby:红宝石:

ruby -ne 'print $stdin.eof ? $_.strip : $_'

or:要么:

ruby -ane 'q=p;p=$_;puts q if $.>1;END{print p.strip!}'

POSIX SED: POSIX SED:

'${/^$/d}' '${/^$/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.

我有一个类似的问题,但是正在处理一个 Windows 文件并且需要保留那些 CRLF——我在 linux 上的解决方案:

sed 's/\r//g' orig | awk '{if (NR>1) printf("\r\n"); printf("%s",$0)}' > tweaked

我唯一想要这样做的是代码高尔夫,然后我只是将我的代码从文件中复制出来并将其粘贴到echo -n 'content'>file语句中。

sed -n "1 x;1 !H
$ {x;s/\n*$//p;}
" YourFile

Should remove any last occurence of \\n in file.应该删除文件中最后一次出现的 \\n。 Not working on huge file (due to sed buffer limitation)无法处理大文件(由于 sed 缓冲区限制)

sed ':a;/^\n*$/{$d;N;};/\n$/ba' file

Here's a simple solution that uses sed.这是一个使用 sed 的简单解决方案。 Your versions of sed needs to support the -z option.您的 sed 版本需要支持-z选项。

       -z, --null-data

              separate lines by NUL characters

It can either be used in a pipe or used to edit the file in place with the -i option它可以用于 pipe 或用于使用-i选项编辑文件

sed -ze 's/\n$//' file

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM