使用内置函数来增强性能是否会因无偿使用子外壳而被否定?

Is using builtins to enhance performance negated by gratuitous use of subshells?

我正在编写一个脚本,我需要在其中确保字符串包含逗号。如果没有,我需要脚本退出。考虑以下内容,我的意图是仅使用内置函数来提高性能:

#!/bin/sh

check_for_commas='This string must contain a comma'

comma_found='0'
iterate_through_string="$check_for_commas"
while [ -n "$iterate_through_string" ]; do
    char="$(printf '%.1s' "$iterate_through_string")"

    if [ "$char" = ',' ]; then
        comma_found='1'
        break
    fi

    iterate_through_string="${iterate_through_string#?}"
done

if [ "$comma_found" != '1' ]; then
    echo 'Your string does not contain a comma. Exiting...'
    exit
else
    echo 'Found a comma in the string. Script can continue...'
fi

我在这个脚本中使用命令替换,它为它迭代的每个字符生成一个子 shell。与此比较:

#!/bin/sh

check_for_commas='This string must contain a comma'

if [ "$(echo "$check_for_commas" | grep -q -F ','; echo "$?")" = '1' ]; then   
    echo 'Your string does not contain a comma. Exiting...'
    exit
else
    echo 'Found a comma in the string. Script can continue...'
fi

我显然不介意做一些额外的工作来获得额外的性能。但我担心使用这么多子外壳已经违背了我的全部初衷。

当无偿使用 subshel​​l 出现时,我对仅使用内置函数来提高性能的追求是否变得毫无用处?

命令替换,如 $(printf ...) 中的那样,确实很昂贵 -- 并且您在这里所做的事情不需要它们

case $check_for_commas in
  *,*) echo "Found a comma in the string";;
  *)   echo "No commas present; exiting"; exit 1;;
esac

在更一般的情况下——单独的 fork() 成本低于一对 fork()/execve(),因此拥有一个子 shell 比单个 external-command 调用更便宜;但是如果你比较一个生成多个子 shell 的循环与一个 external-command 调用,哪个更便宜取决于你的循环将迭代多少次(以及这些东西在你的操作系统上的成本是多少——forks 是例如,在 Windows 上传统上非常昂贵),因此是一项 fact-intensive 调查。 :)

(谈到最初提出的代码——请注意,ksh93 将在特定的 var=$(printf ...) 情况下优化掉 fork;相比之下,在 bash 中,您需要使用printf -v var ...得到相同的效果)。

这是一个简短的 POSIX shell 函数,它使用组合的 删除匹配前缀模式 删除匹配的后缀模式,和test,(或者更确切地说[是一回事),到return a true 标记是否有逗号:

chkcomma(){ [ "${1#${1%,*}}" ] ; }

没有逗号的例子:

chkcomma foo && echo comma found || echo no comma

输出:

no comma

逗号示例:

chkcomma foo,bar && echo comma found || echo no comma

输出:

comma found

这可以进一步抽象为使用 globbing:

查找子字符串
# Usage: instr globpattern string
# returns true if found, false if not.
instr(){ [ "${2#${2%*}}" ] ; }

示例:

instr '[Mm]oo' mood && echo found

输出:

found