Як я можу розширити цитовану змінну до нічого, якщо вона порожня?


21

Скажіть, у мене сценарій:

some-command "$var1" "$var2" ...

І, якщо var1це порожнє, я вважаю за краще, щоб його замінили нічим, а не порожнім рядком, щоб команда виконувалася:

some-command "$var2" ...

і ні:

some-command '' "$var2" ...

Чи є простіший спосіб, ніж тестування змінної та умовно включення її?

if [ -n "$1" ]; then
    some-command "$var1" "$var2" ...
    # or some variant using arrays to build the command
    # args+=("$var1")
else
    some-command "$var2" ...
fi

Чи є підміна параметрів, ніж може розширюватися до нічого в bash, zsh тощо? Я, можливо, все ще хотів би використовувати глобалінг в решті аргументів, тому відключення цього та відміняння змінної не є варіантом.


Я знав, що бачив і, ймовірно, використовував це раніше, але це виявилося важким для пошуку. Тепер, коли Майкл показав синтаксис, я згадав, де я вперше побачив його досить швидко: unix.stackexchange.com/a/269549/70524 , unix.stackexchange.com/q/68484/70524
muru

Якщо ви знаєте, що це якась заміна параметрів, чому ви не заглянули в розділ розширення параметрівman сторінки? (-;
Філіппос

1
@Philippos Я не знав, що це було в той час, тільки що я бачив або використовував його раніше. Відомі знання та невідомі знання. :(
muru

1
додаткові точки cookie для згадування використання масиву для утримання аргументів у самому питанні.
ilkkachu

Відповіді:


29

Оболонки, сумісні з Posix, і Bash мають ${parameter:+word} :

Якщо параметр не встановлений або недійсний, нуль заміняється; в іншому випадку розширення слова (або порожній рядок, якщо слово пропущено) заміняється.

Тож ви можете просто зробити:

${var1:+"$var1"}

і повинні var1бути перевірені та "$var1"використовуватись, якщо вона встановлена ​​та не порожня (із звичайними правилами подвійного цитування). Інакше він розширюється ні до чого. Зауважте, що тут цитується лише внутрішня частина, а не вся справа.

Те саме працює і в zsh. Ви повинні повторити змінну, тому вона не є ідеальною, але вона працює точно так, як ви хотіли.

Якщо ви хочете, щоб змінна set-but-empty розширилася до порожнього аргументу, використовуйте ${var1+"$var1"}замість цього.


1
Досить добре для мене. Отже, у цьому вся справа не цитується, лише wordчастина є.
муру

1
Оскільки запитували питання "bash, zsh тощо" (і для архіву Q&A), я редагував, щоб відобразити, що це функція posix. Навіть якщо ви додасте тег / bash до питання після мого коментаря. (-;
Філіппос

2
Я взяв на себе сміття редагувати в різниці між :+і +.
ilkkachu

Дякую за рішення, сумісне з POSIX! Я намагаюся використовувати sh/ dashдля потенційних сценаріїв chokepoint, тому я завжди ціную це, коли хтось показує, як річ можлива, не вдаючись до Баша.
JamesTheAwesomeDude

Я шукав зворотний ефект (Розгорніть на щось інше, якщо він починається як нульовий). Виявляється, цю логіку можна перевернути, змінивши + на a - як у: $ {empty_var: -replacement}
Алекс Янсен

5

Ось що zshробиться за замовчуванням, коли ви опускаєте лапки:

some-command $var1 $var2

Насправді, єдиною причиною, чому ви все ще потрібні цитати в zsh навколо розширення параметра, є уникнення такої поведінки (порожнє видалення), оскільки zshне має інших проблем, які впливають на інші оболонки, коли ви не цитуєте розширення параметрів (неявне розділення + глобус) .

Ви можете зробити те ж саме з іншими оболонками, схожими на POSIX, якщо вимкніть спліт та глобул:

(IFS=; set -o noglob; some-command $var1 $var2)

Тепер я заперечую, що якщо ваша змінна може мати значення 0 або 1, це має бути масив, а не скалярна змінна, і використовуйте:

some-command "${var1[@]}" "${var2[@]}"

А використання var1=(value)при var1повинен містити одне значення, var1=('')коли вона містить один порожній значення, і var1=()коли вона не містить НЕ значення.


0

Я зіткнувся з цим за допомогою rsync в скрипті bash, який запустив команду з або без a -nдля перемикання сухих прогонів. Виявляється, rsync і ряд команд gnu приймають ''перший правильний аргумент і діють інакше, ніж якби його не було.

Налагодження цього зайняло досить багато часу, оскільки нульові параметри майже повністю непомітні.

Хтось із списку rsync показав мені спосіб уникнути цієї проблеми, а також значно спростив моє кодування. Якщо я правильно це розумію, це варіація останньої пропозиції @ Stéphane Chazelas.

Створіть ваші аргументи команди на ряді окремих змінних. Вони можуть бути встановлені в будь-якому порядку або логіці, яка відповідає проблемі.

Потім, в кінці, використовуйте змінні, щоб побудувати масив зі всім належним місцем і використовувати це як аргументи до фактичної команди.

Таким чином команда видається лише в одному місці в коді, а не повторюється для кожного варіанту аргументів.

Будь-яка змінна, яка порожня, просто зникає за допомогою цього методу.

Я знаю, що використання eval сильно хмуриться. Я не пам’ятаю всіх деталей, але мені здалося, що мені потрібно, щоб такі речі працювали таким чином - щось стосується обробки параметрів із вбудованим пробілом.

Приклад:

dry_run=''
if [[ it is a test run ]]
then
  dry_run='-n'
fi
...
rsync_options=(
  ${dry_run}
  -avushi
  ${delete}
  ${excludes}
  --stats
  --progress
)
...
eval rsync "${rsync_options[@]}" ...

Це гірший спосіб робити те, що я описав у запитанні (умовно будуючи масив аргументів). Залишаючи змінні без котирування, хто знає, до яких проблем ви залишаєте себе відкритими.
муру

@muru Ти маєш рацію, але мені все одно потрібно щось подібне. Я не зовсім розумію, як це виправити, використовуючи методи з інших відповідей. Було б чудово побачити фрагмент коду, зроблений правильно, який поєднує все це. Пізніше я зіграю з останнім варіантом Стефана, оскільки це може зробити те, що я хочу.
Джо


Просто повернувся до цього. Дякую за приклад. Це має сенс. Я буду працювати з цим.
Джо

У мене є подібний випадок використання тут, але ви можете зробити щось подібне: if ["$ dry" == "true"]; тоді сухий = "- n"; fi ... тому, якщо змінна суха є істинною, ви робите це -n, ​​а потім будуєте свою команду rsync, як зазвичай, і матимете її так: rsync $ {dry1: + "$ dry1"} ... якщо це нуль нічого не станеться, інакше він стане -n у вашій команді
Freedo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.