• Linux中对管道命令中的任意子命令进行返回码校验


    ~~ linux return code with pipeline~~
    ~~ linux 管道命令中的返回码~~

    BASH SHELL中,通常使用 $? 来获取上一条命令的返回码。

    Shell Scripting Tutorial - Checking the exit status of ANY command in a pipeline

    对于管道中的命令,使用$?只能获取管道中最后一条命令的返回码,例如:

    ${RUN_COMMAND} 2> "${CUR_DIR}"/weiflow-from-weiclient.log | ${WEIBOX_UTIL_HOME}/rotatelogs.py -n 3 "${CUR_DIR}"/weiflow-from-weiclient.log 500M
    

    使用 $PIPESTATUS来获取管道中每个命令的返回码。

    注意:

    1. PIPESTATUS 是一个数组,第一条命令的返回码存储在${PIPESTATUS[0]},以此类推。

    2. 如果前一条命令不是一个管道,而是一个单独的命令,命令的返回码存储为({PIPESTATUS[0]},此时){PIPESTATUS[0]}同(?值相同(事实上,PIPESTATUS最后一个元素的值总是与)?的值相同)

    3. 每执行一条命令,切记PIPESTATUS都会更新其值为上一条命令的返回码,

    cat /not/a/valid/filename|cat
          if [ ({PIPESTATUS[0]} -ne 0 ]; then echo ){PIPESTATUS[@]}; fi

    上例中执行完管道后,({PIPESTATUS[0]}值为1,){PIPESTATUS[1]}值为0

    但是上面的脚本执行完成后,输出为0,这是因为if 分支的测试命令值为真,然后 PIPESTATUS[0]的值此时被置为0。应当在命令执行完成后立即在同一个测试命令中对所有值进行测试,例如

    if [ ({PIPESTATUS[0]} -eq 1 -a ){PIPESTATUS[1]} -eq 0 ] ; then echo something; fi

    或者先将$PIPESTATUS数组保存下来,以后再处理,例如

    ret=${PIPESTATUS[@]};

    RESULT

    ${RUN_COMMAND} 2> "${CUR_DIR}"/weiflow-from-weiclient.log | ${WEIBOX_UTIL_HOME}/rotatelogs.py -n 3 "${CUR_DIR}"/weiflow-from-weiclient.log 500M
    exit ${PIPESTATUS[0]}
    

    Checking the exit status of ANY command in a pipeline

    • It's a pretty common thing in a shell script to want to check the exit status of the previous command. You can do this with the $? variable, as is widely known:
    #!/bin/bash
    grep some.machine.example.com /etc/hosts
    if [ "$?" -ne "0" ]; then
      # The grep command failed to find "some.machine.example.com" in /etc/hosts file
      echo "I don't know the IP address of some.machine.example.com"
      exit 2
    fi
    
    • What gets difficult is when you execute a pipeline: (see pipelines for more information on the Unix Pipeline)
    #!/bin/bash
    grep some.machine.example.com /etc/hosts 2>&1 | tee /tmp/hosts-results.txt
    if [ "$?" -ne "0" ]; then
      # Ah - what we get here is the status of the "tee" command, 
      # not the status of the "grep" command :-(
    
    • What you get is the result of the tee command, which writes the results to the display as well as to the /tmp/hosts-results.txt file.

    • To find out what grep returned, $? is of no use.

    • Instead, use the ${PIPESTATUS[]} array variable. ${PIPESTATUS[0]} tells us what grep returned, while ${PIPESTATUS[1]} tells us what tee returned.

    • So, to see what grep found, we can write our script like this:

    #!/bin/bash
    grep some.machine.example.com /etc/hosts 2>&1 | tee /tmp/hosts-results.txt
    if [ "${PIPESTATUS[0]}" -ne "0" ]; then
      # The grep command failed to find "some.machine.example.com" in /etc/hosts file
      echo "I don't know the IP address of some.machine.example.com"
      exit 2
    fi
    

    Here's The Rub

    • The downside is, that any command you use to access ${PIPESTATUS[]}, will automatically replace the current state of the array with the return code of the command you have just run:
    Pipeline (command) PIPESTATUS shows status of:
    grep ... tee ...
    echo "Grep returned ${PIPESTATUS[0]}" echo "Grep ...
    echo "Maybe PIPESTATUS isn't so useful after all" echo "Maybe ...
    • So as soon as we use echo to tell us about the return code of grep, the ${PIPESTATUS[]} array now tells us about the return code of the echo statement itself, which is pretty likely to be zero, as not much can cause echo to fail!

    The Fix

    • Because ${PIPESTATUS[]} is a special variable, it changes all the time. However, we can copy this array into another array, which is just a regular array, which will not be changed at all just by running some more commands. Copying an array requires a slightly different syntax to simply copying contents of a variable into another:

    • RC=( "${PIPESTATUS[@]}" )

    • Where RC stands for Return Code. We can then investigate the status of RC at our leisure. For testing purposes, the program true always returns zero, and false always returns 1:

    #!/bin/bash
    echo "tftf"
    true | false | true | false
    RC=( "${PIPESTATUS[@]}" )
    echo "RC[0] = ${RC[0]}"		# true = 0
    echo "RC[1] = ${RC[1]}"		# false = 1
    echo "RC[2] = ${RC[2]}"		# true = 0
    echo "RC[3] = ${RC[3]}"		# false = 1
    
    echo "ftft"
    false | true | false | true
    RC=( "${PIPESTATUS[@]}" )
    echo "RC[0] = ${RC[0]}"		# false = 1
    echo "RC[1] = ${RC[1]}"		# true = 0
    echo "RC[2] = ${RC[2]}"		# false = 1
    echo "RC[3] = ${RC[3]}"		# true = 0
    
    echo "fftt"
    false | false | true | true
    RC=( "${PIPESTATUS[@]}" )
    echo "RC[0] = ${RC[0]}"		# false = 1
    echo "RC[1] = ${RC[1]}"		# false = 1
    echo "RC[2] = ${RC[2]}"		# true = 0
    echo "RC[3] = ${RC[3]}"		# true = 0
    
  • 相关阅读:
    关于TFS中WorkItem的探究
    Pro Android4 面试题归纳
    truncate 、delete、drop
    获取数据库中所有表的行数及其他信息
    【转】高手速成android开源项目【View篇】
    Can't create handler inside thread that has not called Looper.prepare()解决办法
    Android使用获取HTML
    .net操作EXCEL
    ASP.NET发送邮件
    如何使用 MasterPage
  • 原文地址:https://www.cnblogs.com/suanec/p/12405775.html
Copyright © 2020-2023  润新知