Shell学习 - 2.31 Shell test命令

test 是 Shell 内置命令,用来检测某个条件是否成立。test 通常和 if 语句一起使用,并且大部分 if 语句都依赖 test。

test 命令有很多选项,可以进行数值、字符串和文件三个方面的检测。

Shell test 命令的用法为:

test expression

当 test 判断 expression 成立时,退出状态为 0,否则为非 0 值。

test 命令也可以简写为[],它的用法为:

[ expression ]

注意[]和expression之间的空格,这两个空格是必须的,否则会导致语法错误。[]的写法更加简洁,比 test 使用频率高。

test 和 [] 是等价的,后续我们会交替使用 test 和 [],以让读者尽快熟悉。

在《Shell if else》中,我们使用 (()) 进行数值比较,这节我们就来看一下如何使用 test 命令进行数值比较。

#!/bin/bash

read age

if test $age -le 2; then

    echo "婴儿"

elif test $age -ge 3 && test $age -le 8; then

    echo "幼儿"

elif [ $age -ge 9 ] && [ $age -le 17 ]; then

    echo "少年"

elif [ $age -ge 18 ] && [ $age -le 25 ]; then

    echo "成年"

elif test $age -ge 26 && test $age -le 40; then

    echo "青年"

elif test $age -ge 41 && [ $age -le 60 ]; then

    echo "中年"

else

    echo "老年"

fi

其中,-le选项表示小于等于,-ge选项表示大于等于,&&是逻辑与运算符。

学习 test 命令,重点是学习它的各种选项,下面我们就逐一讲解。

1) 与文件检测相关的 test 选项

表1:test 文件检测相关选项列表

文件类型判断

选 项

作 用

-b filename

判断文件是否存在,并且是否为块设备文件。

-c filename

判断文件是否存在,并且是否为字符设备文件。

-d filename

判断文件是否存在,并且是否为目录文件。

-e filename

判断文件是否存在。

-f filename

判断文件是否存在,井且是否为普通文件。

-L filename

判断文件是否存在,并且是否为符号链接文件。

-p filename

判断文件是否存在,并且是否为管道文件。

-s filename

判断文件是否存在,并且是否为非空。

-S filename

判断该文件是否存在,并且是否为套接字文件。

文件权限判断

选 项

作 用

-r filename

判断文件是否存在,并且是否拥有读权限。

-w filename

判断文件是否存在,并且是否拥有写权限。

-x filename

判断文件是否存在,并且是否拥有执行权限。

-u filename

判断文件是否存在,并且是否拥有 SUID 权限。

-g filename

判断文件是否存在,并且是否拥有 SGID 权限。

-k filename

判断该文件是否存在,并且是否拥有 SBIT 权限。

文件比较

选 项

作 用

filename1 -nt filename2

判断 filename1 的修改时间是否比 filename2 的新。

filename -ot filename2

判断 filename1 的修改时间是否比 filename2 的旧。

filename1 -ef filename2

判断 filename1 是否和 filename2 的 inode 号一致,可以理解为两个文件是否为同一个文件。这个判断用于判断硬链接是很好的方法

Shell test 文件检测举例:

#!/bin/bash

read filename

read url

if test -w $filename && test -n $url

then

    echo $url > $filename

    echo "写入成功"

else

    echo "写入失败"

fi

在 Shell 脚本文件所在的目录新建一个文本文件并命名为 urls.txt,然后运行 Shell 脚本,运行结果为:

urls.txt↙

http://c.biancheng.net/shell/↙

写入成功

2) 与数值比较相关的 test 选项

表2:test 数值比较相关选项列表

选 项

作 用

num1 -eq num2

判断 num1 是否和 num2 相等。

num1 -ne num2

判断 num1 是否和 num2 不相等。

num1 -gt num2

判断 num1 是否大于 num2 。

num1 -lt num2

判断 num1 是否小于 num2。

num1 -ge num2

判断 num1 是否大于等于 num2。

num1 -le num2

判断 num1 是否小于等于 num2。

