【问题标题】:Arrays in a POSIX compliant shell符合 POSIX 的 shell 中的数组
【发布时间】:2016-05-24 23:33:34
【问题描述】:

根据this reference sheet on hyperpolyglot.org,可以使用以下语法来设置数组。

i=(1 2 3)

但是 dash 出现错误,这是 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 的正确方法是什么?

【问题讨论】:

  • POSIX 中没有数组。如果你仔细观察,那是指一个literalhyperpolyglot.org 的整个部分只是 flat wrong(可能由 M$ 完成)。
  • 我不明白在这种情况下表格的字面意思是什么,表格的部分称为resizable arrays。但即使不是关于数组,它也应该根据参考表正确执行。但你说得对,重要的是没有数组的概念。
  • 感谢您的澄清,我正在考虑依靠这张纸,然后我会寻找另一个。
  • 哎呀,这是一个可怕的“参考”。它具有非 POSIX function 关键字,魔术 $RANDOMecho -n,推荐 trap exit ERR 而不是更有用的 trap 'exit 1' ERR,并且非常鲁莽地引用。不推荐。

标签: arrays bash posix dash-shell


【解决方案1】:

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

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

【讨论】:

  • 我也读过,但我需要确认。这张表看起来如此详细和精确,以至于我感到困惑
  • @zoom 佛罗里达州沼泽地的供应商也提供详细而准确的调查报告。
  • 是的,但一个常见的混淆是当像bash 这样的 shell 在 POSIX 模式下运行时,例如使用--posix 调用,或#!/bin/sh shebang,它就会悄悄地理解这种非POSIX 主义。
【解决方案2】:

正如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

【讨论】:

  • 谢谢!最后一个代码 sn-p 正是我要找的 :)
  • 这个问题是它试图在一个字符串中存储几个单独的字符串。一旦您想存储包含空格字符的字符串,您将无法正确检索它们。您必须使用除空格以外的其他安全分隔符,然后实现您自己的字符串解析。
  • 如果数组包含文件路径,则任何文件名都可以使用空格,但您可以使用其他符号,例如管道|。因此,您可以尝试使用myArray="a|b|c|d",然后将tr ' ' '\n' 更改为tr '|' '\n',而不是myArray="a b c d"。我测试过,效果很好
  • shellcheck 似乎暗示使用while read -r item; do 来防止任何反斜杠字符的损坏是有益的。
【解决方案3】:

确实,POSIX sh shell 没有 bash 和其他 shell 所具有的命名数组,但是有一个 list sh shell(如bash 和其他人)可以使用,这就是 位置参数列表

此列表通常包含传递给当前脚本或 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 "$@"

【讨论】:

  • 在 POSIX 中有类似 unshift 的东西吗?你能以某种方式使用$@ 作为堆栈吗?
  • @Lassi 在开头添加:set -- "$item" "$@"。最后添加:set -- "$@" "$item"。你不能轻易删除$@ 的最后一个元素,但是你可以用shift 删除第一个元素。堆栈可以通过推送/弹出第一个元素来实现。
猜你喜欢
  • 1970-01-01
  • 2014-03-19
  • 1970-01-01
  • 1970-01-01
  • 2021-09-24
  • 1970-01-01
  • 1970-01-01
  • 2023-02-03
  • 2011-06-12
相关资源
最近更新 更多