简体   繁体   中英

I use perl in a bash script that pipes to a regular expression. How do I also set a variable in the piped stream?

I have created a bash script that runs on several machines that contain different git local repositories. It tests many conditions and tells me if the repository has uncommitted files, untracked files, and one test in particular tells me that the local is ahead or behind the remote by the number of commits. The problem with the script is that it doesn't return or set an 'ok' flag which I use to echo the "ok" message if everything is in sync. So, I get the message that it's ahead or behind, but then get the "ok" message. Here is the portion of the script that does the ahead or behind, and I can't see how to get it to set an ok = false somehow.

   git fetch>/dev/null && git branch -v |
        perl -wlne'
           print "$ENV{reponame} [$1] --> $3 $2"
              if /^..(\S+)\s+([a-f0-9]+)\s+(\[(?:ahead|behind)\s+\d+\])/
        ' |
        while IFS= read -r MOD; do
           ok=false
           printf ' %s\n' "$MOD"  # Replace with code that uses $MOD
        done
        if $ok; then
              echo " OK --> $reponame [$br] $rev"
        fi 

I copied this from another script and don't really understand the IFS = read -r MOD; section that I thought might set the flag, but it doesn't occur.

This is the output I get:

 bin [develop] --> [behind 1] 026e1ad
 OK --> bin [develop] 026e1ad
 OK --> notes [develop] 4cd077f
 OK --> indecks [develop] e6b4293
 OK --> queue [develop] 5469679
 OK --> frameworks [master] 05fedb6
 OK --> dashboard [isolate] f8b1101
 OK --> nodejs [develop] 5af2ea7
 OK --> perl-forth [master] 45cc837
 OK --> blog [master] c19edfd

Note that for bin I get:

 bin [develop] --> [behind 1] 026e1ad
 OK --> bin [develop] 026e1ad

I'd rather not get that OK after the behind 1. Another script checks for any non-OK in the left column and sends me an email.

With the perl and all the piping, how could I set the ok variable before it prints?

In most shell implementations, all processes in a pipeline are run in a subshell. In this case, you're running a while loop at the end of a pipeline, so it (and it alone) is in the subshell. Whether you set ok to false or not, it has no effect on the if block because that's run the main shell, which doesn't inherit variables from the subshell.

zsh and AT&T ksh (but not other ksh implementations) execute the last command in the main shell and not a subshell. POSIX permits either behavior, but the bash behavior is far more common among shells.

The easiest way to handle this is to run the entire command you're interested in in a subshell:

  git fetch>/dev/null && git branch -v |
        perl -wlne'
           print "$ENV{reponame} [$1] --> $3 $2"
              if /^..(\S+)\s+([a-f0-9]+)\s+(\[(?:ahead|behind)\s+\d+\])/
        ' |
        (while IFS= read -r MOD; do
           ok=false
           printf ' %s\n' "$MOD"  # Replace with code that uses $MOD
        done
        if $ok; then
              echo " OK --> $reponame [$br] $rev"
        fi) 

This puts both parts using the ok variable in the same subshell, so you can modify it and it will have an effect.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM