Search code examples
awksystem

Run Shell Command Inside Awk only Once 'per command' Using System(), i e, print only Once "text"


I have the following example of awk script below according to a prior answer to this question here 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

I am trying to add a command shell only one time run after creating the 2.txt file and before creating the 3.txt file.

This means that the specific shell command should only be executed after the 2.txt file has been fully created.

The solution should also be able to print text2 before 4.txt to be created.

I am using system(), my attempt so far it does not work well and the only thing that does not work is that the shell script is performed 3 times rather than 1 (this can be seen with the 3 text prints on the screen).

What can I do to see what really misses to solve this and echo 'text' is run only once and before 2.txt be created?

I tried to insert a counter (without while) inside system() (system("counter=0; if [[ "$counter" -gt 2 ]] then echo "text" fi; sleep 4")) but did not work, and it really does not make much sense because counter=0 should be out of system() I believe.

Note to Clarify Choice of Answer with Solved Status :

Doubt complex and cruel, choose which answer I will give as solved:

On the one hand the simplicity that explains a lot on the own account of the user @dan answer (author's answer to the previous issue of the file number in awk);

On the other hand a answer with a very useful deepening for learning from user @markp-fuso.

Maybe I should think about other users when they need to read this issue, so I'll choose how solved the issue of markp-fuso. I hope to understand all the authors of answers to this issue.


Solution

  • Primary issue (repeated text/text2 entries on stdout) comes from what appears to be a dual use of the awk variable k and some missing input control.

    The awk variable k is being used as an auto-incremented index by the 2nd and 3rd gsub(... k++ ...) calls; at no point is k reset to 0 (or 1) so logically speaking OPs k==1 tests should only occur once. However ...

    The tests for k==1 will occur for every input line (from all files) ... this occurs for lines with and without the home_cool string; and because there are two k==1 tests being validated for each input line OP is going to get double outputs (text and text2) as long as k==1.

    Also keep in mind that k is only being incremented (by the gsub() calls) for lines with the string home_cool; net result is lines without home_cool will see k==1 unchanged so the two k==1 tests will fire for these lines, too; we'll get extra text/text2 entries on stdout until the next gsub() fires and increments k (via the k++)


    I'd suggest using a different variable (eg, p) to determine when to print the text/text2 entries, and moving the k==1 tests (now if (p)) to a location where they are only run when appropriate.

    One idea for a rewrite:

    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
    

    When run this generates the following on stdout:

    text
    text2
    

    NOTES:

    • other than the print issues I'm assuming OP's logic is correct (ie, the contents of the [234].txt files are correct)
    • if an input file is empty then the associated f==? test will not fire which means ...
    • the associated if (p)... test/operation will not fire;
    • for example ...
    • if 0-2.txt is empty then f==4 will never test as positive so ...
    • the associated if (p) ... print "text2" ... will not fire
    • it's not clear (to me) if OP needs to conditionally print the text/text2 messages if the corresponding files are empty ...
    • easy enough to modify the code but I'll skip that for now so as to limit the confusion