如何“扩展”bash 变量(包含的代码适用于 bash 但不适用于 zsh)

如何“扩展”bash 变量(包含的代码适用于 bash 但不适用于 zsh)

一个答案如很好地解释了如何控制传球全部变量到命令。

我想探索如何在每个参数的基础上做到这一点。观察(这已在 中进行了测试zsh):

$ program() { echo 1: $1 2: $2 3: $3; }

$ run() { program "$@"; }

$ run2() { echo `run $1`; }

$ run2 'a b' c
1: a b 2: 3:

我想要一种方法来传递第一个参数,它是一个包含空格的字符串,并将该拼接$@样式传递到另一个命令中。例如run2 "a b" c应该产生1: a 2: b 3:

至此,我已经解决了眼前的问题,因为虽然我的测试代码在 zsh 中测试时中断,但一旦实现到实际的 bash 脚本中,它就可以工作。

这确实表明这可能依赖于字符串和参数处理中的一些复杂性,这些不“安全”。因此,这更多的是请求评论以更稳健的方式可靠地实现此行为。

答案1

bash 和 zsh 之间重要的区别在于run2调用的方式run,特别是不加引号的效果$1

  • 在 zsh 中,run $1将“remove-if-empty”运算符应用于$1,即run使用传递给 的第一个参数进行调用run2,但如果第一个参数为run2空(或者如果run2调用时不带参数),则run调用时不带任何参数。争论。
  • 在其他 Bourne 风格的 shell(例如 bash)中,run $1将“split+glob”运算符应用于$1,即将第一个参数 分割为run2空格分隔的块 1,将每个部分解释为通配符模式 2,并替换与一个或多个匹配的每个通配符模式按匹配列表显示更多文件。

因此,在 zsh 中使用参数进行run2 'a b' c调用(参数原封不动地传递),但在 bash 中使用两个参数进行调用(分成以空格分隔的部分)。runa brunab

我想要一种方法来传递第一个参数,它是一个包含空格的字符串,并将该拼接$@样式传递到另一个命令中。例如run2 "a b" c应该产生1: a 2: b 3:

你的描述和你的例子说的是不同的事情。如果要将第一个参数传递给另一个命令,请使用run "$1"确保该参数未拆分。不变地传递参数是"$@".

看来您真正想要做的是将第一个参数分解为run2以空格分隔的块。在 bash 中,您可以通过关闭通配符扩展然后(假设未IFS更改默认值)使用不带引号的扩展来实现此目的。

run2 () ( set -f; run $1; )

echo "$(somecommand)"本质上相当于somecommand在子 shell 中运行,看来这就是您的意思,而不是echo $(somecommand)在命令的输出上应用 split+glob,因此我删除了多余的 echo-command-substitution。)

在 zsh 中,您可以=在参数替换中使用字符来对值执行全局拆分(并且不进行通配)。

run2 () { run $=1; }

zsh 的语法与普通 sh 的语法不兼容。如果您想从 zsh 获取 sh 脚本,您可以使用以下emulate构建:

emulate sh -c '. myscript.sh'

用于emulate ksh模拟 ksh(的某些功能)。这并不模拟所有 bash 功能,但它允许您使用数组。

1更一般地说,基于 的值IFS
²除非已使用 关闭此功能set -f

答案2

欢迎来到引用和引用惊喜的世界。

主要问题是zsh默认情况下不会按 IFS 字符进行拆分。
在那:它与所有其他外壳不同。

为了测试引用如何改变 shell 的工作方式,我们需要代码来测试代码的带引号和未带引号的版本。

您的代码(添加几个变量):

program() { printf '%02d-1: %6s   2: %6s    3: %6s' "$i" "$1" "$2" "$3"; }
runa()  { program  "$@" ; }
run1()  { echo "`runa  $1 $2 $3 `"; }
run1    'a b' c

让我详细阐述一下细节。

假设您创建一个sh指向所在zsh位置的本地链接:

ln -s "/usr/bin/zsh" ./sh

另外,我们假设您将以下脚本复制到文件中so

使用带引号和不带引号的变量重复每个函数的脚本:

program() { printf '%02d-1: %6s   2: %6s    3: %6s' "$i" "$1" "$2" "$3"; }
runa() { program "$@"; }
runb() { program  $@ ; }

run1() { echo "`runa  "$1" "$2" "$3"`"; }
run2() { echo "`runb  "$1" "$2" "$3"`"; }
run3() { echo "`runa  $1 $2 $3`"; }
run4() { echo "`runb  $1 $2 $3`"; }

for i in `seq 4`; do
    run"$i" 'a b' c
done

然后,在执行时,我们会打印出以下内容:

# Any shell (except zsh) results.
01-1:    a b   2:      c    3:
02-1:      a   2:      b    3:      c
03-1:      a   2:      b    3:      c
04-1:      a   2:      b    3:      c

只有第一个运行 (run1)(所有内容都被引用)保持'a b'连接。

然而,zsh 的行为就好像 all 一直被引用一样:

# ZSH results.
01-1:    a b   2:      c    3:
02-1:    a b   2:      c    3:
03-1:    a b   2:      c    3:
04-1:    a b   2:      c    3:

模拟中的 zsh。

zsh如果称为sh或 ,则应该会模拟较旧的 shell ksh
但实际上这并不总是正确的:

$ ./sh ./so   # emulated sh
01-1:    a b   2:      c    3:
02-1:    a b   2:      c    3:
03-1:      a   2:      b    3:      c
04-1:      a   2:      b    3:      c

第二行与任何其他 shell 的第二行不同。

这是个好主意阅读这个问题的答案

相关内容