Jeśli musisz przechowywać dane wyjściowe i chcesz mieć tablicę, mapfileułatwia to.
Najpierw zastanów się, czy w ogóle musisz przechowywać dane wyjściowe polecenia. Jeśli nie, po prostu uruchom polecenie.
Jeśli zdecydujesz, że chcesz odczytać wynik polecenia jako tablicę wierszy, prawdą jest, że jednym ze sposobów jest wyłączenie globowania, ustawienie IFSpodziału na linie, stosowanie podstawiania poleceń w ( )składni tworzenia tablicy, a IFSnastępnie resetowanie , co jest bardziej złożone niż się wydaje. Odpowiedź terdona obejmuje niektóre z tych podejść. Ale proponuję użyć zamiast tego wbudowanego polecenia mapfilepowłoki Bash do odczytu tekstu jako tablicy wierszy. Oto prosty przypadek, w którym czytasz z pliku:
mapfile < filename
To odczytuje wiersze do tablicy o nazwie MAPFILE. Bez przekierowania wejścia czytałbyś ze standardowego wejścia powłoki (zwykle z terminala) zamiast z pliku o nazwie . Aby wczytać tablicę inną niż domyślna , przekaż jej nazwę. Na przykład wczytuje to do tablicy :< filenamefilenameMAPFILElines
mapfile lines < filename
Innym domyślnym zachowaniem, które możesz zmienić, jest pozostawienie znaków nowej linii na końcach linii; pojawiają się jako ostatni znak w każdym elemencie tablicy (chyba że dane wejściowe zakończyły się bez znaku nowej linii, w którym to przypadku ostatni element nie ma). Aby przełamać te znaki nowej linii, aby nie pojawiły się w tablicy, przekaż -topcję do mapfile. Na przykład odczytuje to tablicę recordsi nie zapisuje końcowych znaków nowego wiersza do jej elementów tablicy:
mapfile -t records < filename
Możesz także używać -tbez przekazywania nazwy tablicy; to znaczy, działa również z niejawną nazwą tablicy MAPFILE.
mapfilePowłoka wbudowane podpór innych opcji, a to może alternatywnie być powoływane jako readarray. Uruchom help mapfile(i help readarray), aby uzyskać szczegółowe informacje.
Ale nie chcesz czytać z pliku, chcesz czytać z wyniku polecenia. Aby to osiągnąć, użyj substytucji procesu . Ta komenda czyta wiersze z komendy some-commandz arguments...jak jego argumenty wiersza polecenia i miejsc w nich w mapfile„s domyślnej tablicy MAPFILE, z ich zakończeń znaków nowej linii usunięte:
mapfile -t < <(some-command arguments...)
Podstawienie procesu zastępowane jest rzeczywistą nazwą pliku, z którego można odczytać wynik działania. Plik jest nazwanym potokiem, a nie zwykłym plikiem, a na Ubuntu zostanie nazwany jak (czasem z inną liczbą niż ), ale nie musisz zajmować się szczegółami, ponieważ dba o to powłoka wszystko za kulisami.<(some-command arguments...)some-command arguments.../dev/fd/6363
Możesz pomyśleć, że możesz zrezygnować z zastępowania procesów za pomocą zamiast tego, ale to nie zadziała, ponieważ gdy masz wiele potoków oddzielonych , Bash uruchamia wszystkie polecenia w podpowłokach . Zatem zarówno i uruchomić w swoich środowiskach , ale z zainicjowany oddzielić od środowiska powłoki, w którym uruchomiono gazociąg. W podpowłoce, w której działa, tablica jest zapełniana, ale tablica ta jest odrzucana po zakończeniu polecenia. nigdy nie jest tworzony ani modyfikowany dla osoby dzwoniącej.some-command arguments... | mapfile -t|some-command arguments...mapfile -tmapfile -tMAPFILEMAPFILE
Oto jak wygląda przykład w odpowiedzi terdona , jeśli użyjesz mapfile:
mapfile -t dirs < <(find . -type d)
for d in "${dirs[@]}"; do
echo "DIR: $d"
done
Otóż to. Nie musisz sprawdzać, czy IFSzostał ustawiony , śledzić, czy nie został ustawiony i jaką wartością ustawić nową linię, a następnie zresetować lub zresetować później. Nie trzeba wyłączać masek (na przykład, z set -f) - co jest naprawdę potrzebne, jeśli chcesz używać tej metody poważnie, ponieważ nazwy plików mogą zawierać *, ?oraz [--then ponownie włączyć go ( set +f) potem.
Możesz także całkowicie zastąpić tę konkretną pętlę pojedynczym printfpoleceniem - choć tak naprawdę nie jest to zaletą mapfile, ponieważ możesz to zrobić niezależnie od tego, czy użyjesz mapfileinnej metody do zapełnienia tablicy. Oto krótsza wersja:
mapfile -t < <(find . -type d)
printf 'DIR: %s\n' "${MAPFILE[@]}"
Ważne jest, aby pamiętać, że, jak wspomina terdon , operacja linia po linii nie zawsze jest odpowiednia i nie będzie działać poprawnie z nazwami plików, które zawierają znaki nowej linii. Odradzam nazywanie plików w ten sposób, ale może się to zdarzyć, w tym przez przypadek.
Tak naprawdę nie ma jednego uniwersalnego rozwiązania.
Prosiłeś o „ogólne polecenie dla wszystkich scenariuszy” i podejście mapfilepolegające na tym, że w pewnym stopniu zbliżasz się do tego celu, ale zachęcam do ponownego rozważenia swoich wymagań.
Zadanie pokazane powyżej lepiej wykonać za pomocą jednego findpolecenia:
find . -type d -printf 'DIR: %p\n'
Możesz także użyć zewnętrznego polecenia, takiego jak seddodać DIR: na początku każdej linii. Jest to prawdopodobnie nieco brzydkie i w przeciwieństwie do polecenia find doda dodatkowe „przedrostki” w nazwach plików zawierających znaki nowej linii, ale działa niezależnie od danych wejściowych, więc spełnia twoje wymagania i nadal lepiej jest czytać dane wyjściowe w pliku zmienna lub tablica :
find . -type d | sed 's/^/DIR: /'
Jeśli chcesz wyświetlić listę, a także wykonać akcję na każdym znalezionym katalogu, na przykład uruchomić some-commandi przekazać ścieżkę katalogu jako argument, findmożesz to zrobić również:
find . -type d -print -exec some-command {} \;
Jako kolejny przykład wróćmy do ogólnego zadania dodawania prefiksu do każdej linii. Załóżmy, że chcę zobaczyć wynik, help mapfileale numeruje linie. Tak naprawdę nie użyłbym mapfiledo tego ani żadnej innej metody, która odczytuje go do zmiennej powłoki lub tablicy powłoki. Załóżmy, help mapfile | cat -nże nie daje pożądanego formatowania, mogę użyć awk:
help mapfile | awk '{ printf "%3d: %s\n", NR, $0 }'
Odczytywanie wszystkich danych wyjściowych polecenia do jednej zmiennej lub tablicy jest czasem przydatne i odpowiednie, ale ma poważne wady. Nie tylko musisz poradzić sobie z dodatkową złożonością korzystania z powłoki, gdy istniejące polecenie lub kombinacja istniejących poleceń może już robić to, czego potrzebujesz, lub lepiej, ale całe wyjście polecenia musi być przechowywane w pamięci. Czasami możesz wiedzieć, że to nie jest problem, ale czasami możesz przetwarzać duży plik.
Alternatywą, która jest często podejmowana - a czasem wykonywana właściwie - jest odczytywanie wejścia wiersz po wierszu za read -rpomocą pętli. Jeśli nie musisz zapisywać poprzednich wierszy podczas pracy na późniejszych wierszach i musisz użyć długich danych wejściowych, może to być lepsze niż mapfile. Ale również tego należy unikać w przypadkach, gdy można po prostu potokować go do polecenia, które może wykonać pracę, co jest w większości przypadków .