这在OSX上完美运行
#!/bin/bash
chars=( {a..z} )
n=3
for ((i=0; i<n; i++))
do
echo "${chars[i]}"
done
但是,当我在Ubuntu上运行它时,出现以下错误。
ForLoopAlphabetTest.sh: 2: ForLoopAlphabetTest.sh: Syntax error: "(" unexpected
我似乎无法解决问题。有什么建议么?
这在OSX上完美运行
#!/bin/bash
chars=( {a..z} )
n=3
for ((i=0; i<n; i++))
do
echo "${chars[i]}"
done
但是,当我在Ubuntu上运行它时,出现以下错误。
ForLoopAlphabetTest.sh: 2: ForLoopAlphabetTest.sh: Syntax error: "(" unexpected
我似乎无法解决问题。有什么建议么?
Answers:
大概您正在以以下方式运行脚本:
sh ForLoopAlphabetTest.sh
在Ubuntu中,sh链接到dash;由于dash没有数组的概念,因此您得到的语法错误(。
该脚本可在上完美运行bash,因此如果将其作为bash参数运行就可以了:
bash ForLoopAlphabetTest.sh
现在,您bash在脚本上有了shebang,因此可以使脚本可执行(chmod u+x ForLoopAlphabetTest.sh),并以以下方式运行它:
/path/to/ForLoopAlphabetTest.sh
或从脚本目录中:
./ForLoopAlphabetTest.sh
另请注意,您的脚本包含大括号扩展名{a..z}和C样式for构造:;for (( ... ))也不支持dash;因此,如果您的目标是可移植性,则应sh仅查看POSIX 语法。
/bin/sh在任何类Unix操作系统上运行的可移植脚本,那么您将无法使用数组。Bash(和其他一些shell)已经添加了它们,因为它们非常方便并且不能始终被更易移植的代码轻松替换。但是,特别是对于您的脚本,无需使用任何特定于bash的功能,就可以轻松完成此操作。您对该怎么做感兴趣?
sh
您的脚本使用了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的外壳也没有),但是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
seq命令生成数字序列。$( )执行命令替换,因此$(seq 97 122)被替换为输出seq 97 122。这些是字符代码为a过z。printf命令可以将字符代码转换为字母(例如,printf '\141'印刷品a,后跟换行符),但是代码必须为八进制,而seq输出只能为十进制。因此,我使用了printf两次:内部printf %o $i将十进制数(由提供seq)转换为八进制,并替换为外部printf命令。(尽管也可以使用十六进制,但这并不简单,并且似乎不太方便。)printf将\八进制数字解释为带有该代码的字符和\n换行符。但是外壳程序还\用作转义符。一个\在前面$将防止$从引起膨胀发生(在这种情况下,命令替换),但我不想阻止,所以我与另一个逃脱它\; 这就是的原因\\。不需要转义第二个\,n因为与不同\$,\n对于双引号字符串中的shell没有特殊含义。'它可以移植到大多数类似Unix的系统上,而不取决于您使用哪种Bourne风格的shell。但是,seq默认情况下未安装一些类似Unix的系统(它们倾向于使用jot,大多数GNU / Linux系统默认未安装)。如果需要,可以使用带有expr或算术替换的循环来进一步提高可移植性:
#!/bin/sh
i=97
while [ $i -le 122 ]; do
printf "\\$(printf %o $i)\n"
i=$((i + 1))
done
一个使用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
调整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
那里比应该打印的最后一个字母的字符代码高$stop一个,因此我在命令中使用(小于)而不是(小于或等于)。(它也会在制造和使用上起作用)。-lt-le[stop=$((i + n - 1))[ $i -le $stop ]