Може хтось скаже мені, чи слід обробляти цитати навколо змінних у сценарії оболонки?
Наприклад, чи є наступним правильним:
xdg-open $URL
[ $? -eq 2 ]
або
xdg-open "$URL"
[ "$?" -eq "2" ]
А якщо так, то чому?
Може хтось скаже мені, чи слід обробляти цитати навколо змінних у сценарії оболонки?
Наприклад, чи є наступним правильним:
xdg-open $URL
[ $? -eq 2 ]
або
xdg-open "$URL"
[ "$?" -eq "2" ]
А якщо так, то чому?
Відповіді:
Загальне правило: цитуйте його, якщо він може бути порожнім або містити пробіли (або будь-які пробіли) або спеціальні символи (макіяж). Не цитування рядків з пробілами часто призводить до того, що оболонка розбиває один аргумент на багато.
$?
не потрібні лапки, оскільки це числове значення. Чи $URL
потрібно це, залежить від того, що ви там можете дозволити і чи все ще хочете аргументувати, якщо він порожній.
Я схильний завжди цитувати рядки просто за звичкою, оскільки так безпечніше.
IFS=0
, то echo $?
може бути дуже дивно.
cp $source1 $source2 $dest
, але якщо з якоїсь - то непередбаченої причини dest
не чинить приготуйтеся, третій аргумент просто зникає, і він буде мовчки копіювати source1
через source2
замість даючи вам відповідна помилка для порожнього пункту призначення (як це було б, якби ви цитували кожен аргумент).
quote it if...
має процес мислення назад - котирування - це не те, що ви додаєте, коли потрібно, це те, що ви видаляєте, коли потрібно. Завжди загортайте рядки та сценарії в єдині лапки, якщо вам не потрібно використовувати подвійні лапки (наприклад, щоб дозволити розширенню змінної) або не потрібно використовувати жодних лапок (наприклад, робити глобалізацію та розширення імен файлів).
Коротше кажучи, цитуйте все, де вам не потрібна оболонка, щоб виконати розбиття лексеми та розширення підстановки.
Одиничні лапки захищають текст між ними дослівно. Це правильний інструмент, коли потрібно переконатися, що оболонка взагалі не торкається струни. Зазвичай це механізм котирування вибору, коли вам не потрібна змінна інтерполяція.
$ echo 'Nothing \t in here $will change'
Nothing \t in here $will change
$ grep -F '@&$*!!' file /dev/null
file:I can't get this @&$*!! quoting right.
Подвійні лапки підходять, коли потрібна змінна інтерполяція. З відповідними адаптаціями, це також хороший спосіб вирішення, коли вам потрібні поодинокі лапки в рядку. (Немає прямого способу уникнути єдиної цитати між окремими цитатами, оскільки не існує механізму відходу всередині одинарних лапок - якби вони були, вони б не цитували повністю дослівно.)
$ echo "There is no place like '$HOME'"
There is no place like '/home/me'
Жодні лапки не підходять, коли спеціально потрібно оболонці виконати розбиття лексеми та / або розширення підстановки.
Розбиття токена;
$ words="foo bar baz"
$ for word in $words; do
> echo "$word"
> done
foo
bar
baz
На противагу:
$ for word in "$words"; do echo "$word"; done
foo bar baz
(Цикл працює лише один раз, над єдиним цитуваним рядком.)
$ for word in '$words'; do echo "$word"; done
$words
(Цикл виконується лише один раз, через буквальний одноцитуваний рядок.)
Розширення підстановки:
$ pattern='file*.txt'
$ ls $pattern
file1.txt file_other.txt
На противагу:
$ ls "$pattern"
ls: cannot access file*.txt: No such file or directory
(Файлу не названо буквально file*.txt
.)
$ ls '$pattern'
ls: cannot access $pattern: No such file or directory
(Немає файлу з іменем $pattern
!)
Якщо говорити конкретніше, зазвичай слід цитувати все, що містить ім’я файлу (тому що назви файлів можуть містити пробіли та інші метахарактери оболонки). Як правило, цитується все, що містить URL-адресу (оскільки багато URL-адрес містять метахарактеристики оболонки, як ?
і &
). Все, що містить регекс, зазвичай цитується (ditto ditto). Все, що містить значні пробіли, окрім одинарних пробілів між символами, які не містять пробілів, потрібно цитувати (тому що в іншому випадку оболонка буде об'єднувати пробіли у, фактично, єдині пробіли, та обрізає будь-який провідний чи кінцевий пробіли).
Коли ви знаєте, що змінна може містити лише значення, яке не містить метахарактерів оболонки, цитування не є обов'язковим. Таким чином, без котирування $?
в основному добре, оскільки ця змінна може містити лише одне число. Однак "$?"
це також правильно і рекомендується для загальної послідовності та коректності (хоча це моя особиста рекомендація, а не широко визнана політика).
Значення, що не є змінними, в основному відповідають одним і тим же правилам, хоча ви можете також уникати будь-яких метахарактерів, а не цитувати їх. У загальному прикладі URL-адреса з &
в ній буде проаналізована оболонкою як фонова команда, якщо метахарактер не буде уникнути або процитовано:
$ wget http://example.com/q&uack
[1] wget http://example.com/q
-bash: uack: command not found
(Звичайно, це також трапляється, якщо URL-адреса є змінною, що не котирується.) Для статичної рядки найбільше сенсу мають одинарні лапки, хоча тут працює будь-яка форма цитування чи скасування.
wget 'http://example.com/q&uack' # Single quotes preferred for a static string
wget "http://example.com/q&uack" # Double quotes work here, too (no $ or ` in the value)
wget http://example.com/q\&uack # Backslash escape
wget http://example.com/q'&'uack # Only the metacharacter really needs quoting
Останній приклад також пропонує іншу корисну концепцію, яку я люблю називати "котируванням пилу". Якщо вам потрібно змішати одинарні та подвійні лапки, ви можете використовувати їх поруч один з одним. Наприклад, наступні цитовані рядки
'$HOME '
"isn't"
' where `<3'
"' is."
можна склеювати спільно назад, утворюючи єдину довгу струну після токенізації та видалення цитат.
$ echo '$HOME '"isn't"' where `<3'"' is."
$HOME isn't where `<3' is.
Це не дуже розбірливо, але це звичайна техніка, і тому добре знати.
Як сторону, сценарії зазвичай не повинні використовуватись ls
ні для чого. Щоб розгорнути підстановку, просто ... скористайтеся нею.
$ printf '%s\n' $pattern # not ``ls -1 $pattern''
file1.txt
file_other.txt
$ for file in $pattern; do # definitely, definitely not ``for file in $(ls $pattern)''
> printf 'Found file: %s\n' "$file"
> done
Found file: file1.txt
Found file: file_other.txt
(Цикл в останньому прикладі абсолютно зайвий; printf
спеціально працює добре з декількома аргументами. stat
Також петля над відповідним символом є поширеною проблемою, і часто робиться неправильно.)
Змінна, що містить список токенів, на які переходить цикл, або макіяж для розширення, зустрічається рідше, тому ми іноді скорочуємо "цитувати все, якщо ви точно не знаєте, що ви робите".
Ось триточкова формула цитат загалом:
Подвійні цитати
У контекстах, де ми хочемо придушити розщеплення слів і глобалізацію. Також у контекстах, де ми хочемо, щоб літерал трактувався як рядок, а не регулярний вираз.
Одиночні цитати
У рядкових літералах, де ми хочемо придушити інтерполяцію та спеціальну обробку відкосів. Іншими словами, ситуації, коли використання подвійних лапок було б недоцільним.
Без цитат
У контекстах, де ми абсолютно впевнені, що немає жодних проблем з розділенням або глобалізацією слів, або ми хочемо розділити слова і розширити їх .
Приклади
Подвійні цитати
"StackOverflow rocks!"
, "Steve's Apple"
)"$var"
, "${arr[@]}"
)"$(ls)"
, "`ls`"
)"/my dir/"*
)"single'quote'delimited'string"
)"${filename##*/}"
)Одиночні цитати
'Really costs $$!'
, 'just a backslash followed by a t: \t'
)'The "crux"'
)$'\n\t'
)$'{"table": "users", "where": "first_name"=\'Steve\'}'
)Без цитат
$$
, $?
, і $#
т.д.)((count++))
, "${arr[idx]}"
,"${string:start:length}"
[[ ]]
висловлювання, що не містить питань розбиття слів і проблем з глобалізацією (це питання стилю і думки можуть сильно відрізнятися)for word in $words
)for txtfile in *.txt; do ...
)~
тлумачити як $HOME
( ~/"some dir"
але не "~/some dir"
)Дивитися також:
"ls" "/"
фразу "всі рядкові контексти", потрібно кваліфікувати більш ретельно.
[[ ]]
, цитування має значення в правій частині =
/ ==
і =~
: це робить різницю між інтерпретацією рядка як шаблону / регулярного вираження або буквально.
$'...'
), безумовно, повинні мати свій розділ.
"ls" "/"
замість більш поширених ls /
, і я вважаю це головним недоліком в інструкціях.
case
:)
Як правило, я користуюся цитатами як "$var"
безпечні, якщо я не впевнений, що $var
це не містить місця.
Я використовую $var
як простий спосіб з'єднання ліній:
lines="`cat multi-lines-text-file.txt`"
echo "$lines" ## multiple lines
echo $lines ## all spaces (including newlines) are zapped
Для використання змінних у скрипті оболонки використовуйте цитовані змінні як цитовані, це означає, що змінна може містити пробіли або спеціальний символ, який не впливатиме на виконання сценарію оболонки. В іншому випадку, якщо ви впевнені, що у імені змінної немає пробілів чи спеціального символу, тоді ви можете використовувати їх без "".
Приклад:
echo "$ url name" - (Можна використовувати завжди)
echo "$ url name" - (Не можна використовувати в таких ситуаціях, тому будьте обережні, перш ніж використовувати його)