22

根据hyperpolyglot.org 上的这个参考表,可以使用以下语法来设置数组。

i=(1 2 3)

但是我收到一个错误,破折号是 Ubuntu 上的默认设置/bin/sh,应该符合 POSIX。

# Trying the syntax with dash in my terminal
> dash -i
$ i=(1 2 3)
dash: 1: Syntax error: "(" unexpected
$ exit

# Working fine with bash
> bash -i
$ i=(1 2 3)
$ echo ${i[@]}
1 2 3
$ exit

参考表是否具有误导性或错误性?
如果是,那么定义数组或列表并符合 POSIX 的正确方法是什么?

4

3 回答 3

24

Posix 不指定数组,因此如果您受限于 Posix shell 功能,则不能使用数组。

恐怕你的参考是错误的。可悲的是,并非您在互联网上找到的所有内容都是正确的。

于 2016-02-13T22:16:44.637 回答
19

正如rici 所说,dash 没有数组支持。但是,如果您要编写一个循环,则有一些解决方法。

For 循环不会执行数组,但您可以使用 while 循环 + read 内置进行拆分。由于内置的​​破折号读取也不支持分隔符,因此您也必须解决这个问题。

这是一个示例脚本:

myArray="a b c d"

echo "$myArray" | tr ' ' '\n' | while read item; do
  # use '$item'
  echo $item
done

对此有一些更深入的解释:

  • tr ' ' '\n'将让您在删除空格并添加换行符的地方进行单字符替换 - 这是 read 内置函数的默认分隔符。

  • read当它检测到标准输入已关闭时,将以失败的退出代码退出 - 这将是您的输入已被完全处理时。

  • 由于 echo 在输入后打印了一个额外的换行符,因此您可以处理数组中的最后一个“元素”。

这相当于 bash 代码:

myArray=(a b c d)

for item in ${myArray[@]}; do
  echo $item
done

如果要检索第 n 个元素(出于示例的目的,假设为第 2 个):

myArray="a b c d"

echo $myArray | cut -d\  -f2 # change -f2 to -fn
于 2017-06-14T19:38:58.090 回答
13

确实,POSIX shshell 没有与其他 shell 相同的命名数组bash,但是有一个shell(以及其他 shell)可以使用的列表,这就是位置参数列表shbash

此列表通常包含传递给当前脚本或 shell 函数的参数,但您可以使用set内置命令设置其值:

#!/bin/sh

set -- this is "a list" of "several strings"

在上面的脚本中,位置参数$1, $2, ... 设置为显示的五个字符串。--用于确保您不会意外设置 shell 选项(该命令set也可以这样做)。-如果第一个参数以虽然开头,这只是一个问题。

例如,循环这些字符串,您可以使用

for string in "$@"; do
    printf 'Got the string "%s"\n' "$string"
done

或更短的

for string do
    printf 'Got the string "%s"\n' "$string"
done

要不就

printf 'Got the string "%s"\n' "$@"

set对于将 glob 扩展为路径名列表也很有用:

#!/bin/sh

set -- "$HOME"/*/

# "visible directory" below really means "visible directory, or visible 
# symbolic link to a directory".

if [ ! -d "$1" ]; then
    echo 'You do not have any visible directories in your home directory'
else
    printf 'There are %d visible directories in your home directory\n' "$#"

    echo 'These are:'
    printf '\t%s\n' "$@"
fi

shift内置命令可用于从列表中移出第一个位置参数。

#!/bin/sh

# pathnames
set -- path/name/1 path/name/2 some/other/pathname

# insert "--exclude=" in front of each
for pathname do
    shift
    set -- "$@" --exclude="$pathname"
done

# call some command with our list of command line options
some_command "$@"

于 2019-12-16T09:38:41.870 回答