awk命令:如果一行不是以字符开头,则删除该行之前的换行符。

7

尝试使用awk命令实现以下规则:如果一行不以 "O|"、"A|" 或 "S|" 开头,则将该行之前的换行符移除。

我有一个输入文件(input.txt)。

O|field1|field2
O|field1|
field2
A|field1|
field2
S|field1|
field2
O|field1|field2
O|field1|field2
O|field1|
field2
A|field1|
field2
S|field1|
field2
O|field1|field2

我希望得到这个输出

O|field1|field2
O|field1|field2
A|field1|field2
S|field1|field2
O|field1|field2
O|field1|field2
O|field1|field2
A|field1|field2
S|field1|field2
O|field1|field2

执行此代码:

awk '/^O\|/ || /^A\|/ || /^S\|/ {printf "%s", $0; next} 1 {print}' input.txt > output.txt

它会返回:

O|field1|field2O|field1|field2
A|field1|field2
S|field1|field2
O|field1|field2O|field1|field2O|field1|field2
A|field1|field2
S|field1|field2
O|field1|field2

有人可以帮我吗?

8个回答

4
这个 awk 应该适合你的需求。
awk -F'|' 'NF==3 && $3 == "" {p = $0; next}
      {print (NF == 1 ? p $1 : $0); p = ""}' file

O|field1|field2
O|field1|field2
A|field1|field2
S|field1|field2
O|field1|field2
O|field1|field2
O|field1|field2
A|field1|field2
S|field1|field2
O|field1|field2

4

根据您展示的样本,请尝试以下awk代码。

awk '
BEGIN{FS=OFS="|"}
!/\|/ {
  print val,$0
  val=""
  next
}
$0~/\|$/ && NF==3{
  val=$0
  next
}
1
' Input_file

3

类似这样的测试记录布局的方法,可能比测试字段值更适合您:

$ awk -v RS='([^|]*[|]){2}[^|]*\n' '{$0=RT; gsub(/\n/,""); print}' file
O|field1|field2
O|field1|field2
A|field1|field2
S|field1|field2
O|field1|field2
O|field1|field2
O|field1|field2
A|field1|field2
S|field1|field2
O|field1|field2

上述使用GNU awk进行多字符RS的定义,将记录定义为由用|分隔的3个字段,并以换行符结尾,然后删除每个记录中的任何换行符再打印出来。


3

你似乎只有最后一个字段缺失的问题。

如果|是字段分隔符,您可以检查第三个字段是否不为空,并打印整行。

如果字段1不是AOS,则打印前一行加上当前行。

awk -F'|' '{
  if($1 !~ /^[AOS]$/) { print p $0; next }
  if ($3!="") print $0
  p = $0
}' file

输出

O|field1|field2
O|field1|field2
A|field1|field2
S|field1|field2
O|field1|field2
O|field1|field2
O|field1|field2
A|field1|field2
S|field1|field2
O|field1|field2

3

另一种解决方案

awk -v RS="" '{gsub("\\|\n","|")}1' file

O|field1|field2
O|field1|field2
A|field1|field2
S|field1|field2
O|field1|field2
O|field1|field2
O|field1|field2
A|field1|field2
S|field1|field2
O|field1|field2

将文件视为一个长流,并在管道符后删除换行符。

1
{m,g,n}awk NF=NF RS= OFS=\| FS='[|]\n'
{  g,n}awk NF=NF RS= OFS=\| FS='\\|\n' 
{m    }awk NF=NF RS= OFS=\| FS='\|\n' 
O|field1|field2
O|field1|field2
A|field1|field2
S|field1|field2
O|field1|field2
O|field1|field2
O|field1|field2
A|field1|field2
S|field1|field2
O|field1|field2

2
有趣的解决方案,你能稍微解释一下它是如何工作的吗?NF=NF 是什么意思,它只能放在开头吗? - The fourth bird
1
@Thefourthbird: NF = NF与其他人使用的$1=$1相同,但您可以在控制台终端中安全地键入它,不用加引号。 mawk将命令行赋值视为反斜杠,而gawknawk则像主代码中的任何双引号字符串一样处理它们,因此需要双反斜杠,正如您可以在FS的2个不同变体中看到的那样。如果您将NF=NF之后的任何内容放在其左侧,则还必须添加-v标志前缀,例如-v OFS=…。在主代码之后执行此操作可跳过该部分,但这些部分会被处理... - RARE Kpop Manifesto
1
在所有的 BEGIN { } 段落之后,但在 NR == 1 之前设置 RS 为空意味着所有没有完全空白行分隔的输入块将一次性处理,并且我只是交换了 FSRS 的角色,并使用 FS + OFS 来解决格式问题。因此,这些解决方案适用于不连续地输入的情况。 - RARE Kpop Manifesto
1
@Thefourthbird: $1 = $1实际上是一个有问题的形式。echo ' abc xyz ' | mawk '$1=$1' FS='[ ]+'打印出来的是绝对什么都没有,因为$1实际上是一个空字符串,而"abc"位于$2。这个赋值等同于执行$1 = "",这意味着该模式将一个空字符串评估为布尔值FALSE,因此不执行默认操作{ print }NF=NF修复了这个问题,但要记住它仍然跳过空行,因为NF为零。 - RARE Kpop Manifesto

1

使用GNU sed

sed -rz 's/\|\n([^OAS])/\|\1/g' input.txt

0
怎么样:
awk '/^[OAS]\|/ {if (l){print l}l=$0;next} {l=l $0} END {print l}' inputFile

变量l表示我们正在构建的行。
如果输入以特殊字符开头,则打印l(如果它不为空)。否则,开始构建该行。在处理完所有行后运行END以打印出l的最终版本。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接