Prevent grep returning an error when input doesn't match

后端 未结 6 2320
鱼传尺愫
鱼传尺愫 2020-12-08 04:01

I want to write in a bash script a piece of code that checks if a program is already running. I have the following in order to search whether bar is running

         


        
相关标签:
6条回答
  • 2020-12-08 04:11

    Try to make so:

    ps auxw | grep -v grep | cat

    cat returns always 0 and ignores exit code of grep

    0 讨论(0)
  • 2020-12-08 04:18

    Short answer

    Write

    ps -ef | grep bar | { grep -v grep || test $? = 1; }
    

    if you are using set -e.

    If you use bash's pipefail option (set -o pipefail), remember to apply the exception handling (||test) to every grep in the pipeline:

    ps -ef | { grep bar || test $? = 1; } | { grep -v grep || test $? = 1; }
    

    In shell scripts I suggest you to use the ”catch-1-grep“ (c1grep) utility function:

    c1grep() { grep "$@" || test $? = 1; }
    

    Explained

    grep's exit status is either 0, 1 or 2: [1]

    • 0 means a line is selected
    • 1 means no lines were selected
    • 2 means an error occurred

    grep can also return other codes if it's interrupted by a signal (e.g. 130 for SIGINT).

    Since we only want to ignore exit status 1, we use test to suppress that specific exit status.

    • If grep returns 0, test is not run.
    • If grep returns 1, test is run and returns 0.
    • If grep returns any other value, test is run and returns 1.

    In the last case, the script will exit immediately due to set -e or set -o pipefail. However, if you don't care about grep errors at all, you can of course write

    ps -ef | grep bar | { grep -v grep || true; }
    

    as suggested by Sean.


    [additional] usage in shell scripts

    In shell scripts, if you are using grep a lot, I suggest you to define an utility function:

    # "catch exit status 1" grep wrapper
    c1grep() { grep "$@" || test $? = 1; }
    

    This way your pipe will get short & simple again, without losing the features of set -e and set -o pipefail:

    ps -ef | c1grep bar | c1grep -v grep
    

    FYI:

    • I called it c1grep to emphasize it's simply catching exit status 1, nothing else.
    • I could have called the function grep instead (grep() { env grep "$@" ...; }), but I prefer a less confusing and more explicit name, c1grep.

    [additional] ps + grep

    So if you want to know how to avoid grep -v grep or even the | grep part of ps|grep at all, take a look at some of the other answers; but this is somewhat off-topic imho.


    [1] grep manpage

    0 讨论(0)
  • 2020-12-08 04:22

    Sure:

    ps -ef | grep bar | { grep -v grep || true; }
    

    Or even:

    ps -ef | grep bar | grep -v grep | cat
    
    0 讨论(0)
  • 2020-12-08 04:31

    Why ask ps to provide massive amounts of output with -ef if you only are going to throw away 99% of it? ps and especially the GNU version is a swiss army knife of handy functionality. Try this:

    ps -C bar -o pid= 1>/dev/null
    

    I specify -o pid= here just because, but in fact it's pointless since we throw away all of stdout anyway. It would be useful if you wanted to know the actual running PID, though.

    ps automatically will return with a non-zero exist status if -C fails to match anything and with zero if it matches. So you could simply say this

    ps -C bar 1>/dev/null && echo bar running || echo bar not running
    

    Or

    if ps -C bar 1>/dev/null ; then
        echo bar running
    else
        echo bar not running
    fi
    

    Isn't that simpler? No need for grep, not twice or even once.

    0 讨论(0)
  • 2020-12-08 04:32
    foo=`ps -ef | grep bar | grep -v grep` || true
    
    0 讨论(0)
  • 2020-12-08 04:35

    A good trick to avoid grep -v grep is this:

    ps -ef | grep '[b]ar'
    

    That regular expression only matches the string "bar". However in the ps output, the string "bar" does not appear with the grep process.


    In the days before I learned about pgrep, I wrote this function to automate the above command:

    psg () { 
        local -a patterns=()
        (( $# == 0 )) && set -- $USER
        for arg do
            patterns+=("-e" "[${arg:0:1}]${arg:1}")
        done
        ps -ef | grep "${patterns[@]}"
    }
    

    Then,

    psg foo bar
    

    turns into

    ps -ef | grep -e '[f]oo' -e '[b]ar'
    
    0 讨论(0)
提交回复
热议问题