如何删除换行符,如果它是在一个文件中的最后一个字符?(How can I delete a newl

2019-09-03 02:25发布

我有一些我想删除最后一个换行符,如果它是在一个文件中的最后一个字符的文件。 od -c我表明我运行命令不写一个尾随新行的文件:

0013600   n   t  >  \n

我试着SED,但我能想到的是不是做的伎俩最好的一些技巧:

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

任何想法如何做到这一点?

Answer 1:

perl -pe 'chomp if eof' filename >filename2

或者,编辑在地方文件:

perl -pi -e 'chomp if eof' filename

[编者注: -pi -e原是-pie ,但是,正如一些评论指出并解释@hvd,后者不能正常工作。]

这被描述为awk的网站,我看到一个“perl的亵渎”。

但是,在测试中,它的工作。



Answer 2:

你可以采取的事实, 外壳利用命令替换删除尾随换行符

简单的表格,在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块,其中,它是在没有尾部印刷\n由于输出记录分隔符(设置OFS )为空字符串。
  • 如果你想有一个详细,但快速和可靠的解决方案, 在原地真正编辑 (而不是创建一个临时的。文件则取代了原来的),考虑jrockway的Perl脚本



Answer 3:

你可以做到这一点head从GNU的coreutils,它支持是相对于文件的末尾参数。 所以,离开了最后一个字节使用:

head -c -1

为了测试结束的换行符您可以使用tailwc 。 下面的示例将结果保存到一个临时文件,随后将覆盖原文:

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

你也可以用spongemoreutils做“就地”编辑:

[[ $(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和支持就地编辑。



Answer 4:

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

编辑2:

下面是一个awk版本(校正)不积累了潜在的巨大阵列:

线= $ 0} END {printf的$ 0}” ABC



Answer 5:

呆子

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


Answer 6:

如果你想要做的是正确的,你需要的是这样的:

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;
}

我们打开阅读和追加的文件; 开口追加意味着我们已经seek编到文件的末尾。 然后,我们获得与文件的末尾数字位置tell 。 我们用这个数字来寻求回一个字符,然后我们读到一个字符。 如果它是一个换行符,我们截断文件到换行符的前一个字符,否则,我们什么也不做。

这将运行在固定的时间和任何输入恒定的空间,并且不需要任何更多的磁盘空间,无论是。



Answer 7:

对于单行的文件一个非常简单的方法,需要从的coreutils GNU回波:

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


Answer 8:

这里是一个不错的,整洁的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("\r\n"):
    trunc(filename, end_pos)
elif line.endswith("\n"):
    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("\n"))


Answer 9:

还有一个Perl的WTDI:

perl -i -p0777we's/\n\z//' filename


Answer 10:

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

另请参见匹配SED任何字符(包括新行) 。



Answer 11:

一个快速的解决方案是使用GNU工具截断:

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

如果文件不具有尾随新生产线的测试将是真实的。

是非常快的,真正到位,需要去除没有新的文件和搜索也从刚刚结束一个字节(尾-C1)读书。



Answer 12:

用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


Answer 13:

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


Answer 14:

假设Unix文件类型,只希望最后的换行符这一工程。

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

它不会在多个换行符工作...

* 作品只有在最后一行是空行。



Answer 15:

然而,另一种答案FTR(和我的最爱!):回声/猫要剥离,并通过反引号捕获输出的东西。 最后的换行符将被剥离。 例如:

# 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


Answer 16:

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.


Answer 17:

我想这样做的唯一时间是代码高尔夫球,然后我刚才复制我的代码出来的文件,并将其粘贴到echo -n 'content'>file声明。



Answer 18:

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


Answer 19:

我有一个类似的问题,但与Windows文件工作,需要保持这些CRLF - 我对Linux的解决方案:

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


Answer 20:

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

应该删除\ n的任何最后一次出现在文件中。 不工作的巨大的文件(由于sed的缓冲区的限制)



Answer 21:

红宝石:

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

要么:

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


Answer 22:

这是一个很好的解决方案,如果你需要它,或者一个文件管道/重定向,而不是读/输出工作。 这适用于单个或多个行。 它的工作原理是否有尾随的换行符与否。

# 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

细节:

  • head -c -1截断字符串的最后一个字符,不管字符是什么。 因此,如果字符串不以换行符终止,那么你会失去一个字符。
  • 因此,要解决这个问题,我们添加另一个命令,如果没有一个将添加一个结尾的新行: sed '$s/$//' 。 第一$手段只适用命令的最后一行。 s/$//手段代替用“一无所有”,这基本上是什么都不做“行尾”。 但它也有添加尾随换行符的副作用是不存在的。

注意:Mac的默认head不支持-c选项。 你可以做brew install coreutils和使用ghead代替。



文章来源: How can I delete a newline if it is the last character in a file?