带字母表的 for 循环

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语法。

  • @denski 如果您想编写可以在任何类 Unix 操作系统上由 `/bin/sh` 运行的可移植脚本,那么您将无法使用数组。Bash(和其他一些 shell)添加了它们是因为它们非常方便,并且不能总是容易地被更便携的代码替换。但是,特别是对于您的脚本,您可以毫无困难地完成它,而且无需使用任何 bash 特定的功能。你对如何做到这一点感兴趣吗? (3认同)

Eli*_*gan 10

您的脚本使用了 Bash shell 的三个功能,并非所有 Bourne 风格的 shell 都提供这些功能。正如heemayl 所说,您可以简单地使用bash而不是运行该脚本sh。您hashbang顶部(行#!/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
Run Code Online (Sandbox Code Playgroud)

这对于大多数类 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 ]