【问题标题】:Convert multiline string to array将多行字符串转换为数组
【发布时间】:2014-08-28 23:13:15
【问题描述】:

我有这个脚本:

nmapout=`sudo nmap -sP 10.0.0.0/24`
names=`echo "$nmapout" | grep "MAC" | grep -o '(.\+)'`
echo "$names"

现在$names 变量包含用换行符分隔的字符串:

>_
 (Netgear)
 (Hon Hai Precision Ind. Co.)
 (Apple)

我尝试用子串的方式进行数组转换:

names=(${names//\\n/ })
echo "${names[@]}"

但问题是,如果我运行此循环,我无法通过索引(即${names[$i] 等)访问它们

for (( i=0; i<${#names[@]}; i++ ))
do
     echo "$i: ${names[$i]"
     # do some processing with ${names[$i]}
done

我得到这个输出:

>_
 0: (Netgear)
 1: (Hon
 2: Hai

但我想要的是:

>_
 0: (Netgear)
 1: (Hon Hai Precision Ind. Co.)
 2: (Apple)

我想不出一个好的方法来做到这一点,请注意第二个字符串中有空格。

【问题讨论】:

  • 你为什么想要数组?我更喜欢通过循环使用read
  • @kan ,其实这是一个大脚本的一小部分,原来的脚本将索引用于其他目的,这就是我要保留数组的原因。
  • 相关:如何将 空格分隔 字符串转换为 bash 数组:Reading a delimited string into an array in Bash

标签: arrays bash newline


【解决方案1】:

在@HariBharathi 答案中添加需要 null byte 分隔符

#!/bin/bash

IFS=$'\n' read -r -d '' -a array <<< "$names"

备注:mapfile/readarray不同,这个兼容macOS bash 3.2

【讨论】:

    【解决方案2】:

    设置IFS(内部字段分隔符)。 Shell 使用IFS 变量来确定字段分隔符是什么。默认情况下,IFS 设置为 空格字符。将其更改为 换行符 字符,如下所示:

    #!/bin/bash
    names="Netgear
    Hon Hai Precision Ind. Co.
    Apple"
        
    SAVEIFS=$IFS   # Save current IFS (Internal Field Separator)
    IFS=$'\n'      # Change IFS to newline char
    names=($names) # split the `names` string into an array by the same name
    IFS=$SAVEIFS   # Restore original IFS
    
    for (( i=0; i<${#names[@]}; i++ ))
    do
        echo "$i: ${names[$i]}"
    done
    

    输出

    0: Netgear
    1: Hon Hai Precision Ind. Co.
    2: Apple
    

    【讨论】:

    • 因为原始$IFS中可能有特殊字符,所以最好避免尝试存储它。最好用括号将整个内容包裹在一个子shell中。
    • @etheranger ,我是 bash 脚本的新手,您能否详细说明“带括号的子shell”?
    • @ramgorur This manual 解释了所有细节。我在这里建议使用该页面上提到的变量范围。基本上,如果您将一个或多个命令放在(括号)中,它们的行为有点像 bash 的新实例。它们从运行脚本继承变量和设置,但通常不会将更改传播回去。
    • 如果我以非 root 身份执行,我会得到 Syntax error: "(" unexpected
    • 您可以在第 9 行使用 IFS=$'\n' names=(${names}) 将 IFS 更改为仅一行。这与加入第 8 行和第 9 行相同。
    【解决方案3】:

    Bash 还有一个readarray 内置命令,可以在手册页中轻松搜索。它使用换行符 (\n) 作为默认分隔符,MAPFILE 作为默认数组,所以可以这样做:

        names="Netgear
        Hon Hai Precision Ind. Co.
        Apple"
    
        readarray -t <<<$names
    
        printf "0: ${MAPFILE[0]}\n1: ${MAPFILE[1]}\n2: ${MAPFILE[2]}\n"
    

    -t 选项删除分隔符 ('\n'),以便可以在 printf 中显式添加。输出是:

        0: Netgear
        1: Hon Hai Precision Ind. Co.
        2: Apple
    

    【讨论】:

    • 这是对所提问题的正确答案。 readarray 正是为此而设计的
    • 这确实是具体问题的正确答案。
    • readarray 是在 bash v4.0 中引入的。 macOS IFS 的解决方案。
    【解决方案4】:

    正如其他人所说,IFS 会帮助你。IFS=$'\n' read -ra array &lt;&lt;&lt; "$names" 如果您的变量有带空格的字符串,请将其放在双引号之间。 现在您可以通过${array[@]} 轻松获取数组中的所有值

    【讨论】:

    • 默认情况下read使用\n作为分隔符,所以必须在read命令中放入-d '',否则数组只包含$names的第一行。更正版本:IFS=$'\n' read -r -d '' -a array &lt;&lt;&lt; "$names"。你也忘了在{前面加上$
    • 我是新手,能否详细说明此命令中的-r-a 用法
    • 我有点困惑。您已经在初始答案中使用了-r-a,只是缩写为-ra。在我的评论中,我添加了-d ''。 bash 手册页很好地解释了所有这些命令行选项(查找 read 内置命令)。
    【解决方案5】:

    让我为Sanket Parmar's answer 做出贡献。如果可以将字符串拆分和处理提取到单独的函数中,则无需保存和恢复$IFS — 改为使用local

    #!/bin/bash
    
    function print_with_line_numbers {
        local IFS=$'\n'
        local lines=($1)
        local i
        for (( i=0; i<${#lines[@]}; i++ )) ; do
            echo "$i: ${lines[$i]}"
        done
    }
    
    names="Netgear
    Hon Hai Precision Ind. Co.
    Apple"
    
    print_with_line_numbers "$names"
    

    另见:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-08
      • 2019-02-13
      • 2012-11-17
      • 2023-03-07
      相关资源
      最近更新 更多