Інші відповіді зламається , якщо висновок команди містить прогалини (які досить часто) або кульку символи , такі як *, ?, [...].
Для отримання результату команди в масиві, що містить один рядок на елемент, є по суті 3 способи:
Використання Bash≥4 mapfile- це найбільш ефективно:
mapfile -t my_array < <( my_command )
В іншому випадку цикл зчитує вихід (повільніше, але безпечно):
my_array=()
while IFS= read -r line; do
my_array+=( "$line" )
done < <( my_command )
Як запропонував Чарльз Даффі у коментарях (спасибі!), Наступне може бути кращим, ніж метод циклу у №2:
IFS=$'\n' read -r -d '' -a my_array < <( my_command && printf '\0' )
Будь ласка, переконайтеся, що ви використовуєте саме цю форму, тобто переконайтеся, що у вас є:
IFS=$'\n' у тому ж рядку, що й readвираз: це встановить змінну середовища лише IFS для readоператора. Так що це взагалі не вплине на решту вашого сценарію. Мета цієї змінної полягає в тому, щоб сказати, readщоб перервати потік на символі EOL \n.
-r: це важливо. Це говорить read про те, що не слід інтерпретувати косої риси як послідовності втечі.
-d '': зверніть увагу на пробіл між -dопцією та її аргументом ''. Якщо ви не залишите пробіл тут, запит ''ніколи не буде помічений, оскільки він зникне на кроці видалення цитат, коли Bash розбере заяву. Це говорить readпро припинення читання в нульовому байті. Деякі люди пишуть це як -d $'\0', але насправді це не потрібно. -d ''краще.
-a my_arrayвказує readзаповнити масив my_arrayпід час читання потоку.
- Ви повинні використовувати
printf '\0'оператор після my_command , щоб readповернутись 0; насправді це не велика справа, якщо ви цього не зробите (ви просто отримаєте код повернення. 1Це нормально, якщо ви не використовуєте set -e- чого ви все одно не повинні), але просто пам’ятайте про це. Це чистіше і більш семантично правильно. Зауважте, що це відрізняється від того printf '', що нічого не видає. printf '\0'друкує нульовий байт, необхідний для того, readщоб радісно перестати читати там (пам'ятаєте -d ''варіант?).
Якщо ви можете, тобто якщо ви впевнені, що ваш код буде працювати на Bash≥4, скористайтеся першим методом. І ви можете бачити, що це теж коротше.
Якщо ви хочете використовувати read, цикл (метод 2) може мати перевагу перед методом 3, якщо ви хочете виконати деяку обробку, коли рядки читаються: у вас є прямий доступ до нього (через $lineзмінну в прикладі, який я дав), і Ви також маєте доступ до вже прочитаних рядків (через масив ${my_array[@]}у прикладі, який я дав).
Зауважте, що mapfileпередбачено спосіб зворотного виклику eval'd на кожному прочитаному рядку, і насправді ви можете навіть сказати йому, щоб викликати цей зворотний дзвінок лише через кожну N прочитаних рядків; перегляньте help mapfileі варіанти, -Cі -cтам. (Моя думка з цього приводу полягає в тому, що це трохи незграбно, але його можна використовувати іноді, якщо у вас є лише прості речі - я не дуже розумію, чому це було здійснено в першу чергу!).
Тепер я розповім, чому такий метод:
my_array=( $( my_command) )
порушується, коли є пробіли:
$ # 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!
Тоді деякі люди рекомендуватимуть IFS=$'\n'це виправити:
$ 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!
Але тепер давайте скористаємося ще однією командою з глобусами :
$ 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?
Це тому, що у мене є файл, який називається tу поточному каталозі ... і це ім'я файлу відповідає глобулю [three four] ... У цей момент деякі люди рекомендують використовувати set -fдля відключення глобалізації: але подивіться на це: ви повинні змінити IFSі використовувати, set -fщоб мати можливість виправити виправлення зламана техніка (а ти навіть це навіть не фіксуєш)! при цьому ми дійсно боремося з оболонкою, не працюючи з оболонкою .
$ mapfile -t my_array < <( echo "* one two"; echo "[three four]")
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="[three four]")'
тут ми працюємо з оболонкою!