无法通过管道传输bash的“ mapfile” ...但是为什么呢?


13

我只想将某个目录中的所有文件放入bash数组中(假设所有文件名中都没有换行符):

所以:

myarr=()
find . -maxdepth 1  -name "mysqldump*" | mapfile -t myarr; echo "${myarr[@]}"

空结果!

如果我使用临时文件或其他方法来回旋使用文件:

myarr=()
find . -maxdepth 1  -name "mysqldump*" > X
mapfile -t myarray < X
echo "${myarray[@]}"

结果!

但是为什么不能mapfile从管道中正确读取?



很好的答案,谢谢大家。有趣的是,管道的执行策略(每个部分在一个贯穿进程中运行)如何“向上”泄漏并修改代码的表观含义,基本上将“本地”静默地放在管道中出现的每个变量的前面。希望使用某种语言,而不是疯狂地粘其他程序的语言。
David Tonhofer

2
如果将代码提供给shellcheck,则会收到警告:SC2030“对var的修改是本地的(对于由管道引起的子shell)”SC2031“在子shell中修改了var。该更改可能会丢失。” 。优秀的。
David Tonhofer

为什么要使用findand mapfile这里,而不仅仅是简单myarr=(mysqldump*)?这甚至可以用于带有空格和换行符的文件名。
BlackJack

1
只是注意到,如果没有文件匹配,则必须打开nullglobshopt -s nullglob)选项myarr=(mysqldump*),以免导致数组结尾('mysqldump*')
David Tonhofer

Answers:


25

来自man 1 bash

管道中的每个命令都作为单独的进程(即,在子Shell中)执行。

这样的子shell从主shell继承变量,但是它们是独立的。这意味着mapfile您的原始命令将独立运行myarr。然后echo(在管道外部)打印为空myarr(这是主壳的myarr)。

此命令的工作方式不同:

find . -maxdepth 1 -name "mysqldump*" | { mapfile -t myarr; echo "${myarr[@]}"; }

在这种情况下mapfileecho可以在相同myarr的主机上进行操作(这不是主shell的myarr)。

要更改主外壳myarr,必须mapfile准确地在主外壳中运行。例:

myarr=()
mapfile -t myarr < <(find . -maxdepth 1 -name "mysqldump*")
echo "${myarr[@]}"

如果访客有TL; DR时刻,请在Attie的响应中添加到“流程替代”的链接。
David Tonhofer

11

Bash在子Shell环境中运行管道的命令,因此在其中进行的任何变量分配等在Shell的其余部分都不可见。

Dash(Debian的/bin/sh)和busybox的sh相似,而zsh和ksh在主外壳中运行最后一部分。在Bash中,您可以shopt -s lastpipe执行相同的操作,但是它仅在禁用作业控制时才起作用,因此默认情况下在交互式shell中无效。

所以:

$ bash -c 'x=a; echo b | read x; echo $x'
a
$ bash -c 'shopt -s lastpipe; x=a; echo b | read x; echo $x'
b

read并且mapfile有相同的问题。)

或者( Attie所述),使用流程替换,它像通用管道一样工作,并且在Bash,ksh和zsh中受支持。

$ bash -c 'x=a; read x < <(echo b); echo $x'
b

POSIX未指定管道的各个部分是否在子外壳中运行,因此不能真正说任何外壳在此都是“错误的”。


2
如果禁用bash的作业控制,则也可以在交互式shell中使用lastpipe:set +m; shopt -s lastpipe; x=a; echo b | read x; echo $x; set -m
Cyrus

@Cyrus,是的,我忘记了细节,谢谢
ilkkachu

9

正如Kamil指出的那样,管道中的每个元素都是一个单独的过程。

您可以使用以下流程替换find在另一个流程中运行,而mapfile调用仍保留在当前的解释器中,从而允许myarr事后访问:

myarr=()
mapfile -t myarr < <( find . -maxdepth 1  -name "mysqldump*" )
echo "${myarr[@]}"

b < <( a )a | b管道的连接方式而言,其行为将相似-区别在于b在“ 此处 ” 执行。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.