den*_*ski 12 bash scripts syntax
这在 OSX 上完美运行
#!/bin/bash
chars=( {a..z} )
n=3
for ((i=0; i<n; i++))
do
echo "${chars[i]}"
done
Run Code Online (Sandbox Code Playgroud)
但是当我在 Ubuntu 上运行它时,出现以下错误。
ForLoopAlphabetTest.sh: 2: ForLoopAlphabetTest.sh: Syntax error: "(" unexpected
Run Code Online (Sandbox Code Playgroud)
我似乎无法解决问题。有什么建议?
hee*_*ayl 25
据推测,您正在运行脚本:
sh ForLoopAlphabetTest.sh
Run Code Online (Sandbox Code Playgroud)
在 Ubuntu 中,sh符号链接为dash; 由于dash没有数组的概念,您会收到(.
该脚本在 上完美运行bash,因此如果您将其作为bash的参数运行就可以了:
bash ForLoopAlphabetTest.sh
Run Code Online (Sandbox Code Playgroud)
现在,您bash在脚本上有了shebang,因此您可以使脚本可执行 ( chmod u+x ForLoopAlphabetTest.sh),并将其运行为:
/path/to/ForLoopAlphabetTest.sh
Run Code Online (Sandbox Code Playgroud)
或从脚本的目录:
./ForLoopAlphabetTest.sh
Run Code Online (Sandbox Code Playgroud)
另请注意,您的脚本包含大括号扩展{a..z}和 C 风格的for构造:for (( ... ))它们也不被dash;支持。所以如果你的目标是可移植性,你应该只看 POSIXsh语法。
Eli*_*gan 10
您的脚本使用了 Bash shell 的三个功能,并非所有 Bourne 风格的 shell 都提供这些功能。正如heemayl 所说,您可以简单地使用bash而不是运行该脚本sh。您hashbang顶部(行#!/bin/bash)指定bash,如果你,但唯一有效执行脚本,如heemayl解释。如果您将脚本名称传递给sh,sh则不会自动调用bash,而只会运行脚本。这是因为一旦您的脚本实际运行, hashbang 行就无效了。
如果您需要编写不依赖于 Bash 功能的完全可移植的脚本,您的另一种选择是更改您的脚本,使其在没有它们的情况下也能工作。您使用的 Bash 功能是:
( {a..z} )您分配给的括号表达式chars创建一个数组,并且${chars[i]}出现在您的循环中的索引到它。{a..z}扩展为a b c d e f g h i j k l m n o p q r s t u v w x y z. 但是,这不是 Bourne 风格的 shell 的通用(或标准化)功能,并且 Dash 不支持它。for循环语法。尽管基于算术扩展,它本身并不是 Bash 特有的(尽管一些非常古老的、不符合 POSIX 标准的 shell也没有它),但 C 风格的for循环是Bash 主义的,不能广泛移植到其他贝壳。Bash 广泛可用,尤其是在 Ubuntu 等 GNU/Linux 系统上,并且(如您所见)在 macOS 和许多其他系统上也可用。考虑到您使用了多少特定于 Bash 的功能,您可能只想使用它们,并且只需确保在运行脚本时使用的是 Bash(或其他支持您正在使用的功能的 shell)。
但是,如果您愿意,您可以将它们替换为可移植的结构。数组和C风格的for循环很容易替换;生成没有大括号扩展的字母范围(并且没有在脚本中对它们进行硬编码)是一个有点棘手的部分。
首先,这是一个打印所有小写拉丁字母的脚本:
#!/bin/sh
for i in $(seq 97 122); do
printf "\\$(printf %o $i)\n"
done
Run Code Online (Sandbox Code Playgroud)
seq命令生成数字序列。$( )执行命令替换,因此$(seq 97 122)被替换为 的输出seq 97 122。这些是字符代码为a过z。printf命令可以将字符代码转换为字母(例如,printf '\141'prints a,后跟一个换行符),但代码必须是八进制的,而seq输出只能是十进制的。所以我使用了printf两次:内部printf %o $i将十进制数(由 提供seq)转换为八进制,并替换为外部printf命令。(虽然也可以使用hexadecimal,但它并不简单,而且似乎不那么便携。)printf将\后跟八进制数解释为具有该代码的字符,并\n作为换行符。但是外壳也\用作转义字符。一个\在前面$将防止$从引起膨胀发生(在这种情况下,命令替换),但我不想阻止,所以我与另一个逃脱它\; 这就是原因\\。\前面的第二个n不需要转义,因为与双引号字符串中的 shell不同\$,\n它没有特殊含义。'这对于大多数类 Unix 系统是可移植的,并且不取决于您使用哪种 Bourne 风格的 shell。然而,一些类 Unix 系统seq默认没有安装(他们倾向于使用jot,大多数 GNU/Linux 系统默认没有安装)。expr如果需要,您可以使用循环或算术替换来进一步提高可移植性:
#!/bin/sh
i=97
while [ $i -le 122 ]; do
printf "\\$(printf %o $i)\n"
i=$((i + 1))
done
Run Code Online (Sandbox Code Playgroud)
一个使用while-loop用的[命令,继续当唯一的循环$i是在范围内。
您的脚本不是打印整个字母表,而是定义一个变量n并打印第一$n个小写字母。这是您的脚本版本,它不依赖于特定于 Bash 的功能并可在 Dash 上运行,但需要seq:
#!/bin/sh
n=3 start=97
for i in $(seq $start $((start + n - 1))); do
printf "\\$(printf %o $i)\n"
done
Run Code Online (Sandbox Code Playgroud)
调整值会n更改打印的字母数,就像在您的脚本中一样。
这是一个不需要的版本seq:
#!/bin/sh
n=3 i=97 stop=$((i + n))
while [ $i -lt $stop ]; do
printf "\\$(printf %o $i)\n"
i=$((i + 1))
done
Run Code Online (Sandbox Code Playgroud)
在那里,比应该打印的最后一个字母的字符代码高$stop一个,所以我在命令中使用(小于)而不是(小于或等于)。(它也可以用于制作和使用)。-lt-le[stop=$((i + n - 1))[ $i -le $stop ]