Перетворити масив у аргументи команди?


40

У мене є масив "options" команди.

my_array=(option1 option2 option3)

Я хочу викликати цю команду в bash-скрипті, використовуючи значення з масиву як параметри. Отже, command $(some magic here with my_array) "$1"стає:

command -option1 -option2 -option3 "$1"

Як я можу це зробити? Це можливо?


1
Отже, ви хочете додати -до початку кожного слова в my_array?
Кевін

@Kevin: Так, і виконайте це. Усі рядки знаходяться всередині одного сценарію bash. Я запитую це, тому що ці самі варіанти будуть використовуватися у багатьох місцях всередині сценарію, тому замість того, щоб копіювати їх усі навколо, я подумав про масив.
Хтось досі використовує вас MS-DOS

1
Це може здатися безглуздим, але якщо ви створюєте великі сценарії оболонок, можливо, вам варто заглянути в perl, робити подібні речі дуже просто.
alfa64

Відповіді:


43

Я вважаю за краще простий bashспосіб:

command "${my_array[@]/#/-}" "$1"

Однією з причин цього є пробіли. Наприклад, якщо у вас є:

my_array=(option1 'option2 with space' option3)

Розчинені sedна основі рішення перетворять його на -option1 -option2 -with -space -option3(довжина 5), але вищезазначене bashрозширення перетворить його на -option1 -option2 with space -option3(довжина все-таки 3). Рідко, але іноді це важливо, наприклад:

bash-4.2$ my_array=('Ffoo bar' 'vOFS=fiz baz')
bash-4.2$ echo 'one foo bar two foo bar three foo bar four' | awk "${my_array[@]/#/-}" '{print$2,$3}'
 two fiz baz three

Якщо я правильно розумію, цю заміну змінної неможливо перетворити на функцію, а також не призначити змінну, правда? Це потрібно перейти безпосередньо до виклику команди.
Конрад Рудольф

1
@KonradRudolph, ви можете привласнити його іншої змінної до тих пір , як ви робите це як привласнення масиву: somevar=("${my_array[@]/#/-}")(Зверніть увагу на круглі дужки навколо значення.) Тоді, звичайно , ви повинні тримати його обробки в вигляді масиву при його використанні: echo "${somevar[@]}". Щодо функцій, то ви занадто обмежені можливостями мови. Ви можете передати масив: somefunc "${my_array[@]/#/-}", то всередині ви будете мати його в $@: function somefunc() { echo "$@"; }. Але те ж $@буде містити й інші параметри, якщо вони існують, і немає іншого способу повернути змінений масив, ніж stdout.
манатура

Як не дивно, не працює з zsh. Перший раз я натрапив на щось, що працює в bash, але не zsh.
Привіт-Ангел

@ Привіт-Ангел, ти впевнений, що ти використовував @як індексний масив? Я поставив це на тест з zsh 5.5.1, і лише за різницею я помітив, що zsh тільки додає кожен елемент, коли він пишеться як ${my_array[@]/#/-}, в той час як bash це робив і для *підписки.
манастирство

О, вибачте, я, можливо, щось накрутив, справді працює на мене!
Привіт-Ангел

2

Я не обробляв, що він знаходиться в масиві, і думав, що пробіл розділений між собою. Це рішення буде працювати з цим, але, враховуючи, що це масив, перейдіть до рішення манатурки ( @{my_array[@]/#/-}).


Це не надто погано sedі з передплатою. Від того, наскільки легким буде регулярний вираз, залежить від того, що ви можете гарантувати щодо варіантів. Якщо параметри - це одне "слово" ( a-zA-Z0-9лише), то достатньо простого початкового межі слова ( \<):

command $(echo $my_array | sed 's/\</-/g') "$1"

Якщо у ваших варіантах є інші символи (швидше за все -), вам знадобиться щось трохи складніше:

command $(echo $my_array | sed 's/\(^\|[ \t]\)\</\1-/g') "$1"

^ відповідає початку рядка, [ \t] відповідає пробілу чи вкладці, \|відповідає будь-якій стороні ( ^або [ \t]), \( \)групам (для\| ) і зберігає результат, \<відповідає початку слова. \1починає заміну, зберігаючи першу відповідність від parens ( \(\)), і, -звичайно, додає потрібний нам тире.

Вони працюють з gnu sed, якщо вони не працюють з вашими, дайте мені знати.

І якщо ви будете використовувати одну і ту ж річ кілька разів, ви можете просто обчислити її один раз і зберегти:

opts="$(echo $my_array | sed 's/\(^\|[ \t]\)\</\1-/g')"
...
command $opts "$1"
command $opts "$2"

Це не спрацювало з Mac OS X sed, тому мені потрібно було користуватися gsed(я встановив за допомогою homebrew). Інша річ: замість цього echo $my_arrayмені потрібно було зробити, echo ${my_array[@]}щоб отримати всі предмети my_array: echo $my_arrayдавав мені лише перший елемент. Але дякую за відповідь та пояснення регексу, особливо про "межу слова", це надзвичайно корисно. :)
Хтось досі використовує вас MS-DOS

Я хотів би вийти зі скрипту, якщо sed система не сумісна з цією функцією межі слова. Як би я це зробив? Друкує версію sed і порівнює її? З якої версії sed була додана ця функція?
Хтось досі використовує вас MS-DOS

1
@ SomebodystillusesyouMS-DOS Перша моя думка - це безпосередньо перевірити, чи працює він - [ $(echo x | sed 's/\</y/') == "yx" ] || exit.
Кевін

2
Це порушиться, якщо будь-який з елементів масиву містить спеціальні символи, найбільш очевидно і невблаганно пробіли.
Жил "ТАК - перестань бути злим"

1
@ SomebodystillusesyouMS-DOS Gilles має рацію, ви повинні змінити свою згоду на манантські роботи.
Кевін

0
[srikanth@myhost ~]$ sh sample.sh 

-option1 -option2 -option3

[srikanth@myhost ~]$ cat sample.sh

#!/bin/bash

my_array=(option1 option2 option3)

echo ${my_array[@]} | sed 's/\</-/g'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.