bash:将'\n'分隔的字符串转换为数组

ram*_*rur 17 arrays bash newline

我有这个脚本 -

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

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

>_
 (Netgear)
 (Hon Hai Precision Ind. Co.)
 (Apple)
Run Code Online (Sandbox Code Playgroud)

我尝试用子串方法进行数组转换 -

names=(${names//\\n/ })
echo "${names[@]}"
Run Code Online (Sandbox Code Playgroud)

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

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

我得到这个输出 -

>_
 0: (Netgear)
 1: (Hon
 2: Hai
Run Code Online (Sandbox Code Playgroud)

但我想要的是 -

>_
 0: (Netgear)
 1: (Hon Hai Precision Ind. Co.)
 2: (Apple)
Run Code Online (Sandbox Code Playgroud)

我无法找到一个好方法,请注意第二个字符串中有空格.

任何的想法 ?

小智 27

设置IFS.Shell使用IFS变量来确定字段分隔符.默认情况下IFS设置为空格字符.将其更改为换行符.

#!/bin/bash
names="Netgear
Hon Hai Precision Ind. Co.
Apple"

SAVEIFS=$IFS   # Save current IFS
IFS=$'\n'      # Change IFS to new line
names=($names) # split to array $names
IFS=$SAVEIFS   # Restore IFS

for (( i=0; i<${#names[@]}; i++ ))
do
    echo "$i: ${names[$i]}"
done
Run Code Online (Sandbox Code Playgroud)

产量

0: Netgear
1: Hon Hai Precision Ind. Co.
2: Apple
Run Code Online (Sandbox Code Playgroud)

  • 因为原始`$ IFS`中可能有特殊字符,所以最好避免尝试存储它.最好用括号将整个事物包装在子shell中. (3认同)
  • 您可以在第9行使用IFS = $'\ n'names =($ {names})更改一行的IFS,这与将第8行和第9行联接在一起是相同的。 (2认同)

Ale*_*Ale 19

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"
Run Code Online (Sandbox Code Playgroud)

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

    0: Netgear
    1: Hon Hai Precision Ind. Co.
    2: Apple
Run Code Online (Sandbox Code Playgroud)

  • `readarray` 是在 bash v4.0 中引入的。某些系统(例如 macOS &lt;11.*)仍使用 bash v3.2。在这种情况下,可以使用基于“IFS”的解决方案。 (4认同)
  • 这是所提问题的正确答案。`readarray` 就是为了做到这一点而设计的 (2认同)

fir*_*iku 11

让我为Sanket Parmar的回答做出贡献.如果您可以将字符串拆分和处理提取到单独的函数中,则无需保存和恢复$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"
Run Code Online (Sandbox Code Playgroud)

也可以看看:


Gab*_*les 8

如何将多行字符串读入常规 bash“索引”数组

Bashshellcheck静态代码分析器和检查器工具建议在 SC2206 中使用read -rmapfile。他们的mapfile示例是完整的,但他们的read示例仅涵盖了按空格而不是换行符分割字符串的情况,因此我从@Toni Dietze 的评论中read了解了用于此目的的命令的完整形式。

因此,以下是如何使用两者来按换行符分割字符串。请注意,这<<<称为“herestring”。它类似于“ <<heredoc ,并且<读取文件:

# split the multiline string stored in variable `var` by newlines, and
# store it into array `myarray`

# Option 1
# - this technique will KEEP empty lines as elements in the array!
# ie: you may end up with some elements being **empty strings**!
mapfile -t myarray <<< "$multiline_string"

# OR: Option 2 [my preference]
# - this technique will NOT keep empty lines as elements in the array!
# ie: you will NOT end up with any elements which are empty strings!
IFS=$'\n' read -r -d '' -a myarray <<< "$multiline_string"
Run Code Online (Sandbox Code Playgroud)

还有我最常使用的第三种技术,它不一定是shellcheck推荐的,但如果你正确使用它,它就很好,并且比上面的任何一个选项都更具可读性。eRCaGuy_dotfiles/useful_scripts我在我的目录中的许多脚本中使用它。克隆该存储库并grep -rn "IFS"在其中运行以查找我使用该技术的所有位置。

请参阅此处,了解我第一次了解到这一点的地方:@Sanket Parmar 的回答:将多行字符串转换为数组

这里是:

# Option 3 [not necessarily recommended by shellcheck perhaps, since you must
# NOT use quotes around the right-hand variable, but it is **much
# easier to read**, and one I very commonly use!]
#
# Convert any multi-line string to an "indexed array" of elements:
#
# See:
# 1. "eRCaGuy_dotfiles/useful_scripts/find_and_replace.sh" for an example 
#    of this.
# 1. *****where I first learned it: https://stackoverflow.com/a/24628676/4561887
SAVEIFS=$IFS   # Save current IFS (Internal Field Separator).
IFS=$'\n'      # Change IFS (Internal Field Separator) to the newline char.
# Split a long string into a bash "indexed array" (via the parenthesis),
# separating by IFS (newline chars); notice that you must intentionally NOT use
# quotes around the parenthesis and variable here for this to work!
myarray=($multiline_string) 
IFS=$SAVEIFS   # Restore IFS
Run Code Online (Sandbox Code Playgroud)

也可以看看:

  1. 我在上面学到了“选项 3”:@Sanket Parmar 在这里回答:Convert multiline string to array
  2. 将文件读入包含空行的数组
  3. 我使用 cmd 将 bash 多行字符串读入 bash 数组的示例read查找目录中非目录本身的所有文件


Har*_*thi 5

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

  • 默认情况下,`read` 使用`\n` 作为分隔符,所以你必须在`read` 命令中放置`-d ''`,否则数组只包含`$names` 的第一行。更正版本:`IFS=$'\n' read -r -d '' -a array &lt;&lt;&lt; "$names"`。你还忘记在 `{` 前面放一个 `$`。 (5认同)