AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 490393
Accepted
Kalle Richter
Kalle Richter
Asked: 2018-12-22 12:28:29 +0800 CST2018-12-22 12:28:29 +0800 CST 2018-12-22 12:28:29 +0800 CST

在shell变量比较的两侧添加前缀到字符串文字的目的是什么?

  • 772

多年来,我多次遇到变量与字符串文字的比较,其中有一个字符作为变量和文字的前缀,例如

if [ "x$A" = "xtrue" ]; then

为了检查是否$A是"true".

我认为这样做是为了实现 shell 兼容性或解决长期错误、不直观的行为等。没有什么明显的想法出现在脑海中。

今天我想我想知道原因,但我的研究没有发现任何结果。或者,也许只是我从相当频繁地接触罕见事件中做出了一些事情。

这种做法是否仍然有用,甚至可能是最好的?

shell variable
  • 2 2 个回答
  • 951 Views

2 个回答

  • Voted
  1. Best Answer
    Stéphane Chazelas
    2018-12-23T04:34:38+08:002018-12-23T04:34:38+08:00

    这里要理解的重要一点是,在大多数 shell 中,[它只是一个由 shell 解析的普通命令,就像任何其他普通命令一样。

    然后 shell 调用带有参数列表的[(aka test) 命令,然后将[它们解释为条件表达式。

    那时,这些只是一个字符串列表,并且关于哪些字符串是由某种形式的扩展产生的信息会丢失,即使在那些[内置的 shell 中(这些天都是类似 Bourne 的 shell)。

    该[实用程序过去很难分辨它的哪些参数是运算符,哪些是操作数(运算符工作的东西)。语法本质上是模棱两可的,这并没有帮助。例如:

    • [ -t ]曾经是(并且仍然在某些 shell/ [s 中)来测试 stdout 是否是终端。
    • [ x ]是[ -n x ]: 测试是否x是非空字符串的缩写(所以你可以看到与上面的冲突)。
    • 在某些 shell/ [s 中,-a并且-o可以是一元的([ -a file ]对于可访问的文件(现在替换为[ -e file ]),[ -o option ]是否启用了该选项?)和二元运算符(and和or)。同样,! -a x可以是and(nonempty("!"), nonempty("x"))或not(isaccessible("x"))。
    • (,)并!添加更多问题。

    在 C 或 等普通编程语言perl中,在:

    if ($a eq $b) {...}
    

    $a不可能将or的内容$b作为运算符,因为条件表达式在它们之前被解析$a并被$b扩展。但在贝壳中,在:

    [ "$a" = "$b" ]
    

    shell首先扩展变量²。例如,如果$acontains(和$bcontains ,则)所有[命令看到的是[、(、=和参数。这是否意味着(在词法上相等)或(是非空字符串)。)]"(" = ")"()( -n = )=

    历史实现(test出现在 70 年代末期的 Unix V7 中)过去常常失败,即使只是因为它们处理参数的顺序并不模棱两可。

    在 PDP11 模拟器中使用版本 7 Unix:

    $ ls -l /bin/[
    -rwxr-xr-x 2 bin      2876 Jun  8  1979 /bin/[
    $ [ ! = x ]
    test: argument expected
    $ [ "(" = x ]
    test: argument expected
    

    大多数 shell 和[实现已经或曾经遇到过这些问题或其变体。今天有bash4.4:

    bash-4.4$ a='(' b=-o c=x
    bash-4.4$ [ "$a" = "$b" -o "$a" = "$c" ]
    bash: [: `)' expected, found =
    

    POSIX.2(于 90 年代初发布)设计了一种算法,当以最常见的使用模式(例如仍未指定)传递最多 4 个参数(除了和)时,该算法将使[' 的行为明确和确定。它弃用了, ,和, 并删除了没有操作数。确实在2.0中实现了该算法(或至少尝试过) 。[][ -f "$a" -o "$b" ]()-a-o-tbashbash

    因此,在符合 POSIX 的[实现中,[ "$a" = "$b" ]保证比较内容$a和$b是否相等,无论它们是什么。没有-o,我们会写:

    [ "$a" = "$b" ] || [ "$a" = "$c" ]
    

    也就是说,调用[两次,每次使用少于 5 个参数。

    但是所有[实现都需要相当长的时间才能变得合规。bash's 直到 4.4 才符合要求(尽管最后一个问题是[ '(' ! "$var" ')' ]没有人会在现实生活中真正使用它)

    Solaris 10 和更早版本,/bin/sh它不是 POSIX shell,但 Bourne shell 仍然存在以下问题[ "$a" = "$b" ]:

    $ a='!' b='!'
    $ [ "$a" = "$b" ]
    test: argument expected
    

    [ "x$a" = "x$b" ]由于没有以 .[开头的运算符,因此使用可以解决该问题x。另一种选择是使用case:

    case "$a" in
      "$b") echo same;;
         *) echo different;;
    esac
    

    (引用是必要的$b,而不是围绕$a)。

    无论如何,它不是空值,也从来不是空值。人们在忘记引用变量时会遇到空值问题[,但这不是问题[。

    $ a= b='-o x'
    [ $a = $b ]
    

    默认值为$IFS:

    [ = -o x ]
    

    这是对是否为非空字符串的测试=,x但没有任何前缀会有所帮助³[ x$a = x$b ]仍然会:[ x = x-o x ]这会导致错误,并且可能会变得更糟,包括 DoS 和带有其他值的任意命令注入,例如bash:

    bash-4.4$ a= b='x -o -v a[`uname>&2`]'
    bash-4.4$ [ x$a = x$b ]
    Linux
    

    正确的解决方案是始终引用:

    [ "$a" = "$b" ]   # OK in POSIX compliant [ / shells
    [ "x$a" = "x$b" ] # OK in all Bourne-like shells
    

    请注意,expr有类似(甚至更糟)的问题。

    expr还有一个=运算符,尽管它用于测试两个操作数在看起来像十进制整数时是否为相等的整数,或者在不是时排序相同。

    在许多实现中,expr + = +, or expr '(' = ')'orexpr index = index不进行相等比较。expr "x$a" = "x$b"可以解决它进行字符串比较,但前缀 anx可能会影响排序(例如在具有排序元素的区域设置中x),并且显然不能用于数字比较expr "0$a" = "0$b"不适用于比较负整数。expr " $a" = " $b" 适用于某些实现中的整数比较,但不适用于其他实现(因为a=01 b=1,有些会返回真,有些会返回假)。


    ¹ksh93是一个例外。in ksh93,[可以看作是一个保留字 in ,[ -t ]它实际上不同于var=-t; [ "$var" ], or from ""[ -t ]or cmd='['; "$cmd" -t ]。这是为了保持向后兼容性,并且在重要的情况下仍然符合 POSIX。仅当-t它是文字时才在此处作为运算符,并ksh93检测到您正在调用该[命令。

    ² ksh 添加了一个[[...]]条件表达式运算符,它有自己的语法解析规则(以及它自己的一些问题)来解决这个问题(在其他一些 shell 中也有,但有一些不同)。

    ³ 除非在参数扩展时不调用 split+glob,但zsh仍然可以进行空删除,或者在全局禁用 split+glob 时在其他 shell 中使用set -o noglob; IFS=

    • 17
  2. JdeBP
    2018-12-22T14:25:01+08:002018-12-22T14:25:01+08:00

    人们经常将前缀归因于空字符串的问题,但这不是原因。问题是一个非常简单的问题:变量的扩展可能是test's 运算符之一,突然将二进制相等测试变成了不同的表达式。

    该命令在大多数平台上的最新实现避免了表达式解析器中的前瞻陷阱,防止解析器将二元运算符的第一个操作数识别为操作数以外的任何内容,只要有足够的标记成为二元运营商当然:

    % a=-n
    % /bin/test "$a" = -n ; 回声$?
    0
    % /bin/test "$a" = ; 回声$?
    0
    % /bin/test x"$a" = ; 回声$?
    测试:=:预期参数
    2
    % a='('
    % /bin/test "$a" = "(" ; echo $?
    0
    % /bin/test "$a" = ; 回声$?
    测试:预期收盘价
    2
    %

    • 7

相关问题

  • 有没有办法让 ls 只显示某些目录的隐藏文件?

  • while 语句中的 Bash 或条件

  • grep -v grep 有什么作用

  • 如何将带有〜的路径保存到变量中?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    ssh 无法协商:“找不到匹配的密码”,正在拒绝 cbc

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    如何卸载内核模块“nvidia-drm”?

    • 13 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add 返回:“连接代理时出错:没有这样的文件或目录” 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya 为什么 Linux 使用 LF 作为换行符? 2017-12-20 05:48:21 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve