`awk 'NF {p=1} p'` 如何从文件的开头和结尾删除空行?

use*_*032 5 awk text-processing

寻找一种从文件的开头和结尾(使用tac)删除空行的方法,我偶然发现了这个:

awk 'NF {p=1} p'
Run Code Online (Sandbox Code Playgroud)

这如何/为什么有效?

我明白NF只有true当有任何字段时(如果该行不是空行)。

Adm*_*Bee 14

这将从文件开头删除空行,但不会从文件末尾删除。[注意:在编辑提到的问题之前,这个答案是明智的tac]

它的工作原理如下:

  • NF是在当前行上找到的字段数。如果为零,则表示该行为空或空白,即最多包含空格(假设字段分隔符保留其默认值,其中任意数量的连续空格被视为分隔符)。
  • 如果规则块 ( { ... })之外(且不与之关联)的任何条件计算结果为 ,则打印当前行true。该标志p最初未初始化并将评估为false,因此事先不会打印任何内容。
  • 一旦找到非空行(NF非零且计算结果为true),{p=1}则输入规则块并将标志p设置为1。之后,p规则块的外部评估为true,并打印任何后续行(包括当前的第一个非空白行)。

请注意,由于标志p永远不会重置,因此在第一个非空行之后出现的任何空行都将在不进行过滤的情况下打印。如果您也想从末尾删除空行,则需要两遍的方法:

awk 'FNR==NR{if (NF) {if (!first) first=FNR; last=FNR} next}
     FNR>=first && FNR<=last' input.txt input.txt
Run Code Online (Sandbox Code Playgroud)

这将处理文件两次(因此它被指定为操作数两次)

  • 在第一遍中FNR,每个文件的行计数器等于NR全局行计数器,我们确定第一个和最后一个非空行。
  • 在第二遍(FNR现在小于NR)中,我们只打印(包括)如此识别的第一条和最后一条非空白行之间的行。

注意

正如Stéphane Chazelas回答所述,两遍方法仅适用于常规文件。如果您的输入具有不同的性质,请参阅此处建议的方法以获取解决方案。

  • 实际上,它与 `awk NF,0` 或 `sed '/[^[:blank:]]/,$!d'` 相同 (3认同)

gle*_*man 5

使用此技术从文件的头部和尾部删除空行:

awk 'NF {p=1} p' file | # remove blank lines at the file head
  tac |                 # reverse the lines
  awk 'NF {p=1} p' |    # remove blanks from the "new head"
  tac |                 # re-reverse the file
  sponge file           # from the `moreutils` package, to overwrite the file
Run Code Online (Sandbox Code Playgroud)


Sté*_*las 5

例如,您的代码的作用以及为什么它只删除输入开头的空白行已经在@AdminBee的答案中进行了解释,但为了完整起见,我将建议一种替代方法来删除前导和尾随空白行,而无需对文件进行两次传递(这只适用于常规文件,不适用于任意输入)。

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

我们将空白行的打印延迟到我们随后看到的下一个非空白行(前提是我们之前已经看到过至少一个非空白行)。


Ed *_*ton 5

使用用于多字符 RS 的 GNU awk 和\s用于[[:space:]]

awk -v RS='^\\s*\n|\n\\s*$' '$0!=""'
Run Code Online (Sandbox Code Playgroud)

例如:

$ cat file


foo
bar


Run Code Online (Sandbox Code Playgroud)

$ awk -v RS='^\\s*\n|\n\\s*$' '$0!=""' file
foo
bar
Run Code Online (Sandbox Code Playgroud)

以上将记录分隔符设置为输入开头的空白行(包括仅包含空格字符的行)或输入结尾的空白行,并仅打印其间的内容。这$0!=""是必要的,因为如果文件开头有空行,那么它将匹配^\s*\n,因此 awk 将理解在 RS 出现之前有一个空记录。

wrtawk 'NF {p=1} p'How / why does this work?你的问题 - 需要明确的是,它不起作用。它只会从文件的开头删除空行,而不是从文件的末尾删除。它在第一次找到非空行时设置一个标志(命名p为 forprint而不是fforfound或类似的),并且仅在p为真时才打印,从而在第一个非空行之前不打印任何内容。