AWK:从段落中跳过一行

时间:2019-02-19 00:04:31

标签: awk

问题(解决方案如下)

让我们说以下脚本对多个文件进行操作,如果找到了模式“ TODO:”,则会打印出a whole surrounding paragraph

awk -v RS='' '{
    if(/TODO:/) {
        print
        print "\n"
    }
}' *.txt

是否可以通过以下方式打印这些段落,使这些段落中包含模式DONE:的行被跳过?

如果提供以下数据:

Apples
Oranges
Bananas

TODO: A
TODO: B
Lorem ipsum

Ad usu oporteat
TODO: C
DONE: D
TODO: E
Ipsum lorem

然后,输出不应包含条目DONE: D,不应包含带有水果的段落(因为那里没有TODO:项),并且应包含其他所有内容:

TODO: A
TODO: B
Lorem ipsum

Ad usu oporteat
TODO: C
TODO: E
Ipsum lorem

(当然,我可以通过管道| grep -v 'DONE:'进行操作,但是想在这里学到一些有关awk的信息...)

解决方案和结果:

首先,由@EdMorton进行,是对所提供功能的明显而明确的改进:

awk -v RS='' -v ORS='' 'FNR==1{td_file=0} {
    if(/TODO:/) {
        if (!td_file) {
            print "\n\n"
            f=FILENAME; sub(".txt", "", f)
            print f "\n"
            td_file=1
        }
        sub(/\n.*DONE:.[^\n]*\n/,"\n")
        print
    }
}' *.txt

time报告:

real    0m0.048s
user    0m0.029s
sys     0m0.018s
据我了解并经过一番清理后,由@ RavinderSingh13撰写的

第二

awk '
# Check, if this a new file being proceeded
# If so, reset td_file marker to False
FNR==1{td_file=0}{
# Check if this file contains 'TODO:' pattern and if it hasn't been proceeded yet
    if(/TODO:/ && !td_file) {
# If so, print out FILENAME
        print "\n" FILENAME
# Set td_file marker to True
# (to mark the file as proceeded, in order not to print out FILENAME twice)
        td_file=1
    }
}
# Check, if this is a new file OR the current line has data (number of fields is not 0)
FNR==1 || !NF{
# If so, and if td_entr marker is True, and if we have something to print (container cont is not empty)
    if (td_entr && cont) {
# Then, print it out
        print cont
    }
# And reset variables
    cont=td_entr=""
}
# Check if the current line starts with 'TODO:'
/TODO:/ {
# If so, set todo marker to 1
    td_entr=1
}
# Also, check if the current line does not contain 'DONE:'
!/DONE:/ {
# If so, check variable cont:
# If it doesn't exist, create it and assign to the current line being proceeded
# If it exists, add the Output Records Separator, ORS, and then append the current line being proceeded
    cont=cont?cont ORS $0:$0
    }
' *.txt

经过测试,time报告说该版本需要更多资源(如果我正确理解算法,这并不奇怪):

real    0m0.090s
user    0m0.065s
sys     0m0.022s

鉴于此比较(并且由于第一个解决方案完全基于我在问题中提供的小脚本),我将@EdMorton答复设置为答案。尽管如此,我还是非常感谢两位参与者,谢谢(我今天确实学到了一些东西:)!

2 个答案:

答案 0 :(得分:2)

编辑: :由于OP在他/她的帖子中添加了更多详细信息,因此现在添加以下解决方案。

awk 'prev!=FILENAME{if(found && val){print val};val=found="";prev=FILENAME}!NF{if(val && found){print val};val=found=""} /^TODO/{found=1} !/DONE:/{val=val?val ORS $0:$0} END{if(val && found){print val}}'  *.txt

说明: 在此处添加了以上代码的完整说明。

awk '
prev!=FILENAME{               ##Checking if variable prev value is NOT equal to FILENAME(which is awk out of the box variable which concatins name of Input_file(s)).
  if(found && val){           ##If new Input_file is being read and variable found and val are NOT NULL then do following.
    print val                 ##Printing variable val here.
  }
  val=found=""                ##Nullifying variables val and found here.
  prev=FILENAME               ##Setting variable prev value to FILENAME(current Input_files name).
}
!NF{                          ##Checking condition if a line DO NOT have any fields or have spaces only then do following.
  if(val && found){           ##Checkig condition if variable val and found are NOT NULL here then do following.
    print val                 ##Printing variable val here.
  }
  val=found=""                ##Nullifying variables val and found here.
}
/^TODO/{                      ##Checking condition if a line starts with TODO then do following.
  found=1                     ##Setting found value as 1 here.
}
!/DONE:/{                     ##Checking if a line does not contains string DONE: then do following.
  val=(val?val ORS $0:$0)     ##Creatig variable val whose value will be keep concatenating its own value.
}
END{                          ##Mentioning END section of this awk program here.
  if(val && found){           ##Checking if variable val and found are NOT NULL then do following.
    print val                 ##Printing variable val here.
  }
}' *.txt                      ##Mentioning all *.txt here.

我在上面假设您只想从TODO到直到Ipsum字符串开始打印,如果行中包含DONE: D,则在这之间也要跳过。



一个简单的awk就可以了。

awk '!/DONE: D/' Input_file

说明: 在这里,我们正在检查条件,如果一行不包含字符串DONE: D,然后打印这些行。现在问题来了,当条件变为TRUE时,我们没有提及任何动作,因此,其解释是:awk对条件然后动作的方法起作用,因为没有定义动作,因此默认情况下将打印当前行。 / p>

答案 1 :(得分:1)

$ awk -v RS= -v ORS='\n\n' '/TODO:/{sub(/\nDONE: D\n/,"\n"); print}' file
TODO: A
TODO: B
Lorem ipsum

Ad usu oporteat
TODO: C
TODO: E
Ipsum lorem