Змінна лапками "$ ()"


12

Я написав цей сценарій:

#!/bin/bash
while [ true ] 
do
    currentoutput="$(lsusb)"
    if [ "$currentoutput" != "$lastoutput" ]
    then
        echo "" date and Time >> test.log
        date +%x_r >> test.log
        lastoutput="$(lsusb)"
        lsusb >> test.log
    fi
    sleep 5
done

Я новачок, який намагається швидко вчитися, і у мене виникло питання про лапки змінної .

Поставте змінну між $ (), я розумію, але навіщо лапки навіть у ifвиписці? Це зробити вкладену команду?


5
Зауважте, що while [ true ]це створює нескінченний цикл, але, можливо, не з тієї причини, яку ви вважаєте, що це робить; while [ false ] також створює нескінченний цикл, оскільки з одним аргументом [ ... ]досягається успіху, якщо цей аргумент є не порожнім рядком. while trueнасправді запустить команду з ім'ям true(яка завжди вдається).
чепнер

4
Ви не ставите змінну всередину $(). Ви кладете команду всередину $().
Wildcard

Відповіді:


23

Лапки запобігають "розбиткові слова". Тобто: розбиття змінних на кілька елементів у символах пробілу (або якщо бути точнішим, на пробіли, вкладки та нові рядки, як визначено у значенні $IFSзмінної оболонки за замовчуванням ).

Наприклад,

