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パイプから適切に読み取らないのですか?



すばらしい回答をありがとうございます。パイプラインの実行戦略(各プロセスで実行される各部分)が「上向き」にリークしてコードの見かけの意味を変更する方法が興味深い。基本的にはパイプに現れるすべての変数の前に「ローカル」を静かに置く。他のプログラムのクレイジーグルー以外の言語では、それがバグになると思います。
デビッドトンホーファー2018

2
コードをshellcheckに渡すと、警告が表示されます:SC2030「varの変更はローカル(パイプラインによって引き起こされるサブシェルに対して)」およびSC2031「varはサブシェルで変更されました。その変更は失われる可能性があります。」。優秀な。
デビッドトンホーファー2018

単純にだけでなく、なぜfindandをmapfileここで使用するのmyarr=(mysqldump*)ですか?これは、スペースと改行を含むファイル名でも機能します。
BlackJack 2018

1
ちょうど1回転していることに気づいたnullglob(上のオプションをshopt -s nullglobのために)myarr=(mysqldump*)配列で終わるいないに('mysqldump*')何のファイルが一致しない場合。
デビッドトンホーファー2018

回答:


25

からman 1 bash

パイプラインの各コマンドは、個別のプロセスとして(つまり、サブシェルで)実行されます。

このようなサブシェルはメインシェルから変数を継承しますが、独立しています。つまりmapfile、元のコマンドでは独自に動作しますmyarr。次に、echo(パイプの外にある)空myarr(メインシェルのmyarr)を出力します。

このコマンドの動作は異なります。

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

この場合mapfileecho同じで動作myarr(メインシェルのされていませんmyarr)。

メインシェルを変更myarrするにmapfileは、メインシェルで正確に実行する必要があります。例:

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

訪問者がTL; DRの瞬間を持っている場合に備えて、Attieの応答に示されている「処理置換」へのリンクを追加しました。
David Tonhofer

11

Bashはサブシェル環境でパイプラインのコマンドを実行するため、その中で行われる変数の割り当てなどは、シェルの残りの部分からは見えません。

ダッシュ(Debianのもの/bin/sh)とbusyboxのshものは似ていますが、zshとkshはメインシェルの最後の部分を実行します。Bashでは、を使用shopt -s lastpipeして同じことを実行できますが、ジョブ制御が無効になっている場合にのみ機能するため、デフォルトではインタラクティブシェルでは機能しません。

そう:

$ 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のジョブ制御を使用すると、インタラクティブシェルでlastpipeを使用することができた場合は、あまりにも:set +m; shopt -s lastpipe; x=a; echo b | read x; echo $x; set -m
サイラス

@Cyrus、ああ、詳細を忘れてしまいました。ありがとう
ilkkachu '15

9

カミルが指摘したように、パイプラインの各要素は個別のプロセスです。

次のプロセス置換を使用findして、別のプロセスで実行することができます。mapfile呼び出しは現在のインタープリターに残っているため、myarr後でアクセスできます。

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

b < <( a )a | bパイプラインの配線方法と同様に動作します-違いは、bここ」で実行されることです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.