Інші відповіді зламається , якщо висновок команди містить прогалини (які досить часто) або кульку символи , такі як *
, ?
, [...]
.
Для отримання результату команди в масиві, що містить один рядок на елемент, є по суті 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]")'
тут ми працюємо з оболонкою!