我想使用删除仅位于文件末尾的所有空行awk
我能够成功找到一种仅使用以下命令删除顶部所有空行的方法:
awk '/^$/ && a!=1 {a=0} !/^$/ {a=1} a==1 {print}' file.txt
然而,我不知道如何扭转它,所以我可以删除底线。我知道我可以只使用上面的命令并将其与管道连接tac
,但我更喜欢仅使用命令的直接方法awk
(如果可能)。
为了澄清,如果一行“视觉上是空的”,即最多包含空格和/或制表符,则该行被视为“空”。
答案1
awk
由于 awk 按顺序读取文件,从第一行到最后一行,无需外部帮助(例如 Tac),它只能在实际到达文件末尾时判断空行块是否位于文件末尾。
您可以做的是保留一个带有空行的变量(即只有换行符,默认的记录分隔符RS
),并在到达非空行时打印这些空行:
awk '/^$/{n=n RS}; /./{printf "%s",n; n=""; print}' file
print n
我不明白为什么和之间有区别printf n
。
print
附加输出记录分隔符(ORS
,默认为换行符)到要打印的表达式。因此,如果你尝试的话,你会得到一个额外的换行符。您也可以使用单个输出语句来编写它,如下所示
awk '/^$/{n=n RS}; /./{printf "%s%s%s",n,$0,RS; n=""}' file
埃德或者前任
要打印输出(就像 Awk 那样),请选择以下任一选项
printf '%s\n' 'a' '' '.' '?.?+1,$d' ',p' 'Q' | ed -s file
printf '%s\n' 'a' '' '.' '?.?+1,$d' '%p' 'q!' | ex -s file
要直接将更改应用到文件,请选择以下任一选项
printf '%s\n' 'a' '' '.' '?.?+1,$d' 'w' 'q' | ed -s file
printf '%s\n' 'a' '' '.' '?.?+1,$d' 'x' | ex -s file
命令替换
shell 在命令替换中去除尾随换行符。
printf '%s\n' "$(cat file)"
请注意,某些 shell 无法处理大文件,并会出现“参数列表太长”的错误。
灵感来自这个答案。
答案2
无论输入来自管道还是文件,这种 1-pass 方法都可以工作,但必须将每个空行块存储在内存中(除非输入中有数十亿个连续的空行,否则这可能不会真正成为问题) ):
awk 'NF{print s $0; s=""; next} {s=s ORS}' file
如果输入是管道,则这种两遍方法将不起作用,但如果输入是问题中所说的文件并且几乎不使用内存,则这种方法将不起作用:
awk 'NR==FNR{if (NF) n=NR; next} FNR>n{exit} 1' file file
上面假设仅包含空格的行被视为“空”。如果这是错误的,则更NF
改为/./
.
答案3
awk 'length == 0 { ++n; next } { for (i = 1; i <= n; ++i) print ""; n = 0 }; 1' file
或按照评论中的建议缩短,
awk 'length == 0 { ++n; next } { while (n) { print ""; --n } }; 1'
这会跟踪计数器中空行的运行n
。
每当看到空行 ( length == 0
) 时,计数器就会递增,但不会输出任何内容。
当看到非空行时,首先在当前行之前输出适当数量的空行。计数器n
也被重置。
这可以避免从文件末尾输出空行。
使用标准sed
:
sed -n -e :again -e N -e '/[^\n]/!b again' -e p file
这引入了一个显式循环,该循环将行添加到缓冲区,直到其中除了换行符之外还有其他内容。此时,缓冲区被输出。如果输入文件在使用 读取时结束N
,则缓冲区中的数据(仅是换行符)将不会输出。
带注释的代码(最初#n
关闭默认输出,就像使用一样-n
):
#n
# Label to branch to later.
:again
# Append next line of input to buffer
# with a delimiting newline.
N
# Branch (jump) to :again if there's
# only newlines in the buffer.
/[^\n]/!b again
# Output buffer.
p
答案4
co=`awk '!/^$/{x=NR}END{print x}' filename`
co=$(($co+1))
j="$co,$"
sed -i "${j}d" filename
测试并运行良好