为什么 shell 脚本中有三种设置变量的语法?

为什么 shell 脚本中有三种设置变量的语法?
root@ubuntu:~# echo ${x=1}
1
root@ubuntu:~# echo ${x:=1}
1
root@ubuntu:~# echo ${x-1}
1
root@ubuntu:~# 

为什么 shell 脚本中有三种设置变量的语法?

拥有三种语法的技术优势是什么?

即使是编程语言也不会超过一种。

答案1

它们做的事情略有不同。事实上,最后一个echo ${x-1}实际上并没有设置X但仅在以下情况下才替换表达式中的值 1X未设置。

x=1另一方面设置X无条件地。

至于 := 运算符。这是来自克什手动的:

   ${parameter:=word}
          If  parameter is not set or is null then set it to word; the
          value of the parameter is then substituted.  Positional  parameters may 
          not be assigned to in this way.

在 Ruby 中,这就像||=.

如果你看看这个家庭X= 运算符还有很多,:-这是我遇到过的最流行的。那个替代品如果尚未设置变量,则将变量设置为默认值。所以你可以这样使用它:

x=${1:-10}

其英文为:如果 $1 未设置,则将 10 分配给X,否则分配给X1 美元的价值。在函数中,这具有为参数分配默认值的效果。所以

f() {
  typeset x
  x=${1-10}
  ...
}

相当于Python:

def f(x=10): 

我认为大卫·科恩(David Korn)会承认运营商的多样性可能有些过分,但它现在已经成为了POSIX 标准,第 2.6.2 节所以它可能会留在那里。

即使是编程语言也不会超过一种。

正如上面提到的,有一个误解,认为这些是确切地相同。在编程语言中,赋值语句的变体并不罕见。我||=在 Ruby 中提到过。许多语言都有+=-=等等。在 Perl 中,如果你运行$x 之前没有被定义$x += 1,它将设置$x为 1(而且,如果你没有启用“严格”检查,那真是太遗憾了)。

答案2

我认为“为什么有这么多 shell 语法来分配变量?”这个问题的唯一答案。是“因为他们所做的事情都略有不同。”

在shell编程语言中,$x或者${x}扩展到名为 的 shell 变量的值x。换句话说,当 shell 解释包含 的行时$x,它会替换$x为变量的值:

$ x=me; filepath=/home/$x
$ echo $filepath
/home/me

shell 还实现了各种条件扩展,包括具有副作用的扩展:

条件作业

在替换之前有条件地进行分配:

  • ${x=1}x如果之前未设置,则设置为 1。
  • ${x:=1}x如果之前未设置或设置为空字符串,则设置为 1。

例子:

$ unset x
$ echo Hello, "${x=world}"
Hello, world
$ echo "$x"
world

$ # This sets x to the empty string
$ x=
$ echo Hello, "${x=world}"
Hello,
$ echo "$x"

$ echo Hello, "${x:=world}"
Hello, world
$ echo "$x"
world

条件扩展

扩展是变量的值或某个固定字符串。变量是不是改变了。

  • ${x-1}展开为${x}ifx已设置。否则,扩展为1.
  • ${x:-1}${x}如果x已设置且不为空,则扩展为。否则,扩展为1.
  • ${x+1}展开为1ifx已设置。否则,扩展为空字符串。
  • ${x:+1}1如果x已设置且不为空,则扩展为。否则,扩展为空字符串。

例子:

$ unset x
$ echo Hello, "${x-world}"
Hello, world
$ echo "$x"

$ # This sets x to the empty string
$ x=
$ echo Hello, "${x-world}"
Hello,
$ echo Hello, "${x:-world}"
Hello, world
$ echo "$x"

条件错误

  • ${x?error message}x如果未设置,则失败并打印错误消息。
  • ${x:?error message}x如果未设置或设置为空字符串,则会失败并打印错误消息。

如果失败,则不会执行发生扩展的命令。如果 shell 不是交互式的,那么 shell 本身将退出。

