使用 System() 仅在 awk 中运行一次 Shell 命令'per command',即只打印一次"text"



根据对这个问题的先前回答,我在下面有以下awk脚本示例 https://stackoverflow.com/a/69874658/10824251:

awk 
'
FNR==1 {++f}
f==1 {a[i++]=$0}
f==2 {if ($0~/home_cool/) {gsub(/home_cool/, a[int(j++/2)%2]) }; print > "2.txt"}
k==1 {system("echo 'text' && sleep 4")}
f==3 {if ($0~/home_cool/) {gsub(/home_cool/, a[int(k++/2)%3 + 2]) }; print > "3.txt"}
k==1 {system("echo 'text2' && sleep 4")}
f==4 {if ($0~/home_cool/) {gsub(/home_cool/, a[int(k++/2)%3 + 2]) }; print > "4.txt"}' 
1.txt 0.txt 0-1.txt 0-2.txt

我尝试在创建2.txt文件后和创建3.txt文件之前仅添加一次命令外壳。

这意味着只有在完全创建2.txt文件后,才应执行特定的 shell 命令。

该解决方案还应该能够在创建4.txt之前打印text2

我正在使用system(),到目前为止我的尝试效果不佳,唯一不起作用的是 shell 脚本执行了 3 次而不是 1 次(这可以通过屏幕上的 3text打印看到)。

我该怎么做才能看到真正错过了解决这个问题,并且在创建2.txt之前只运行一次echo 'text'

我试图在system()(system("counter=0; if [[ "$counter" -gt 2 ]] then echo "text" fi; sleep 4"))内插入一个计数器(没有while),但没有奏效,这真的没有多大意义,因为我相信counter=0应该不system()

澄清已解决状态答案选择的说明:

疑惑复杂而残酷,选择我将给出的答案作为解决:

一方面,简单性解释了用户自己的很多@dan答案(作者对awk中上一期文件编号的回答);

另一方面,一个非常有用的深化答案,用于向用户学习@markp-fuso。

也许我应该在其他用户需要阅读此问题时考虑他们,所以我会选择如何解决 markp-fuso 的问题。我希望了解所有回答这个问题的作者。

主要问题(在stdout上重复text/text2条目)来自awk变量k的双重用途和一些缺失的输入控制。

awk变量k被第 2 次和第 3 次gsub(... k++ ...)调用用作自动递增索引;在k任何时候都不会重置为0(或1),因此从逻辑上讲,操作k==1测试应该只发生一次。 然而。。。

k==1测试将针对每个输入行(来自所有文件)进行......对于带有和不带有home_cool字符串的行,都会发生这种情况;并且由于每个输入线有两个k==1测试正在验证,只要k==1,OP将获得双输出(texttext2)。

还要记住,对于带有字符串home_cool的行,k只会递增(通过gsub()调用);最终结果是不home_cool的行将看到k==1不变,因此两个k==1测试也将针对这些行触发;我们将在 stdout 上获得额外的text/text2条目,直到下一个gsub()触发并递增k(通过k++

)

我建议使用不同的变量(例如,p)来确定何时打印text/text2条目,并将k==1测试(现在if (p))移动到仅在适当时才运行它们的位置。

重写的一个想法:

awk '
FNR==1 {++f; p=1}                                     # reset our "p"rintme? flag for each new file
f==1 {a[i++]=$0}
f==2 {if ($0~/home_cool/)
{gsub(/home_cool/, a[int(j++/2)%2]) }
print > "2.txt"
}
f==3 {if ($0~/home_cool/)
{gsub(/home_cool/, a[int(k++/2)%3 + 2])}
if (p)                                          # old "k==1" test; if "p"rintme flag set then ...
{print "text"; system("sleep 4"); p=0}       # print, sleep, clear flag
print > "3.txt"                            
}
f==4 {if ($0~/home_cool/) 
{gsub(/home_cool/, a[int(k++/2)%3 + 2])}
if (p)                                          # old "k==1" test; if "p"rintme flag set then ...
{print "text2"; system("sleep 4"); p=0}      # print, sleep, clear flag
print > "4.txt"
}
' 1.txt 0.txt 0-1.txt 0-2.txt

运行时,这将在标准输出上生成以下内容:

text
text2

笔记:

  • 除了打印问题,我假设OP的逻辑是正确的(即,[234].txt文件的内容是正确的)
  • 如果输入文件为空,则关联的f==?测试将不会触发,这意味着...
  • 相关的if (p)...测试/操作不会触发;
  • 例如。。。
  • 如果0-2.txt是空的,那么f==4永远不会测试为阳性,所以......
  • 关联的if (p) ... print "text2" ...不会触发
  • 如果相应的文件为空,目前尚不清楚(对我来说)OP 是否需要有条件地打印text/text2消息......
  • 修改代码很容易,但我现在跳过它以限制混淆

我能做些什么来查看真正错过的问题来解决这个问题并回显"文本" 在创建之前只运行一次2.txt

如果您想要以下行为:仅在2.txt不存在时才运行,然后更改

{system("echo 'text' && sleep 4")}

{system("ls 2.txt && echo 'text' && sleep 4")}

如果没有2.txtls就会出现故障并阻止执行超出&&的内容。请注意,ls确实会发出输出,如果这在您的情况下不可接受,则需要添加stdoutstderr重定向,以便ls/dev/null

我尝试在创建 2.txt 文件后和创建 3.txt 文件之前只运行一次命令 shell。

添加一个FNR==1的 test 作为f==3块的第一个命令,如下所示:

f==3 {if (FNR==1) {system("##")}; if ($0~/home_cool/) {gsub(/home_cool/, a[int(k++/2)%3 + 2]) }; print > "3.txt"}

它将在该块中的其他任何内容之前执行。

或者,您可以添加此pattern {action},直接在f==3上方:

f==3 && FNR==1 {system("##")}
f==3 {if ($0~/home_cool/) {gsub(/home_cool/, a[int(k++/2)%3 + 2]) }; print > "3.txt"}

模式/操作块始终按给定的顺序执行,因此它将首先执行。

无论您认为哪个更具可读性。

最新更新