无法在 bash 的“mapfile”中进行管道传输……但为什么呢?

Dav*_*fer 16 bash

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

所以:

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

结果为空!

如果我使用临时文件或其他文件的迂回方式:

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

结果!

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

Kam*_*ski 27

来自man 1 bash

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

此类子shell 从主shell 继承变量,但它们是独立的。这意味着mapfile在您的原始命令中自行操作myarr。然后echo(在管道外)打印空myarr(这是主壳的myarr)。

此命令的工作方式不同:

find . -maxdepth 1 -name "mysqldump*" | { mapfile -t myarr; echo "${myarr[@]}"; }
Run Code Online (Sandbox Code Playgroud)

在这种情况下,mapfileecho在相同的myarr(不是主 shell 的myarr)上操作。

要更改主 shell,myarr您必须mapfile完全在主 shell 中运行。例子:

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


ilk*_*chu 11

Bash 在子 shell 环境中运行管道的命令,因此在其中发生的任何变量赋值等对 shell 的其余部分都是不可见的。

Dash(Debian 的/bin/sh)和busybox 的sh类似,而zsh 和ksh 在主shell 中运行最后一部分。在 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
Run Code Online (Sandbox Code Playgroud)

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

或者(正如Attie所提到的),使用进程替换,它的工作方式类似于通用管道,并在 Bash、ksh 和 zsh 中受支持。

$ bash -c 'x=a; read x < <(echo b); echo $x'
b
Run Code Online (Sandbox Code Playgroud)

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

  • 如果您禁用 bash 的作业控制,您也可以在交互式 shell 中使用 lastpipe:`set +m; shopt -s 尾管;x=a; 回声 b | 读取 x; 回声 $x; 设置-m` (3认同)

Att*_*tie 9

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

您可以使用以下流程替换find在不同的流程中运行,mapfile调用保留在您当前的解释器中,允许myarr之后访问:

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

b < <( a )将类似于a | b管道的接线方式 - 不同之处在于b在“此处”执行。