注意,test 只能用来比较整数,小数相关的比较还得依赖 bc 命令。

Shell test 数值比较举例:

#!/bin/bash

read a b

if test $a -eq $b

then

    echo "两个数相等"

else

    echo "两个数不相等"

fi

运行结果1:

10 10

两个数相等

运行结果2:

10 20

两个数不相等

3) 与字符串判断相关的 test 选项

表3:test 字符串判断相关选项列表

选 项

作 用

-z str

判断字符串 str 是否为空。

-n str

判断宇符串 str 是否为非空。

str1 = str2

str1 == str2

=和==是等价的,都用来判断 str1 是否和 str2 相等。

str1 != str2

判断 str1 是否和 str2 不相等。

str1 \> str2

判断 str1 是否大于 str2。\>是>的转义字符,这样写是为了防止>被误认为成重定向运算符。

str1 \< str2

判断 str1 是否小于 str2。同样,\<也是转义字符。

有C语言、C++、Python、Java 等编程经验的读者请注意,==、>、< 在大部分编程语言中都用来比较数字,而在 Shell 中,它们只能用来比较字符串,不能比较数字,这是非常奇葩的,大家要习惯。

其次,不管是比较数字还是字符串,Shell 都不支持 >= 和 <= 运算符,切记。

Shell test 字符串比较举例:

#!/bin/bash

read str1

read str2

#检测字符串是否为空

if [ -z "$str1" ] || [ -z "$str2" ]

then

    echo "字符串不能为空"

    exit 0

fi

#比较字符串

if [ $str1 = $str2 ]

then

    echo "两个字符串相等"

else

    echo "两个字符串不相等"

fi

运行结果:

http://c.biancheng.net/

http://c.biancheng.net/shell/

两个字符串不相等

细心的读者可能已经注意到,变量 $str1 和 $str2 都被双引号包围起来,这样做是为了防止 $str1 或者 $str2 是空字符串时出现错误,本文的后续部分将为你分析具体原因。

4) 与逻辑运算相关的 test 选项

表4:test 逻辑运算相关选项列表

选 项

作 用

expression1 -a expression

逻辑与,表达式 expression1 和 expression2 都成立,最终的结果才是成立的。

expression1 -o expression2

逻辑或,表达式 expression1 和 expression2 有一个成立,最终的结果就成立。

!expression

逻辑非,对 expression 进行取反。

改写上面的代码,使用逻辑运算选项:

#!/bin/bash

read str1

read str2

#检测字符串是否为空

if [ -z "$str1" -o -z "$str2" ]  #使用 -o 选项取代之前的 ||

then

    echo "字符串不能为空"

    exit 0

fi

#比较字符串

if [ $str1 = $str2 ]

then

    echo "两个字符串相等"

else

    echo "两个字符串不相等"

fi

前面的代码我们使用两个[]命令,并使用||运算符将它们连接起来,这里我们改成-o选项,只使用一个[]命令就可以了。

在 test 中使用变量建议用双引号包围起来

test 和 [] 都是命令,一个命令本质上对应一个程序或者一个函数。即使是一个程序,它也有入口函数,例如C语言程序的入口函数是 main(),运行C语言程序就从 main() 函数开始,所以也可以将一个程序等效为一个函数,这样我们就不用再区分函数和程序了,直接将一个命令和一个函数对应起来即可。

有了以上认知,就很容易看透命令的本质了:使用一个命令其实就是调用一个函数,命令后面附带的选项和参数最终都会作为实参传递给函数。

假设 test 命令对应的函数是 func(),使用test -z $str1命令时,会先将变量 $str1 替换成字符串:

  • 如果 $str1 是一个正常的字符串,比如 abc123,那么替换后的效果就是test -z abc123,调用 func() 函数的形式就是func("-z abc123")。test 命令后面附带的所有选项和参数会被看成一个整体,并作为实参传递进函数。

  • 如果 $str1 是一个空字符串,那么替换后的效果就是test -z,调用 func() 函数的形式就是func("-z "),这就比较奇怪了,因为-z选项没有和参数成对出现,func() 在分析时就会出错。

