Answers:
如果您想要一个精确的等效chomp
方法,我想到的第一个方法是LatinSuD已经发布的awk解决方案。我将添加一些其他方法,这些方法不会实现,chomp
但会实现一些chomp
经常用于的常见任务。
当您将一些文本填充到变量中时,末尾的所有换行符都会被删除。因此,所有这些命令都会产生相同的单行输出:
echo "$(printf 'one\ntwo') done"
echo "$(printf 'one\ntwo\n') done"
echo "$(printf 'one\ntwo\n\n') done"
echo "$(printf 'one\ntwo\n\n\n\n\n\n\n\n\n\n') done"
如果要在文件或命令输出的最后一行附加一些文本,sed
可能会很方便。使用GNU sed和大多数其他现代实现,即使输入不以换行符结尾,此方法也可以使用¹;但是,如果还没有换行符,则不会添加换行符。
sed '$ s/$/ done/'
¹ 但是,这不适用于所有sed实现:sed是文本处理工具,不为空并且不以换行符结尾的文件也不是文本文件。
chomp
是LatinSuD已经发布的awk解决方案。但是在许多情况下,chomp
这只是完成工作的工具,我提供了完成一些常见任务的方法。让我更新我的答案以澄清这一点。
另一种perl
方法。这将整个输入读取到内存中,因此对于大量数据(使用cuonglm或该awk
方法)可能不是一个好主意:
$ printf "one\ntwo\n" | perl -0777pe 's/\n$//'; echo " done"
one
two done
我从某个地方的github仓库中抢了这个东西,但是找不到哪里
#!/bin/bash
#
# Delete all trailing blank lines.
# From http://sed.sourceforge.net/sed1line.txt
#
# Version: 1.3.0
# Created: 2011-01-02
# Updated: 2015-01-25
# Contact: Joel Parker Henderson (joel@joelparkerhenderson.com)
# License: GPL
##
set -euf
sed -e :a -e '/^\n*$/{$d;N;ba' -e '}'
打印不带换行符的行,仅在有另一行要打印时才添加换行符。
$ printf 'one\ntwo\n' |
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
如果我们正在处理文件,则可以截断文件中的一个字符(如果文件以换行符结尾):
removeTrailNewline(){[[$(tail -c 1“ $ 1”)]] || 截断-s-1“ $ 1”; }
这是一种快速的解决方案,因为它只需要从文件中读取一个字符,然后直接将其删除(truncate
),而无需读取整个文件。
但是,在使用来自stdin(流)的数据时,必须读取所有数据。并且,读取后立即“消耗”它。没有回溯(与截断一样)。要找到流的末尾,我们需要阅读流的末尾。那时,无法返回输入流,因为数据已经“消耗”了。这意味着数据必须以某种形式的缓冲区存储,直到我们匹配流的末尾,然后对缓冲区中的数据进行处理。
解决方案中最明显的方法是将流转换为文件并处理该文件。但问题是需要某种流过滤器。与使用其他文件无关。
天真的解决方案是将整个输入捕获到一个变量中:
FilterOne(){ filecontents=$(cat; echo "x"); # capture the whole input
filecontents=${filecontents%x}; # Remove the "x" added above.
nl=$'\n'; # use a variable for newline.
printf '%s' "${filecontents%"$nl"}"; # Remove newline (if it exists).
}
printf 'one\ntwo' | FilterOne ; echo 1done
printf 'one\ntwo\n' | FilterOne ; echo 2done
printf 'one\ntwo\n\n' | FilterOne ; echo 3done
可以使用sed将整个文件加载到内存中。在sed中,无法避免在最后一行尾随换行符。GNU sed可能会避免打印尾随换行符,但前提是源文件已经丢失了换行符。因此,不,简单的sed无法帮助。
GNU awk除外,带有以下-z
选项:
sed -z 's/\(.*\)\n$/\1/'
使用awk(任何awk),可以对整个流进行处理,而printf
不必在末尾添加换行符。
awk ' { content = content $0 RS }
END { gsub( "\n$", "", content ); printf( "%s", content ) }
'
将整个文件加载到内存中可能不是一个好主意,因为它可能会消耗大量内存。
在awk中,我们可以通过将前一行存储在变量中并打印出当前行来处理每个循环两行:
awk 'NR>1{print previous} {previous=$0} END {printf("%s",$0)}'
但是我们可以做得更好。
如果我们在没有换行符的情况下打印当前行,并且仅在下一行存在时才打印换行符,则我们一次处理一行,最后一行将没有尾随换行符:
awk'NR == 1 {printf(“%s”,$ 0); next}; {printf(“ \ n%s”,$ 0)}'
或者,以其他方式编写:
awk 'NR>1{ print "" }; { printf( "%s", $0 ) }'
要么:
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'
所以:
$ printf 'one\ntwo\n' | awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
chomp
,因为chomp
最多只能删除一个尾随的换行符。