如何使用awk删除文件末尾的空行?

Cod*_*oob 2 awk text-processing

我想使用以下命令删除仅位于文件末尾的所有空行 awk

我只能使用以下命令成功找到删除顶部所有空行的方法:

awk '/^$/ && a!=1 {a=0} !/^$/ {a=1} a==1 {print}' file.txt
Run Code Online (Sandbox Code Playgroud)

但是,我不知道如何反转它,因此我可以删除底线。我知道我可以只使用上面的命令并用 管道它tac,但我更喜欢awk只使用命令的直接方法(如果可能的话)。

澄清一下,如果一行“视觉上是空的”,即最多包含空格和/或制表符,则该行被认为是“空的”。

Qua*_*odo 6

awk

由于 awk 从第一行到最后一行按顺序读取文件,没有外部帮助(例如 Tac),它只能在实际到达文件末尾时确定文件末尾是否有空行块。

您可以做的是保留一个带有空行的变量(即,只有换行符,默认记录分隔符RS)并在遇到非空行时打印这些空行:

awk '/^$/{n=n RS}; /./{printf "%s",n; n=""; print}' file
Run Code Online (Sandbox Code Playgroud)

我不明白为什么print n和之间有区别printf n

print将输出记录分隔符ORS,默认为换行符)附加到要打印的表达式。因此,如果您尝试它,您会得到一个额外的换行符。您也可以使用单个输出语句编写它,如

awk '/^$/{n=n RS}; /./{printf "%s%s%s",n,$0,RS; n=""}' file
Run Code Online (Sandbox Code Playgroud)

埃德前任

要打印输出(就像 Awk 所做的那样),请选择以下任一

printf '%s\n' 'a' '' '.' '?.?+1,$d' ',p' 'Q'  | ed -s file
printf '%s\n' 'a' '' '.' '?.?+1,$d' '%p' 'q!' | ex -s file
Run Code Online (Sandbox Code Playgroud)

要将更改直接应用于文件,请选择以下任一

printf '%s\n' 'a' '' '.' '?.?+1,$d' 'w' 'q'   | ed -s file
printf '%s\n' 'a' '' '.' '?.?+1,$d' 'x'       | ex -s file
Run Code Online (Sandbox Code Playgroud)

要了解发生了什么。

命令替换

Shell 在命令替换中去除尾随的换行符。

printf '%s\n' "$(cat file)"
Run Code Online (Sandbox Code Playgroud)

请注意,某些 shell 无法处理大文件,并且会出现“参数列表太长”的错误。

受到这个答案的启发。

  • 请注意,在一般情况下 `printf var` 是错误的,因为 `printf` 的第一个参数是格式。所以最好使用`printf "%s", var`。在这里使用 `printf n` 应该是无害的,因为变量只包含换行符,所以应该保证不包含 `%` 字符(注意,对于 `printf` 实用程序,除了 `%` 之外,反斜杠字符也是一个问题)。 (2认同)
  • “绝妙的技巧”可能对除非常小的文件外的所有文件都效率低下,而对于大文件则完全失败。(确切的限制以及超出它的文件的行为将取决于您使用的外壳。) (2认同)
  • @D.BenKnoble 如果您可以保证文件有尾随空行,则只能跳过附加空行。这与我链接的答案中包含的方法类似。感谢您收看‘,p’,那是专门给 Ed 的。 (2认同)

Kus*_*nda 6

awk 'length == 0 { ++n; next } { for (i = 1; i <= n; ++i) print ""; n = 0 }; 1' file
Run Code Online (Sandbox Code Playgroud)

或缩短,如评论中所建议,

awk 'length == 0 { ++n; next } { while (n) { print ""; --n } }; 1'
Run Code Online (Sandbox Code Playgroud)

这会跟踪计数器中空行的运行情况n

每当看到空行 ( length == 0) 时,计数器都会增加但不输出任何内容。

当看到非空行时,在当前行之前先输出适当数量的空行。计数器n也被复位。

这避免了从文件末尾输出空行。


使用标准sed

sed -n -e :again -e N -e '/[^\n]/!b again' -e p file
Run Code Online (Sandbox Code Playgroud)

这引入了一个显式循环,该循环向缓冲区添加行,直到其中包含除换行符以外的其他内容。此时,缓冲区被输出。如果输入文件在读取时结束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
Run Code Online (Sandbox Code Playgroud)

  • 不要忘记 [^\n] 是不可移植的,GNU 特定的正则表达式。 (2认同)

Ed *_*ton 6

无论输入是来自管道还是文件,这种 1-pass 方法都可以工作,但必须将每个空行块存储在内存中(除非您的输入中有数十亿个连续的空行,否则这可能不是真正的问题):

awk 'NF{print s $0; s=""; next} {s=s ORS}' file
Run Code Online (Sandbox Code Playgroud)

如果输入是管道,则这种 2-pass 方法将不起作用,但如果输入是您在问题中所说的文件并且几乎不使用内存,则这种方法会起作用:

awk 'NR==FNR{if (NF) n=NR; next} FNR>n{exit} 1' file file
Run Code Online (Sandbox Code Playgroud)

以上假设仅包含空格的行被视为“空”。如果那是错误的,则更NF改为/./.