$ var="one two"
$ howmany(){ echo $#;  }
$ howmany $var
2
$ howmany "$var"
1

Тут ми визначаємо howmanyфункцію, яка просто дає нам знати, скільки позиційних параметрів задано. Як бачите, до змінної передаються два елементи, і з лапок текст у змінній трактується як одна одиниця.

Це важливо для точного передачі інформації. Наприклад, якщо змінна містить шлях до файлу, а ім'я файлу містить пробіли в будь-якому місці шляху, команда, яку ви намагаєтеся запустити, може провалитись або дати неточні результати. Якби ми намагалися створити файл зі $varзмінною, touch $varстворили б два файли, але touch "$var"лише один.

Те саме стосується і вашого [ "$currentoutput" != "$lastoutput" ]боку. Цей конкретний тест виконує порівняння на двох рядках. Коли тест запускається, [команда повинна побачити 3 аргументи - текстовий рядок, !=оператор та інший текстовий рядок. Збереження подвійних лапок запобігає поділу слів, і [команда бачить саме ці 3 аргументи. Тепер, що станеться, якщо змінні не цитуються?

$ var="hello world"
$ foo="hi world"
$ [ $var != $foo ]
bash: [: too many arguments
$ 

Тут слово розщеплення відбувається, і замість цього [бачить два рядки helloі worldпотім !=, за якими слідують дві інші рядки hi world. Ключовим моментом є те, що без подвійних лапок зміст змінних розуміється як окремі одиниці, а не один цілий елемент.

Призначення заміни команд не вимагає подвійних лапок, як у

var=$( df )

де dfзберігається вихід команди var. Однак корисно завжди подвоювати змінні цитата та підмінювати команди, $(...)якщо ви насправді не хочете, щоб вихід розглядався як окремі елементи.


З боку, примітка

while [ true ]

частина може бути

while true

[це команда, яка оцінює свої аргументи, і [ whatever ]завжди є вірною незалежно від того, що знаходиться всередині. На противагу цьому while trueвикористовується команда, trueяка завжди повертає статус успішного виходу (і саме для цього whileпотрібен цикл). Різниця полягає в трохи більшій чіткості та меншій кількості проведених тестувань. Крім того, ви можете також використовувати :замістьtrue

Подвійні лапки echo "" date and Timeчастково, можливо, можуть бути видалені. Вони просто вставляють порожній рядок і додають додатковий простір у вихід. За бажанням сміливо тримайте їх там, але в цьому випадку особливої ​​функціональної цінності немає.

lsusb >> test.log

Ця частина, можливо, може бути замінена на echo "$currentoutput" >> test.log. Немає причин запускатись lsusbзнову після запуску currentoutput=$(lsusb). У випадках, коли у вихідному рядку потрібно зберігати нові рядки - цінність можна побачити у виконанні команди кілька разів, але в lsusbцьому немає необхідності. Чим менше зовнішніх команд ви зателефонуєте, тим краще, тому що кожен виклик невбудованої команди вимагає витрат на процесор, використання пам'яті та час виконання (навіть якщо команди, ймовірно, попередньо завантажені з пам'яті).


Дивись також:


1
Чудова відповідь, якщо ви цього ще не зробили, вам слід написати книгу bash. Але в останній частині echo "$currentoutput" >> test.log краще не повинно бутиprintf '%s\n' "$currentoutput" >> test.log ?
pLumo

2
@RoVo Так, printfслід віддавати перевагу мобільності. Оскільки тут ми використовуємо специфічний для bash скрипт, його можна виправдати echo. Але ваше спостереження дуже правильне
Сергій Колодяжний

1
@SergiyKolodyazhnyy, ... Я не впевнений, що echoце абсолютно надійно, навіть коли оболонка, як відомо, є bash (а значення xpg_echoта posixпрапори відомі); якщо ваше значення може бути -nабо -e, воно може зникнути, а не надрукувати.
Чарльз Даффі

4

У currentoutput="$(lsusb)"lsusb не є змінною, це команда. Що робить цей оператор, він виконує lsusbкоманду і присвоює свій результат currentoutputзмінній.

Старіший синтаксис для цього був

currentoutput=`lsusb`

ви можете знайти його в багатьох прикладах та сценаріях

Щоб відповісти на іншу частину вашого запитання, if [ ]це лише те, як визначається синтаксис для ifв bash. Детальніше дивіться на https://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html


3
Я думаю, що важливо сказати, що [ ]це насправді testкоманда. Ви також можете використовувати ifоператори з іншими командами, оскільки це спирається на 0 або ненульовий тест їхнього коду виходу.
Арронічний

... дійсно, бігти набагато краще, if grep -qe "somestring" fileніж бігати grep -qe "somestring" file; if [ $? = 0 ]; then ..., тому твердження, що if [ ...є частиною визначення ifсинтаксису, є не просто оманливим, а призводить до поганих практик.
Чарльз Даффі

4

Далі виконується зовнішня команда commandта повертає її вихід.

"$(command)"

Без дужок / дужок це буде шукати змінну замість виконання команди:

"$variable"

Що стосується різниці між $variableі "$variable", це стає актуальним, коли $variableмістить пробіли. При використанні "$variable"весь вміст змінної буде вставлено в один рядок, навіть якщо вміст містить пробіли. При використанні $variableвмісту змінної може бути розширений список аргументів з декількох аргументів.


HI @thomasrutter, вибачте, я мав на увазі лапки ... Я редагую свій коментар зараз!
Шанхара

1

Щоб бути протилежним - bash рекомендує використовувати конструкцію [[ ... ]]over, [ ... ]щоб уникнути необхідності цитувати змінні в тесті, і тому пов'язані проблеми розщеплення слів, на які вказували інші.

[надається в bash для сумісності POSIX із скриптами, призначеними для запуску, #!/bin/shабо тими, які переносяться на bash - здебільшого вам слід уникати цього на користь [[.

напр

# With [ - quotes are needed

$ foo='one two'; bar='one two'; [ $foo = $bar ] && echo "they're equal"
-bash: [: too many arguments

$ foo='one two'; bar='one two'; [ "$foo" = "$bar" ] && echo "they're equal"                                                                                                              
they're equal

# versus [[ - quotes not needed

$ foo='one two'; bar='one two'; [[ $foo = $bar ]] && echo "they're equal"
they're equal

bashне дає такої рекомендації; він забезпечує, [[ ... ]] що може бути зручнішим і має деякий функціонал, який [ ... ]не дає, але немає проблеми з [ ... ] правильним використанням .
чепнер

@chepner - Можливо, мені тут повинно бути зрозуміліше. Впевнені, що це не явна рекомендація в баш-manpage, але з огляду на те, [[що все [робить і більше, і все-таки багато разів [подорожує людей, а потім спробуйте перевстановити функції [[назад, щоб [тільки не вийти з ладу і залишити помилки розсіяними - справедливо сказати, що це рекомендація спільноти , наскільки більшість відповідних посібників у стилі баш радять ніколи не використовувати[ .
шалом
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.