这在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 ]