如果我们给 $str1 变量加上双引号,当 $str1 是空字符串时,test -z "$str1"就会被替换为test -z "",调用 func() 函数的形式就是func("-z \"\""),很显然,-z选项后面跟的是一个空字符串(\"表示转义字符),这样 func() 在分析时就不会出错了。

所以,当你在 test 命令中使用变量时,我强烈建议将变量用双引号""包围起来,这样能避免变量为空值时导致的很多奇葩问题。

总结

test 命令比较奇葩,>、<、== 只能用来比较字符串,不能用来比较数字,比较数字需要使用 -eq、-gt 等选项;不管是比较字符串还是数字,test 都不支持 >= 和 <=。有经验的程序员需要慢慢习惯 test 命令的这些奇葩用法。

对于整型数字的比较,我建议大家使用 (()),这在《Shell if else》中已经进行了演示。(()) 支持各种运算符,写法也符合数学规则,用起来更加方便,何乐而不为呢?

几乎完全兼容 test ,并且比 test 更加强大,比 test 更加灵活的是[[ ]];[[ ]]不是命令,而是 Shell 关键字,下节《Shell [[]]》我们将会讲解。

参考:

Shell test命令(Shell [])详解,附带所有选项及说明 (biancheng.net)

$ /home/qlunlp/software/anaconda3531/bin/conda update -n base conda -y environment variables: CIO_TEST=<not set> CONDA_ROOT=/home/qlunlp/software/anaconda3531 CONDA_SHLVL=0 LD_LIBRARY_PATH=/home/qlunlp/software/anaconda3531/envs/paddle_env/lib/:/home/qlunlp/s oftware/anaconda3531/envs/paddle_env/lib/: PATH=/home/qlunlp/.vscode-server/cli/servers/Stable-2fc07b811f760549dab9be9 d2bedd06c51dfcb9a/server/bin/remote-cli:/home/qlunlp/software/anaconda 3531/bin:/home/qlunlp/software/anaconda3531/bin:/usr/local/sbin:/usr/l ocal/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/sn ap/bin:/usr/local/cuda-12.3/bin:/home/qlunlp/.vscode- server/data/User/globalStorage/github.copilot- chat/debugCommand:/usr/local/cuda-12.3/bin REQUESTS_CA_BUNDLE=<not set> SSL_CERT_FILE=/usr/lib/ssl/certs/ca-certificates.crt active environment : None shell level : 0 user config file : /home/qlunlp/.condarc populated config files : /home/qlunlp/.condarc conda version : 4.5.11 conda-build version : 3.15.1 python version : 3.7.0.final.0 base environment : /home/qlunlp/software/anaconda3531 (writable) channel URLs : https://anaconda.org/linux-64 https://anaconda.org/noarch https://repo.anaconda.com/pkgs/main/linux-64 https://repo.anaconda.com/pkgs/main/noarch https://repo.anaconda.com/pkgs/free/linux-64 https://repo.anaconda.com/pkgs/free/noarch https://repo.anaconda.com/pkgs/r/linux-64 https://repo.anaconda.com/pkgs/r/noarch https://repo.anaconda.com/pkgs/pro/linux-64 https://repo.anaconda.com/pkgs/pro/noarch package cache : /home/qlunlp/software/anaconda3531/pkgs /home/qlunlp/.conda/pkgs envs directories : /home/qlunlp/software/anaconda3531/envs /home/qlunlp/.conda/envs platform : linux-64 user-agent : conda/4.5.11 requests/2.31.0 CPython/3.7.0 Linux/5.15.0-122-generic ubuntu/20.04 glibc/2.31 UID:GID : 1000:1000 netrc file : None offline mode : False An unexpected error has occurred. Conda has prepared the above report.
03-14
<think>好的,我现在需要帮用户分析他遇到的Conda更新错误。首先,用户执行的命令是`/home/qlunlp/software/anaconda3531/bin/conda update -n base conda -y`,目的是更新base环境中的conda。但出现了错误,系统生成了报告。 首先,我注意到用户的conda版本是4.5.11,发布于2018年,非常老旧。现在最新版本已经超过4.14了,旧版本可能存在兼容性问题,尤其是在处理依赖或新仓库时。这可能就是更新失败的主要原因。 接下来,看看环境变量。PATH里包含了Anaconda的路径,但CONDA_SHLVL为0,可能当前没有激活任何环境。用户可能直接在base环境下操作,但需要确认是否激活正确。不过conda命令可以直接使用绝对路径,激活可能不是问题。 配置文件方面,用户有`/home/qlunlp/.condarc`,里面可能有旧的频道配置。旧版本conda默认的channel可能是旧的`https://repo.continuum.io`,而现在已经改为`conda-forge`或`defaults`,这可能导致无法找到更新包。 再看看channel URLs,里面有很多anaconda.org的链接,还有`https://repo.anaconda.com/pkgs/...`,但有些可能已经失效或不再维护。尤其是pro和free的频道可能已经合并或变更,导致解析失败。 用户使用的Python是3.7.0,而conda新版本可能需要更高版本的Python,这可能在升级过程中产生冲突。需要先升级conda的元数据,可能需要分步骤进行。 另外,用户的环境变量中LD_LIBRARY_PATH指向了paddle_env的环境,可能干扰了当前base环境的库路径,导致依赖解析出错。建议在更新时暂时清空这些变量,或者确保没有其他环境干扰。 解决方案方面,首先建议用户手动更新conda。因为自动更新失败,可以下载最新安装脚本,例如使用`curl`或`wget`获取Miniconda安装脚本,然后进行本地安装替换旧版本。这样可以绕过旧版本conda自身的升级问题。 另外,检查.condarc文件,确保频道设置正确,比如换成`defaults`或`conda-forge`,并删除不再使用的旧频道。然后尝试更新前先运行`conda clean -all`清理缓存,再执行更新命令。 如果用户担心数据丢失,建议备份当前环境,导出环境列表。但直接覆盖安装可能更简单有效,尤其是在版本跨度大的情况下。 最后,提醒用户注意权限问题,确保Anaconda目录可写,以及网络连接正常,没有代理或SSL证书问题。用户提供的SSL_CERT_FILE指向系统路径,可能没问题,但可以尝试更新证书库。</think>让我们逐步分析您遇到的conda更新问题及解决方案: $\texttt{问题分析}$: 1. 版本过低导致:当前conda版本为4.5.11(发布于2018年),与新版本仓库存在兼容性问题 2. 依赖冲突:旧版Python 3.7.0可能与新conda的依赖要求不匹配 3. 仓库配置问题:配置文件中包含的旧仓库URL可能已失效 4. 环境干扰:LD_LIBRARY_PATH指向其他环境的库路径可能引发冲突 $\texttt{解决方案}$: 1. 分步升级: ```bash # 先升级到4.6.x版本 /home/qlunlp/software/anaconda3531/bin/conda install -n base conda=4.6 -y # 再逐步升级到最新 conda update -n base conda ``` 2. 手动更新(推荐): ```bash # 下载最新安装程序 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh # 覆盖安装(保留已有环境) bash Miniconda3-latest-Linux-x86_64.sh -u -b -p /home/qlunlp/software/anaconda3531 ``` 3. 配置修正: 修改$\texttt{~/.condarc}$文件为: ```yaml channels: - defaults auto_update_conda: true ssl_verify: true ``` $\texttt{注意事项}$: 1. 执行前建议备份环境: ```bash conda list --explicit > environment.txt ``` 2. 确保有写权限:检查Anaconda目录权限 ```bash ls -ld /home/qlunlp/software/anaconda3531 ``` 3. 更新完成后验证: ```bash conda --version # 应显示最新版本(如24.x.x) conda list --revisions # 查看变更历史 ``` $\texttt{补充说明}$: 如果遇到SSL证书问题,可临时设置: ```bash export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt ``` 再执行更新命令。完成后建议将证书路径写入$\texttt{.condarc}$配置文件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜流冰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值