当我在 中执行简单的数学运算时#!/bin/sh
,是否会创建一个子 shell?
例如,
addition=$(( 1 + 1 ))
语法会建议使用子 shell,但我找不到任何相关内容
答案1
$(cmd arg)
在子 shell 环境中运行cmd
,其输出(减去尾随换行符)成为扩展的结果。
(cmd arg)
确实在子 shell 中运行,其输出不受影响。
所以与额外的子壳层$((cmd arg))
相同,但它不是。$(cmd arg)
$((...))
是来自 Korn shell 的一种单独的扩展形式。在 Korn shell 中,((arithmetic expression))
计算算术表达式(其语法与 C 的语法非常相似),退出状态反映表达式是否解析为 0 还是非零。
这允许这样的事情:
if ((var < 10)); then
...
fi
这使得它看起来非常相似C
。
$
用于介绍扩展。就像$(cmd)
是一样,(cmd)
除了它扩大的输出cmd
,$((arith))
就像((arith))
除了它扩大算术表达式的求值结果。
POSIX,其sh
主要基于 ksh88,指定$((...))
但未指定((...))
。实际上,在早期的草案中,它是为$[...]
相反,这就是为什么你发现它bash
并zsh
支持$[...]
作为 的替代品$((...))
。
IIRC,POSIX 最初考虑将其指定为的主要原因$[...]
是因为与命令替换内的子 shell$((...))
冲突。$((cmd arg))
您会发现大多数 shell 都正确识别$((echo x; echo y) | (tr xy ab))
为不是算术展开式,但不是$((cmd arg))
.无论如何,$((cmd))
都意味着扩展到$cmd
变量的算术值,而不是扩展到 的输出cmd
。
POSIX规范中的相关文字有:
shell 命令语言的语法对于以“$((”开头的扩展存在歧义,它可以引入算术扩展或以子 shell 开头的命令替换。算术扩展具有优先权;也就是说,shell 应首先确定是否它可以将扩展解析为算术扩展,并且如果确定它无法将扩展解析为算术扩展,则仅当遇到结束时,shell 不需要评估嵌套扩展。如果尚未确定无法将扩展解析为算术扩展,则 shell 应将扩展视为不完整的算术扩展并报告语法错误。符合要求的应用程序应确保它将“$(”和 '( 分开。 ' 在以子 shell 开头的命令替换中分成两个标记(即,用空格分隔它们),例如,包含单个子 shell 的命令替换可以写为:
ksh
也((...))
与嵌套子 shell 冲突。虽然 POSIX 没有指定((...))
,但它确实允许 ksh 的行为。
实际上,在嵌套子 shell 和/或 cmdsubst 时,应确保在括号之间包含空格:
echo "$( (...) )"
( (a; b) | (c;d) )
答案2
这是一个演示不是在子 shell 中运行:您可以修改算术表达式中的变量:
$ x=5; echo "$(( x *= 2 ))"; echo "$x"
10
10
如果它是一个子 shell,echo $x
将输出5
.
使用 shell 算术时,请注意并非所有 shell 都支持前后自增; (当前版本的 Debian 及其衍生版本中的dash
默认值)将解释为而不是递增变量。/bin/sh
$(( ++i ))
$(( +(+i) ))
i