V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
bwangel
V2EX  ›  Linux

Bash 中判断命令是否存在的一个坑

  •  
  •   bwangel ·
    bwangelme · Aug 14, 2016 · 5542 views
    This topic created in 3545 days ago, the information mentioned may be changed or developed.

    今天写 Shell 的时候碰到了一个坑。

    坑 v1.0

    比如我想判断一个命令是否存在,我想着就用 test 的-x 吧,判断一个文件是否存在且具有执行权限,于是我写出了下面这段代码:

    PIP=`which pip`
    
    if [ ! -x ${PIP} ];then
        echo "NO"
    else
        echo "YES"
    fi
    

    然后我发现不对啊,为啥 pip 不存在的时候,还是输出 YES 呢,然后坑爹的发现, test 的-x-e后面不接参数的时候,默认为 True ,上面的那段代码由于 pip 命令不存在,所以${PIP}为空,就是空,什么都没有。然后就相当于运行的是if [ ! -x ];then,而这种情况下返回 True (什么鬼,为什么会这么设计)。

    坑 v2.0

    然后我就想,那就在${PIP}两边加上字符串吧,于是乎写成了这样:

    PIP=`which pip`
    
    if [ ! -x '${PIP}' ];then
        echo "NO"
    else
        echo "YES"
    fi
    

    这时我发现,这样 pip 命令不存在的情况下会变输出 NO 了,但是 pip 存在的情况下为啥还是 No 捏!然后坑爹的发现,原来上面的语句相当于是执行if [ ! -x '/usr/local/bin/pip'];then,这种情况下竟然返回的是 True ,然后我就搞不懂了,这是什么设计。

    弃坑而逃

    然后我老老实实地打开 Google ,搜了一下 Bash 中判断一个命令是否存在的办法,于是乎发现判断返回值就可以了,于是这样的代码就可以了:

    if ! command -v pip > /dev/null 2>&1;then
        echo "YES"
    fi  
    

    有感而发

    我感到很困惑的是,为什么 Bash 的字符串要这么设计,虽说是弱类型语言吧,但也不要这样傻傻分不清吧。也曾想过以后用 Bash 来做的事情,统一用 Python 来做。但是发现, Python 处理起字符串来,真不如 Bash 的 cat , sort , uniq , wc , awk 这一套撸的方便。请问大家觉得如何,大家平常都是如果处理这种工作的,就是需要固定地执行几十条命令的工作!

    22 replies    2016-09-03 13:07:45 +08:00
    lhbc
        1
    lhbc  
       Aug 14, 2016
    PIP=$(which pip)

    if [ ! -x "${PIP}" ];then
    echo "NO"
    else
    echo "YES"
    fi

    双引号和单引号有区别的。
    字符串变量,建议都使用双引号。
    skydiver
        2
    skydiver  
       Aug 14, 2016   ❤️ 3
    并没有什么坑,明明是你写错了
    xmgit
        3
    xmgit  
       Aug 14, 2016
    [[ ! -x $(which pip) ]] && echo "No" || echo "Yes"

    这不是坑,这是基本功。
    rrfeng
        4
    rrfeng  
       Aug 14, 2016
    1. 用双括号 [[ ]]
    2. 单引号和双引号
    vinceguo
        5
    vinceguo  
       Aug 14, 2016 via Android
    read the fucking manual before coding
    hosiet
        6
    hosiet  
       Aug 14, 2016 via Android   ❤️ 1
    建议打好基本功,先搞清楚单引号双引号的区别,再了解 test 工具方方面面的坑,以及 [[ ]] 语法相较 [ ] 的优越性,之后再考虑拿 shell 写脚本的事情
    bwangel
        7
    bwangel  
    OP
       Aug 14, 2016
    好吧,被狠狠打脸了,看来我得去学习一下!
    7wN5407klUw768m0
        8
    7wN5407klUw768m0  
       Aug 14, 2016
    引号区别而已
    knightdf
        9
    knightdf  
       Aug 15, 2016
    醉了,单引号双引号都没搞清楚写个毛的 shell
    mdzz
        10
    mdzz  
       Aug 15, 2016   ❤️ 3
    引用变量除特殊情况外时总使用大括号,总使用双引号: "${var}"
    常量变量使用 readonly 或者 declare -a ,并使用大写 : readonly CONST="value"
    function 内尽量使用 local 声明变量: local var
    参数变量引用时除特殊情况外总使用 @,并使用双引号: "$@"

    一点点经验,分享给大家,很惭愧。
    jemyzhang
        11
    jemyzhang  
       Aug 15, 2016 via Android
    双引号…单引号内不转义
    bwangel
        12
    bwangel  
    OP
       Aug 15, 2016
    @mdzz ,提一个小小的意见

    好像 declare 的-r 才是声明 readonly 吧!


    -a Each name is an indexed array variable (see Arrays above).

    -r Make names readonly. These names cannot then be assigned values by subsequent assignment statements or unset.
    wweir
        13
    wweir  
       Aug 15, 2016 via Android
    待会儿抄一个美观的过来
    wweir
        14
    wweir  
       Aug 15, 2016   ❤️ 1
    if (( ${+commands[pip]} )); then
    # xxx
    fi
    ppwangs
        15
    ppwangs  
       Aug 15, 2016
    如果命令不存在的话,难道不是输出
    which: no CMD in (.....)
    吗?
    FrankHB
        16
    FrankHB  
       Aug 15, 2016
    都 bash 了,放着内置 hash 命令不用还 test ,是有多无聊……
    necomancer
        17
    necomancer  
       Aug 15, 2016
    which 好像这么用这个变量不会为空的

    ➜ ~ pip=`which pip`
    ➜ ~ echo $pip
    /usr/bin/pip
    ➜ ~ pip=`which pippip`
    ➜ ~ echo $pip
    pippip not found

    所以还是用 $? 吧,如果没找到 $? 是 1 ,如果找到了,是 0 ,或者直接写:

    if which $1 &> /dev/null; then
    echo found
    else
    echo not found
    fi
    kaneg
        18
    kaneg  
       Aug 15, 2016
    用 which pip;echo $?

    输出 0 就是存在,非 0 就是不存在
    bwangel
        19
    bwangel  
    OP
       Aug 16, 2016
    @necomancer 你的是 zsh 吧, zsh 会输出 pipip not found , bash 会输出空!
    Azus
        20
    Azus  
       Aug 16, 2016
    @bwangel
    which 输出是否为空和 bash 没有关系, which 不是 bash 的内部命令
    debian 系输出为空
    redhat 系输出不为空
    necomancer
        22
    necomancer  
       Sep 3, 2016
    @bwangel 输出不一定为空啊,也有是 which: no xxxx in (路径名) 的形式,所以 echo $? 是兼容比较好的选择。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2978 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 79ms · UTC 15:01 · PVG 23:01 · LAX 08:01 · JFK 11:01
    ♥ Do have faith in what you're doing.