AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 749764
Accepted
Nickotine
Nickotine
Asked: 2023-06-25 09:23:22 +0800 CST2023-06-25 09:23:22 +0800 CST 2023-06-25 09:23:22 +0800 CST

创建并附加到数组,mapfile 与 arr+=(input) 相同,还是我遗漏了什么?

  • 772

mapfile有没有好处超过的情况arr+=(input)?

简单的例子

映射文件数组名称,arr:

mkdir {1,2,3}

mapfile -t arr < <(ls)

declare -p arr

输出:

declare -a arr=([0])="1" [1]="2" [2]="3")

编辑:

更改了以下标题;主体具有y数组名称,但标题具有arr名称,这可能会导致混乱。

y+=(输入)

IFS=$'\n'

y+=($(ls))

declare -p y

输出:

declare -a y=([0])="1" [1]="2" [2]="3")

我认为一个优点mapfile是你不必担心分词。

对于另一种方式,您可以通过设置来避免分词,IFS=$'\n'但对于本例来说,无需担心。

第二个例子似乎更容易写,我错过了什么吗?

bash-array
  • 1 1 个回答
  • 34 Views

1 个回答

  • Voted
  1. Best Answer
    Stéphane Chazelas
    2023-06-25T19:29:14+08:002023-06-25T19:29:14+08:00

    即使在之后,它们也根本不是同一件事IFS=$'\n'。

    特别是在 bash 中(尽管该语法是从 zsh 借用的):

    arr=( $(cmd) )
    

    (arr+=( $(cmd) )将用于将元素追加到数组;因此将与keys=( -1 "${!arr[@]}" ); readarray -tO "$(( ${keys[@]: -1} + 1))" arr < <(cmd)² 进行比较)。

    做:

    1. 在子 shell 中运行cmd,其标准输出在管道的写入端打开。
    2. 同时,父 shell 进程从管道的另一端读取数据并:
      • 删除 NUL 字符和尾随换行符
      • 根据$IFS特殊变量的内容分割结果字符串。对于其中的$IFS空白字符(例如换行符),行为更加复杂:
        • 前导和尾随的被删除(在换行符的情况下,它们已被命令替换删除,如上所示)
        • 一个或多个序列被视为一个分隔符。例如, 的输出printf '\n\n\na\n\n\nb\n\n\n'仅分为两个元素:a和b。
      • 然后,这些单词中的每一个都将受到文件名生成(又名通配符)的影响,其行为受到许多选项的影响,包括noglob, nullglob, failglob, extglob, globasciiranges, glabstar, nocaseglob。这适用于那些包含*、?、[、 和 某些 bash 版本的单词,如果启用,\则更多。extglob
    3. 然后将得到的单词作为元素分配给$arr数组。

    例子:

    bash-5.1$ touch x '\x' '?x' aX $'foo\n\n\n\n*'
    bash-5.1$ IFS=$'\n'
    bash-5.1$ ls | cat
    aX
    foo
    
    
    
    *
    ?x
    \x
    x
    bash-5.1$ arr=( $(ls) )
    bash-5.1$ typeset -p arr
    declare -a arr=([0]="aX" [1]="foo" [2]="aX" [3]=$'foo\n\n\n\n*' [4]="?x" [5]="\\x" [6]="x" [7]="?x" [8]="\\x" [9]="\\x" [10]="x")
    

    正如您所看到的,该$'foo\n\n\n\n*'文件被拆分为foo和*,并*扩展为当前工作目录中的文件列表,这解释了为什么我们同时得到foo和$'foo\n\n\n\n*',同样?x解释了为什么我们得到\x(显示为"\\x") 3 次,因为有\x行的输出与和ls都匹配。*?x

    使用 bash 5.0,我们得到:

    bash-5.0$ arr=( $(ls) )
    bash-5.0$ typeset -p arr
    declare -a arr=([0]="aX" [1]="foo" [2]="aX" [3]=$'foo\n\n\n\n*' [4]="?x" [5]="\\x" [6]="x" [7]="?x" [8]="\\x" [9]="x" [10]="x")
    

    在该版本中,反斜杠只有\x两次但x三倍,即使后面没有跟随一个通配符,反斜杠也是一个通配符,因此\x作为通配符匹配x。

    之后shopt nocaseglob,我们得到:

    bash-5.1$ shopt -s nocaseglob
    bash-5.1$ arr=( $(ls) )
    bash-5.1$ typeset -p arr
    declare -a arr=([0]="aX" [1]="foo" [2]="aX" [3]=$'foo\n\n\n\n*' [4]="?x" [5]="\\x" [6]="x" [7]="aX" [8]="?x" [9]="\\x" [10]="\\x" [11]="x")
    

    aX显示 3 次,因为它也?x匹配。

    之后shopt -s failglob:

    bash-5.0$ shopt -s failglob
    bash-5.0$ arr=( $(printf '\\z\n') )
    bash: no match: \z
    bash-5.0$ arr=( $(printf 'WTF\n?') )
    bash: no match: WTF?
    

    和arr=( $(echo '/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*') )

    使系统无法使用几分钟后内存不足。

    因此,总而言之,IFS=$'\n'; arr=( $(cmd) )不会将 的输出行存储cmd在数组中,而是将其输出的非空行扩展所产生的文件名cmd视为 glob 模式。


    使用mapfile或较少误导性的readarray别名:

    readarray -t arr < <(cmd)
    
    1. 如上所述,cmd在子 shell 中运行,其标准输出在管道的写入端打开。
    2. the<(...)被扩展为类似/dev/fd/63或/proc/self/fd/63where 63is a file descriptor of theparent shell open on the read end of the pipeline.
    3. 使用 的<重定向缩写0<,打开 /dev/fd/63 以在 fd 0 上读取,这意味着 的 stdinreadarray也将是该管道的读取端。
    4. readarray从该管道读取每一行(同时cmd写入它),丢弃行分隔符 ( -t),并将其存储在$arr.

    所以最后$arr,假设cmd输出没有 NUL 将包含输出的每一行的内容cmd,无论它们是否为空或是否包含全局字符。

    以上面的例子:

    bash-5.1$ readarray -t arr < <(ls)
    bash-5.1$ typeset -p arr
    declare -a arr=([0]="aX" [1]="foo" [2]="" [3]="" [4]="" [5]="*" [6]="?x" [7]="\\x" [8]="x")
    

    这与我们在之前的输出中看到的一致ls | cat,但如果目的是获取当前工作目录中的文件列表,那么这仍然是错误的。的输出ls无法进行后处理,除非您使用 GNU 实现的某些扩展,ls例如--quoting-style=shell-always或--zero最新版本(9.0 或更高版本):

    bash-5.2$ readarray -td '' arr < <(ls --zero)
    bash-5.2$ typeset -p arr
    declare -a arr=([0]="aX" [1]=$'foo\n\n\n\n*' [2]="?x" [3]="\\x" [4]="x")
    

    这次,将 NUL限制记录readarray的内容存储到 中。不能用于,因为不能在其变量中存储 NUL。d$arrIFS=$'\0'bashbash

    或者:

    bash-5.1$ eval "arr=( $(ls --quoting-style=shell-always) )"
    bash-5.1$ typeset -p arr
    declare -a arr=([0]="aX" [1]=$'foo\n\n\n\n*' [2]="?x" [3]="\\x" [4]="x")
    

    无论如何,将当前工作目录中的文件列表放入数组的正确方法是:

    shopt -s nullglob
    arr=( * )
    

    仅ls --zero当您希望列表按大小或修改时间排序时,您才会使用 bash glob(与 zsh 相反)无法做到的事情。

    如:

    桀骜 最近的 GNU bash + GNU coreutils
    new_to_old=( *.txt(Nom) ) readarray -td '' new_to_old < <(ls -td --zero -- *.txt)
    four_largest=( *.txt(NOL[1,4]) ) readarray -td '' four_largest < <(ls -tdrS --zero -- *.txt | head -zn4)

    a=($(cmd))和之间的另一个区别readarray < <(cmd)是退出状态,前者是cmd,后者是readarray。使用最新版本的,您可以使用获取后者bash的存在状态。cmdwait "$!"; cmd_status=$?


    ¹arr=( ... )语法来自 zsh(bash 直到 1996 年 2.0 才出现数组),但请注意,在 zsh 中,命令替换虽然也会删除尾随换行符并受 -stripping 约束,但不会丢弃 NUL(NUL 甚至$IFS在那里的默认值$IFS)并且不像其他类似 Bourne 的 shell 那样受到通配符的影响,这有助于使其成为一个更安全的 shell。

    ²readarray又名mapfile没有追加模式,但在最近的版本中,您可以告诉它第一个元素的索引,从哪里开始存储元素,-O如下所示。要找出 bash 中最后一个元素的索引(其中数组像 ksh 中一样稀疏!),这是非常困难的。cmd在这里附加to的输出行$arr,而不是那些非常复杂的代码,您不妨将这些行读入临时数组 withreadarray -r tmp < <(cmd)并将元素附加到$arrwith arr+=( "${tmp[@]}" )。另请注意,如果arr变量被声明为标量或关联,则这些方法之间的行为会有所不同。

    • 3

相关问题

  • 使用分隔符时如何删除readarray添加的新行?

  • 查找数组中的第二大值

  • 如果某些数组在某个索引处有元素,则计数并添加

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve