Nie można potokować w „pliku map” bash… ale dlaczego?

13

Chcę tylko przenieść wszystkie pliki z określonego katalogu do tablicy bash (zakładając, że żaden z plików nie ma nowej linii w nazwie):

Więc:

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

Pusty wynik!

Jeśli korzystam z pliku przy użyciu ronda, tymczasowo lub w inny sposób:

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

Wynik!

Ale dlaczego nie mapfileczyta poprawnie z potoku?

David Tonhofer
źródło
Doskonałe odpowiedzi dookoła, dziękuję wszystkim. Ciekawe, w jaki sposób strategia wykonania potoku (każda część działająca w procesie spearate) przecieka „w górę” i modyfikuje pozorne znaczenie kodu, w zasadzie po cichu umieszczając „lokalny” przed każdą zmienną pojawiającą się w potoku. Mam nadzieję, że w języku, który jest czymś innym niż szalony klej do innych programów, byłby to błąd.
David Tonhofer,
2
Jeśli podasz kod do sprawdzania powłoki , otrzymasz ostrzeżenia: SC2030 : „Modyfikacja var jest lokalna (do podpowłoki spowodowanej przez potok)” i SC2031 : „var został zmodyfikowany w podpowłoce. Ta zmiana może zostać utracona.” . Świetny.
David Tonhofer,
Po co w ogóle używać findi mapfiletutaj, a nie tylko myarr=(mysqldump*)? Będzie to nawet działać z nazwami plików ze spacjami i znakami nowej linii.
BlackJack
1
Zauważyłem, że należy włączyć nullglobopcję on ( shopt -s nullglob), myarr=(mysqldump*)aby nie skończyć z tablicą ('mysqldump*')na wypadek, gdyby żadne pliki się nie zgadzały.
David Tonhofer,

Odpowiedzi:

25

Od man 1 bash:

Każde polecenie w potoku jest wykonywane jako osobny proces (tj. W podpowłoce).

Takie podpowłoki dziedziczą zmienne z głównej powłoki, ale są one niezależne. Oznacza to, że mapfilew twoim oryginalnym poleceniu działa samodzielnie myarr. Następnie echo(będąc poza rurą) drukuje puste myarr(co jest główną powłoką myarr).

To polecenie działa inaczej:

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

W tym przypadku mapfilei echodziałają na tym samym myarr(co nie jest głównym powłoka jest myarr).

Aby zmienić główną powłokę, myarrmusisz mapfiledokładnie uruchomić ją w głównej. Przykład:

myarr=()
mapfile -t myarr < <(find . -maxdepth 1 -name "mysqldump*")
echo "${myarr[@]}"
Kamil Maciorowski
źródło
Dodano link do „podstawienia procesu”, jak podano w odpowiedzi Attie, na wypadek, gdyby użytkownik miał moment TL; DR.
David Tonhofer,
11

Bash uruchamia polecenia potoku w środowisku podpowłoki, więc wszelkie przypisania zmiennych itp., Które mają miejsce w nim, nie są widoczne dla reszty powłoki.

Dash (Debian /bin/sh) i busybox shsą podobne, podczas gdy zsh i ksh uruchamiają ostatnią część w głównej powłoce. W Bash możesz shopt -s lastpipezrobić to samo, ale działa to tylko wtedy, gdy kontrola zadań jest wyłączona, więc domyślnie nie w interaktywnych powłokach.

Więc:

$ 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

( readi mapfilemają ten sam problem).

Alternatywnie ( jak wspomniano w Attie) użyj substytucji procesu , która działa jak uogólniony potok i jest obsługiwana w Bash, ksh i zsh.

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

POSIX pozostawia nieokreślone, czy części potoku działają w podpowłokach, czy nie, więc nie można tak naprawdę powiedzieć, że żadna z powłok nie byłaby w tym „zła”.

ilkkachu
źródło
2
Jeśli wyłączysz kontrolę zadań bash, możesz także użyć Lastpipe w interaktywnej powłoce:set +m; shopt -s lastpipe; x=a; echo b | read x; echo $x; set -m
Cyrus
@ Cyrus, ah racja, zapomniałem o szczegółach, dzięki
ilkkachu
9

Jak zauważył Kamil, każdy element rurociągu jest osobnym procesem.

Można użyć następującego podstawienie proces dostać finddo pracy w innym procesie, z mapfileinwokacja pozostały w bieżącym tłumacza, umożliwiając dostęp do myarrpóźniej:

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

b < <( a )będzie działał podobnie do tego, a | bw jaki sposób okablowany jest rurociąg - różnica polega na tym, że bjest wykonywany „ tutaj ”.

Attie
źródło