字母表的 For 循环

字母表的 For 循环

这在 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 说,您只需使用 来运行该脚本即可bashsh您的哈希邦顶部的行(#!/bin/bash)指定bash,但只有当您执行脚本heemayl 解释。如果您将脚本名称传递给shsh则不会自动调用bash,而只会运行该脚本。这是因为一旦你的脚本实际运行,hashbang 行将不起作用

如果你需要写的话,还有另一种选择完全便携不依赖 Bash 功能的脚本,方法是更改​​脚本,使其在没有这些功能的情况下也能工作。您使用的 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换行符。但贝壳也用作\转义字符。\前面的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 ]

相关内容