【问题标题】:Run Shell Command Inside Awk only Once 'per command' Using System(), i e, print only Once "text"使用 System() 仅在 Awk 内运行 Shell 命令一次“每个命令”,即仅打印一次“文本”
【发布时间】:2021-12-20 23:07:06
【问题描述】:

根据https://stackoverflow.com/a/69874658/10824251https://stackoverflow.com/a/69874658/10824251 的先前回答,我有以下 awk 脚本示例:

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 文件之前只运行一次命令 shell。

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

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

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

我可以做些什么来看看真正错过了什么来解决这个问题并且echo 'text' 只运行一次并且在2.txt 被创建之前?

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

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

怀疑复杂而残酷,请选择我将给出的答案:

一方面解释了很多关于用户个人帐户的简单性@dan 答案(作者对上一期文件号的答案awk);

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

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

【问题讨论】:

  • 只需添加一个变量haverun 并使用k==1 && haverun==0。然后在system("echo 'text' && sleep 4") 之后添加; haverun=1。你可以给它起任何你喜欢的名字。您只是将变量用作标志来告诉您 system() 是否已运行。
  • 认为标志只是作为0/1,或not run/run 跟踪system() 是否已运行的“状态”的方式。您可以通过无数种方式在编程中使用相同的标志概念。如果您仍有疑问,请告诉我。
  • 这样做。 awk -v haverun=0 \ ... 或在FNR==1 {++f} 之前删除变量,添加BEGIN { haverun=0 }。无论哪种方式都可以。
  • 您甚至可以完全消除声明。当 awk 遇到一个尚未声明的变量时,它被定义为具有 0 的值。所以如果你看我的第一条评论,我没有提到在任何地方声明haverun。示例 printf "%s\n" a b 1 2 3 | awk 'haverun>1; { haverun++ }'注意: 一个条件不跟随 { .. } 就像 haverun>1 只是使用默认命令 print - 如果您想知道数字是如何打印出来的)GNU Awk User's Guide 是你的朋友。
  • awk 是一个操作文本的工具。 shell 是一种工具,用于操作文件和进程以及对其他工具的顺序调用。您正在尝试编写一个 awk 脚本来对其他工具的调用进行排序,即您正在尝试让 awk 完成 shell 存在的工作。我强烈建议您不要这样做,只需使用 shell 调用 awk 和其他适当的工具。我希望您的问题更清楚,这样我们就可以指导您如何以正确的方式做任何您想做的事情,而不是帮助您实施几乎可以肯定是错误的方式。

标签: awk system


【解决方案1】:

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

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

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

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

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

【讨论】:

  • 很抱歉,我没有听stempiles 没有指定在完成生成2.txt 的处理后应该运行命令shell。因为尽管2.txt 已创建,它仍在处理中并用awk 替换填充。在这种情况下,我编辑了问题,谢谢,对于我的这个错误,我深表歉意。
【解决方案2】:

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

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

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

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

或者,您可以在f==3 的正上方添加此pattern {action}

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

模式/动作块总是按照给定的顺序执行,所以它会先执行。

你认为哪个更易读。

【讨论】:

  • 真的,我的问题仅限于提供的源文件和预期输出的上下文,目前是唯一必要的。但是......我还没有测试过 Markp-fuso 的答案,我相信在未来处理其他几个要求时它会更加通用,但是......用户 Dan 准确地提出了我的问题的解决方案。我没有时间测试markp-fuso响应,所以我将分别对响应的三位作者进行markp-fuso测试,然后再给出解决这个问题。谢谢
【解决方案3】:

主要问题(在标准输出上重复 text/text2 条目)来自似乎是 awk 变量 k 的双重使用和一些缺少的输入控件。

awk 变量k 被第二个和第三个gsub(... k++ ...) 调用用作自动递增索引; k 绝不会重置为 0(或 1),所以从逻辑上讲,OP k==1 测试应该只发生一次。不过……

k==1 的测试将发生在每个输入行(来自所有文件)...这发生在带有和不带有 home_cool 字符串的行上;并且因为有两个 k==1 测试正在验证每个输入行 OP 将获得双输出(texttext2),只要 k==1

还请记住,k 仅对带有字符串home_cool 的行进行递增(通过gsub() 调用);最终结果是没有home_cool 的行将看到k==1 不变,因此两个k==1 测试也会为这些行触发;我们将在标准输出上获得额外的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 消息(对我而言)尚不清楚 ...
  • 很容易修改代码,但为了避免混淆,我暂时跳过它

【讨论】:

  • 真的,我的问题仅限于提供的源文件和预期输出的上下文,目前是唯一必要的。但是......我还没有测试过 Markp-fuso 的答案,而且我相信在未来处理其他几个要求时会更加通用,但是......用户 Dan 准确地提出了我的问题的解决方案。我没有时间测试markp-fuso响应,所以我将分别对响应的三位作者进行markp-fuso测试,然后再给出解决这个问题。谢谢
  • fwiw ...问题问:如何运行system()调用一次and echo 'text' is run only once;这就是这个答案的全部内容
  • 我没有说我会在通用性的标准下进行分析,我说虽然你的答案似乎更通用,但出于所有目的,我什至还没有测试你的答案,我只是做了关于您的答案可能的多功能性的观察,我将仅根据您提到的问题标题的标准来判断。
  • 至于标题的意思是,由于我无法一次打印“文本”,如果我添加一个新的系统()命令,将“文本2”打印到另一个文件号也是预计只打印一个文本2
猜你喜欢
  • 1970-01-01
  • 2021-05-25
  • 1970-01-01
  • 2011-02-14
  • 1970-01-01
  • 1970-01-01
  • 2014-08-21
  • 2016-03-07
  • 2022-11-04
相关资源
最近更新 更多