冒号

在所有条件语法中,可以看出使用 a:使未设置的变量和设置为空字符串的变量以相同的方式起作用。这通常是个好主意,因为有时取消设置变量并不容易。例如,在上面显示的局部赋值语法中,将子变量分配给空字符串很容易,但从子环境中删除变量却不容易:

$ export foo=Goodbye
$ foo= dash -c 'echo "${foo-Hello}", world'
, world
$ foo= dash -c 'echo "${foo:-Hello}", world'
Hello, world
$ echo "$foo"
Goodbye

作为命令的条件扩展

有时,仅能够执行条件扩展以消除其副作用,而不是在命令中实际使用它是很方便的。从技术上讲,这是不可能的,但您可以使用特殊的内置命令:,它不执行任何操作:

# Equivalent to:
#  if [ -z "$x" ]; then x="default value"; fi
: "${x:=default value}"
# Equivalent to:
#   if [ $# -lt 3 ]; then
#     echo "Three arguments are required" 1>&2
#     exit 1
#   fi
: "${3?Three arguments are required.}" 

变量赋值

shell 提供了一种简单的语法来为标量变量赋值:

var=value

如果命令仅包含赋值,则赋值将成为 shell 环境的一部分。但是,如果分配是命令行的一部分,位于实用程序/脚本名称之前,则分配将应用于孩子子进程使用的环境:

$ foo=Hello; echo "$foo, world"
Hello, world
$ echo "$foo"
Hello
$ unset foo
$ foo=Hello echo "$foo, world"
, world
$ echo "$foo"

在命令foo=Hello echo "$foo, world", the assignment is performed in the environment passed toecho . However, the argument"$foo, world" is evaluated in the main shell environment, in which$foo has no value. Furthermore, when the child process (echo , in this case) terminates, its environment is lost, so the assignment was useless. Contrast that with the case where the child process actually uses the variable: (dash is a Posix-compatible shell; I'm just using it here for clarity.bash` 中也会以同样的方式工作。)

$ unset foo
$ foo=Hello dash -c 'echo "$foo, world"'
Hello, world
$ echo "$foo"

扩展赋值语法

一些 shell(例如 bash、ksh 和 zsh)也提供语法

var+=value

这增加了value变量。 “添加”的含义取决于变量的类型var: 对于普通字符串变量,value附加到 的末尾var;而对于声明为整数的变量,value则(以数学方式)相加。在 ksh、zsh 和某些版本的 bash 中,此赋值语法可作为命令的一部分用于子赋值。有关如何将变量声明为数字的详细信息,请参阅各个 shell 手册。 (这是不是足以简单地为变量分配一个数字。)

这些 shell 还具有数组变量,这些变量使用以下语法进行分配和附加:

array=(value1 value2 ...)
array+=(value3 ...)

上述赋值中的括号是语法性的;您可以将左括号视为赋值标记的一部分。数组赋值不能用作简单命令的一部分,因为数组无法导出到子环境。

某些 shell 具有关联数组,支持索引分配。有关关联数组的详细信息,包括如何声明它们,请参阅各个 shell 手册。

算术评估作业

在算术求值中,可以使用类似 C 的赋值运算符(=*=、等),但它们仅执行算术赋值。 +=Posix shell 只有两个算术求值上下文:算术扩展语法$((...))for ((expr; expr; expr))复合语句。 (您可以使用:上面的特殊内置函数将算术扩展转换为语句。)

然而,大多数 shell 也允许条件语句((...)),并且还会在需要数字的上下文中使用算术求值,例如数组下标和对声明为整数的变量进行赋值。细节有所不同;请参阅 shell 手册。

需要谨慎对待算术求值上下文,因为在算术求值上下文中,变量扩展被重新解释为算术表达式,而不仅仅是整数。在某些 shell(例如 bash)中,可以通过注入攻击来利用这一点。最佳实践是永远不要在算术求值上下文中使用不受信任的变量。

相关内容