V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
bwangel
V2EX  ›  Linux

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

  •  
  •   bwangel ·
    bwangelme · 2016-08-14 21:56:27 +08:00 · 4842 次点击
    这是一个创建于 3027 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天写 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 这一套撸的方便。请问大家觉得如何,大家平常都是如果处理这种工作的,就是需要固定地执行几十条命令的工作!

    第 1 条附言  ·  2016-10-03 10:46:43 +08:00
    22 条回复    2016-09-03 13:07:45 +08:00
    lhbc
        1
    lhbc  
       2016-08-14 22:14:56 +08:00
    PIP=$(which pip)

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

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

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

    一点点经验,分享给大家,很惭愧。
    jemyzhang
        11
    jemyzhang  
       2016-08-15 01:02:28 +08:00 via Android
    双引号…单引号内不转义
    bwangel
        12
    bwangel  
    OP
       2016-08-15 07:50:28 +08:00
    @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  
       2016-08-15 09:25:11 +08:00 via Android
    待会儿抄一个美观的过来
    wweir
        14
    wweir  
       2016-08-15 09:59:44 +08:00   ❤️ 1
    if (( ${+commands[pip]} )); then
    # xxx
    fi
    ppwangs
        15
    ppwangs  
       2016-08-15 16:27:56 +08:00
    如果命令不存在的话,难道不是输出
    which: no CMD in (.....)
    吗?
    FrankHB
        16
    FrankHB  
       2016-08-15 20:09:36 +08:00
    都 bash 了,放着内置 hash 命令不用还 test ,是有多无聊……
    necomancer
        17
    necomancer  
       2016-08-15 22:32:00 +08:00
    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  
       2016-08-15 23:36:09 +08:00
    用 which pip;echo $?

    输出 0 就是存在,非 0 就是不存在
    bwangel
        19
    bwangel  
    OP
       2016-08-16 07:24:55 +08:00
    @necomancer 你的是 zsh 吧, zsh 会输出 pipip not found , bash 会输出空!
    Azus
        20
    Azus  
       2016-08-16 19:38:01 +08:00
    @bwangel
    which 输出是否为空和 bash 没有关系, which 不是 bash 的内部命令
    debian 系输出为空
    redhat 系输出不为空
    necomancer
        22
    necomancer  
       2016-09-03 13:07:45 +08:00
    @bwangel 输出不一定为空啊,也有是 which: no xxxx in (路径名) 的形式,所以 echo $? 是兼容比较好的选择。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1293 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 17:54 · PVG 01:54 · LAX 09:54 · JFK 12:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.