Aktualizacja 2020 dla użytkowników Linuksa:
Jeśli masz wersję up-to-date bash (4,4-alfa lub lepsza), jak zapewne zrobić, jeśli jesteś na Linuksie, to powinieneś być w użyciu odpowiedź Benjamin W. .
Jeśli korzystasz z Mac OS, który - ostatnio, jak sprawdziłem - nadal używa basha 3.2 lub w inny sposób używasz starszego basha, przejdź do następnej sekcji.
Odpowiedź dla basha 4.3 lub wcześniejszego
Oto jedno rozwiązanie dla uzyskania wyjścia finddo bashtablicy:
array=()
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done < <(find . -name "${input}" -print0)
Jest to trudne, ponieważ ogólnie nazwy plików mogą zawierać spacje, nowe wiersze i inne znaki wrogie dla skryptów. Jedynym sposobem bezpiecznego używania findi oddzielania nazw plików od siebie jest użycie polecenia, -print0które wypisuje nazwy plików oddzielone znakiem null. Nie stanowiłoby to dużej niedogodności, gdyby funkcje readarray/ basha mapfileobsługiwały ciągi znaków oddzielone znakiem null, ale tak nie jest. Bash readrobi i to prowadzi nas do powyższej pętli.
[Ta odpowiedź została pierwotnie napisana w 2014 r. Jeśli masz najnowszą wersję basha, zapoznaj się z aktualizacją poniżej.]
Jak to działa
Pierwsza linia tworzy pustą tablicę: array=()
Za każdym razem, gdy readwykonywana jest instrukcja, ze standardowego wejścia odczytywana jest nazwa pliku oddzielona znakiem null. -rOpcja nakazuje read, aby zostawić znaki BACKSLASH. -d $'\0'Mówi read, że wkład będzie zerowa rozdzielono. Ponieważ pomijamy nazwę na read, powłoka daje wejście do domyślnej nazwy: REPLY.
array+=("$REPLY")Oświadczenie dołącza nową nazwę pliku do tablicy array.
Ostatnia linia łączy przekierowanie i podstawianie poleceń, aby zapewnić wyjście findna standardowe wejście whilepętli.
Dlaczego warto korzystać z zastępowania procesów?
Gdybyśmy nie używali podstawiania procesów, pętla mogłaby zostać zapisana jako:
array=()
find . -name "${input}" -print0 >tmpfile
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done <tmpfile
rm -f tmpfile
W powyższym przykładzie dane wyjściowe programu findsą przechowywane w pliku tymczasowym i ten plik jest używany jako standardowe wejście do pętli while. Ideą podstawiania procesów jest uczynienie takich plików tymczasowych niepotrzebnymi. Więc zamiast whilepobierać stdin z pętli tmpfile, możemy sprawić, by pobierała stdin z <(find . -name ${input} -print0).
Zastępowanie procesów jest bardzo przydatne. W wielu miejscach, w których polecenie chce czytać z pliku <(...), zamiast nazwy pliku można określić podstawianie procesu . Istnieje analogiczna forma, >(...)której można użyć zamiast nazwy pliku, w którym polecenie chce zapisać do pliku.
Podobnie jak w przypadku tablic, podstawianie procesów jest funkcją powłoki bash i innych zaawansowanych powłok. Nie jest częścią standardu POSIX.
Alternatywa: lastpipe
W razie potrzeby lastpipemożna użyć zamiast zastępowania procesu (końcówka kapelusza: Cezar ):
set +m
shopt -s lastpipe
array=()
find . -name "${input}" -print0 | while IFS= read -r -d $'\0'; do array+=("$REPLY"); done; declare -p array
shopt -s lastpipemówi bashowi, aby uruchomił ostatnie polecenie w potoku w bieżącej powłoce (nie w tle). W ten sposób arraypozostaje po zakończeniu rurociągu. Ponieważ lastpipedziała tylko wtedy, gdy kontrola zadań jest wyłączona, uruchamiamy set +m. (W skrypcie, w przeciwieństwie do wiersza poleceń, kontrola zadań jest domyślnie wyłączona).
Dodatkowe uwagi
Następujące polecenie tworzy zmienną powłoki, a nie tablicę powłoki:
array=`find . -name "${input}"`
Jeśli chciałbyś stworzyć tablicę, musiałbyś umieścić parens wokół wyniku find. Można więc naiwnie:
array=(`find . -name "${input}"`)
Problem polega na tym, że powłoka dokonuje podziału na słowa na wynikach programu, findtak że elementy tablicy nie są gwarantowane, że będą zgodne z oczekiwaniami.
Zaktualizuj 2019
Począwszy od wersji 4.4-alpha, bash obsługuje teraz -dopcję, dzięki czemu powyższa pętla nie jest już potrzebna. Zamiast tego można użyć:
mapfile -d $'\0' array < <(find . -name "${input}" -print0)
Aby uzyskać więcej informacji na ten temat można znaleźć (i upvote) odpowiedź Benjamin W. .