这在 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
我似乎无法解决这个问题。有什么建议吗?
答案1
据推测,您正在以如下方式运行脚本:
sh ForLoopAlphabetTest.sh
在 Ubuntu 中,sh
符号链接到dash
;由于dash
没有数组的概念,因此您会收到 的语法错误(
。
该脚本在 上运行完美bash
,因此如果您将其作为 的参数运行,那就没问题了bash
:
bash ForLoopAlphabetTest.sh
现在,您已经获得了bash
该脚本的全部内容,因此您可以使该脚本可执行(chmod u+x ForLoopAlphabetTest.sh
),然后按如下方式运行它:
/path/to/ForLoopAlphabetTest.sh
或者从脚本的目录中:
./ForLoopAlphabetTest.sh
还要注意,您的脚本包含大括号扩展{a..z}
和 C 样式for
构造:for (( ... ))
这些也不受支持dash
;所以如果您的目标是可移植性,您应该只查看 POSIXsh
语法。
答案2
您的脚本使用了 Bash shell 的三个功能,而这些功能并不是所有 Bourne 风格的 shell 都提供的。heemayl 说,您只需使用 来运行该脚本即可bash
。sh
您的哈希邦顶部的行(#!/bin/bash
)指定bash
,但只有当您执行脚本heemayl 解释。如果您将脚本名称传递给sh
,sh
则不会自动调用bash
,而只会运行该脚本。这是因为一旦你的脚本实际运行,hashbang 行将不起作用。
如果你需要写的话,还有另一种选择完全便携不依赖 Bash 功能的脚本,方法是更改脚本,使其在没有这些功能的情况下也能工作。您使用的 Bash 功能包括:
- 一个大批。这是先出现的,所以这就是你尝试运行脚本时产生错误的原因使用 Dash shell
( {a..z} )
。您分配给 的括号表达式chars
会创建一个数组,而出${chars[i]}
现在循环中的 会对该数组进行索引。 - 大括号扩展。在 Bash 以及许多其他 shell 中,
{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 不支持它。 - C 风格替代
for
循环语法.尽管基于算术扩展,即本身并不特定于 Bash(尽管有些非常老旧、不符合 POSIX 标准的 shell也没有),C 风格的for
循环是Bash-ism 并且不能广泛移植到其他 shell 中。
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
换行符。但贝壳也用作\
转义字符。\
前面的A$
将$
阻止导致扩张发生(在这种情况下,命令替换),但我不想阻止这种情况,所以我用另一个 对其进行了转义\
;这就是 的原因。之前的\\
第二个不需要转义,因为与 不同,它在双引号字符串中对 shell 没有特殊含义。\
n
\$
\n
- 有关如何在 shell 编程中使用双引号和反斜杠的更多信息,请参阅国际标准中关于引用的部分。 也可以看看3.1.2 引用在里面Bash 参考手册, 尤其3.1.2.1 转义符和3.1.2.3 双引号. (这里是整个部分,在上下文中。)请注意,单引号(
'
)也是 shell 引用语法的重要组成部分,我只是碰巧没有在该脚本中使用它们。
这是可移植的最多类 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
使用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 ]