Pozostałe odpowiedzi pęknie jeśli wyjście komendy zawiera spacje (co jest dość częste) lub znaki jak glob *, ?, [...].
Aby uzyskać wynik polecenia w tablicy, z jednym wierszem na element, istnieją zasadniczo 3 sposoby:
Z Bash≥4 użyj mapfile- jest najbardziej wydajny:
mapfile -t my_array < <( my_command )
W przeciwnym razie pętla odczytująca wyjście (wolniej, ale bezpiecznie):
my_array=()
while IFS= read -r line; do
my_array+=( "$line" )
done < <( my_command )
Jak zasugerował Charles Duffy w komentarzach (dzięki!), Poniższe mogą działać lepiej niż metoda pętli w numerze 2:
IFS=$'\n' read -r -d '' -a my_array < <( my_command && printf '\0' )
Upewnij się, że używasz dokładnie tego formularza, tj. Upewnij się, że masz następujące:
IFS=$'\n' w tym samym wierszu co readinstrukcja: spowoduje to ustawienie zmiennej środowiskowej tylko IFS dla readinstrukcji. Więc w ogóle nie wpłynie to na resztę twojego skryptu. Celem tej zmiennej jest nakazanie readprzerwania strumienia przy znaku EOL \n.
-r: To jest ważne. Mówi, read aby nie interpretować odwrotnych ukośników jako sekwencji ucieczki.
-d '': zwróć uwagę na spację między -dopcją a jej argumentem ''. Jeśli nie zostawisz tutaj spacji, ''nigdy nie będzie widoczne, ponieważ zniknie w kroku usuwania cytatu, gdy Bash przeanalizuje instrukcję. To mówi, readaby przestać czytać na bajcie zerowym. Niektórzy piszą to jako -d $'\0', ale tak naprawdę nie jest to konieczne. -d ''jest lepiej.
-a my_arraymówi, readaby wypełnić tablicęmy_array podczas czytania strumienia.
- Musisz użyć
printf '\0'instrukcji później my_command , aby to readpowróciło 0; to właściwie nie jest wielka sprawa, jeśli tego nie zrobisz (otrzymasz po prostu kod powrotu 1, co jest w porządku, jeśli nie używasz set -e- czego i tak nie powinieneś), ale po prostu miej to na uwadze. Jest czystszy i bardziej poprawny semantycznie. Zauważ, że różni się to od printf '', które nic nie wyświetla. printf '\0'wypisuje bajt zerowy, potrzebny, readaby szczęśliwie przestał tam czytać (pamiętasz -d ''opcję?).
Jeśli możesz, tj. Jeśli masz pewność, że Twój kod będzie działał na Bash≥4, użyj pierwszej metody. Widać też, że jest krótszy.
Jeśli chcesz użyć read, pętla (metoda 2) może mieć przewagę nad metodą 3, jeśli chcesz wykonać pewne przetwarzanie podczas odczytywania wierszy: masz do niej bezpośredni dostęp (poprzez $linezmienną z podanego przykładu) i masz również dostęp do już przeczytanych wierszy (poprzez tablicę ${my_array[@]}z przykładu, który podałem).
Zauważ, że mapfileumożliwia to wywołanie zwrotne w każdej przeczytanej linii, aw rzeczywistości możesz nawet powiedzieć mu, aby wywoływał to wywołanie zwrotne tylko po przeczytaniu N linii; spójrz na help mapfilei opcje -Ci -ctam. (Moja opinia na ten temat jest taka, że jest to trochę niezgrabne, ale czasami może być używane, jeśli masz tylko proste rzeczy do zrobienia - nie bardzo rozumiem, dlaczego zostało to wdrożone!).
Teraz powiem ci, dlaczego następująca metoda:
my_array=( $( my_command) )
jest uszkodzony, gdy są spacje:
$ # I'm using this command to test:
$ echo "one two"; echo "three four"
one two
three four
$ # Now I'm going to use the broken method:
$ my_array=( $( echo "one two"; echo "three four" ) )
$ declare -p my_array
declare -a my_array='([0]="one" [1]="two" [2]="three" [3]="four")'
$ # As you can see, the fields are not the lines
$
$ # Now look at the correct method:
$ mapfile -t my_array < <(echo "one two"; echo "three four")
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # Good!
Wtedy niektórzy ludzie zalecą użycie, IFS=$'\n'aby to naprawić:
$ IFS=$'\n'
$ my_array=( $(echo "one two"; echo "three four") )
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # It works!
Ale teraz użyjmy innego polecenia, z globami :
$ echo "* one two"; echo "[three four]"
* one two
[three four]
$ IFS=$'\n'
$ my_array=( $(echo "* one two"; echo "[three four]") )
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="t")'
$ # What?
Dzieje się tak, ponieważ mam plik o nazwie tw bieżącym katalogu… a ta nazwa pliku jest dopasowana przez glob [three four] … w tym momencie niektórzy ludzie zalecaliby użycie set -fdo wyłączenia globbingu: ale spójrz na to: musisz zmienić IFSi użyć, set -faby móc naprawić zepsuta technika (a nawet tego nie naprawiasz)! robiąc to, naprawdę walczymy z powłoką, a nie z nią .
$ mapfile -t my_array < <( echo "* one two"; echo "[three four]")
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="[three four]")'
tutaj pracujemy z powłoką!