Переменная с кавычками «$ ()»

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выражении? Это сделать вложенную команду?

Shankhara
источник
5
Обратите внимание, что while [ true ]это создает бесконечный цикл, но, возможно, не по той причине, о которой вы думаете; while [ false ] также создает бесконечный цикл, потому что с одним аргументом [ ... ]успешно, если этот аргумент является непустой строкой. while trueфактически запустит команду с именем true(которая всегда будет успешной).
chepner
4
Вы не помещаете переменную внутрь $(). Вы вводите команду внутри $().
Wildcard

Ответы:

22

Кавычки предотвращают «расщепление слов». Это значит: разбивать переменные на несколько элементов по пробельным символам (или, точнее, по пробелам, табуляциям и символам новой строки, как определено в значении $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это вполне надежны , даже если оболочка , как известно Баш (и значение 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.

marosg
источник
3
Я думаю, что важно сказать, что [ ]это на самом деле testкоманда. Вы также можете использовать ifоператоры с другими командами, так как они основаны на 0 или ненулевом тесте их кода выхода.
Arronical
... действительно, гораздо лучше запускать, if grep -qe "somestring" fileчем запускать grep -qe "somestring" file; if [ $? = 0 ]; then ..., поэтому утверждение, if [ ...являющееся частью определения ifсинтаксиса, не просто вводит в заблуждение, но ведет к неправильным действиям.
Чарльз Даффи
4

Следующая команда запускает внешнюю команду commandи возвращает ее вывод.

"$(command)"

Без скобок / скобок, это будет искать переменную вместо выполнения команды:

"$variable"

Что касается разницы между $variableи "$variable", это становится актуальным, когда $variableсодержит пробелы. При использовании "$variable"все содержимое переменной будет вставлено в одну строку, даже если содержимое содержит пробелы. При использовании $variableсодержимое переменной может быть расширено в список аргументов из нескольких аргументов.

thomasrutter
источник
Привет @thomasrutter, извини, я имел в виду кавычку ... Я сейчас редактирую свой комментарий!
Шанхара
0

Для противоположности - 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
shalomb
источник
bashне дает такой рекомендации; он предоставляет [[ ... ]] более удобный способ и имеет некоторые функциональные возможности, которых [ ... ]нет, но нет проблем с [ ... ] правильным использованием .
chepner
@chepner - Может быть, я должен быть яснее здесь. Конечно, это не явная рекомендация на man-странице bash, но, учитывая, что [[делает все [, и даже больше, и все же много раз [сбивает людей с толку, а затем пытается и модернизировать функции [[обратно, чтобы [только потерпеть неудачу и оставить ошибки рассеянными - справедливо сказать, что это Рекомендация сообщества в том, что касается наиболее важных руководств по bash, никогда не используйте[ .
шалом