Чому вигук "!" Іноді засмучує удар?


14

Я усвідомлюю, що це !має особливе значення для командного рядка в контексті історії командного рядка, але окрім цього, у сценарії рунінгу знак оклику іноді може викликати помилку розбору.
Я думаю, що це щось стосується event, але я не маю уявлення, що це за подія чи що вона робить. Незважаючи на це, одна і та ж команда може поводитися по-різному в різних ситуаціях.
Останній приклад, наведений нижче, викликає помилку; але чому, коли той самий код працював поза заміною команди? .. з використанням GNU bash 4.1.5

# This works, with or without a space between ! and p
  { echo -e "foo\nbar" | sed -nre '/foo/! p'
    echo -e "foo\nbar" | sed -nre '/foo/!p'; }
# bar
# bar

# This works, works when there is a space between ! and p
  var="$(echo -e "foo\nbar" | sed -nre '/foo/! p')"; echo "$var"
# bar

# This causes an ERROR, with NO space between ! and p
  var="$(echo -e "foo\nbar" | sed -nre '/foo/!p')"; echo "$var"
# bash: !p': event not found


@Warren .. Дякую. Я бачив цю QA, але це дійсно говорить лише про те, як уникнути зворотної косої риси ... Моє питання стосується більше того, чому, здавалося б, уникнутий код працює в одній ситуації, а не в іншій ...
Peter.O

@fred: "начебто вже втекла"? Я взагалі не бачу жодних виходів, і ви використовуєте подвійні лапки. Дивіться мою (переглянуту) відповідь. Яка частина, на вашу думку, втекла?
Калеб

@Caleb. Так, я використав неправильний термін .. protectedбув би доречнішим. (захищено "єдиними цитатами")
Peter.O

Якщо ви переймаєтесь простими завданнями, ви можете використовувати var=$(…)(без подвійних лапок), і це буде працювати так, як (я думаю). Це по - , як і раніше «безпечно» , оскільки значення частина присвоєння простого не підлягає слово розщеплення або підстановка (хоча це не може бути правдою завдань , зроблених з допомогою вбудованих команд (наприклад export, localі т.д.) при всіх оболонках). На жаль, це не виходить за рамки простих призначень, оскільки подвійні лапки є способом захисту від розбиття слів і глобалізації, в той же час отримуючи інші види розширення в інших контекстах.
Кріс Джонсен

Відповіді:


12

!Персонаж викликає заміну історії в Bash. Якщо слідує за рядком (як у вашому невдалому прикладі), він намагається розгорнутись до останньої події історії, яка почалася з цієї рядки. Так само, як $varрозширюється значення цього рядка, воно !echoрозширюється до останньої команди ехо у вашій історії.

Простір - це руйнівний характер у таких розширеннях. Спочатку зауважте, як це буде працювати зі змінними:

# var="like"
# echo "$var"
like
# echo "$"
$
# echo "Do you $var frogs?"
Do you like frogs?       <- as expected, variable name broken at space
# echo "Do you $varfrogs?"
Do you?                  <- $varfrogs not defined, replaced with blank
# echo "Do you $ var frogs?"
Do you $ var frogs?      <- $ not a valid variable name, ignored

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

Ви можете уникнути подібної заміни як змінної, так і розширення історії, використовуючи одинарні лапки. У ваших перших прикладах використовувались одинарні цитати, і так добре виходило. Останні ваші приклади містяться в подвійних лапках, і таким чином баш сканував їх на послідовності розширення, перш ніж це робило щось інше. Єдина причина, по якій перша не відключилася, полягає в тому, що пробіл є символом перерви, як показано вище.


Дякую Калебу .. Ще одну мою попередню концепцію відхилив ... Я думав, що баш-синтаксичний аналіз був зроблений з найпотаємнішої дужки або дужки, а потім працював назовні ... Схоже, баш розбирає по-різному моє припущення.
Пітер.O

1
Цитування достатньо заплутане в bash, не змінюючись у вкладених рядках. Насправді заміни відбуваються дуже рано в процесі. Розглянемо цей приклад:var=word; echo "test '$var'"; echo 'test "$var"'
Калеб

.. Так не зрозумів. Мені було відомо про введення лапок в лапки ... Моє непорозуміння полягало в тому, що я думав, що код у дужках підстановки команди буде розбиратися окремо, на те, що оточує ці дужки; але, мабуть, не .. дякую.
Пітер.O

6

Як уже говорив Калеб , !використовується для виклику заміни історії баша.

Якщо ви, як я, вважаєте, що вам не потрібна така функція, ви можете відключити її, вставляючи такий рядок у ~/.bashrc:

set +H

Мені це не потрібно, оскільки історію можна відновити стрілкою вгору і Ctrl- rпоступовим зворотним пошуком. Детальний список ярликів див. На сторінці посібника bash, в розділі Команди маніпулювання історією .


2
Як ти живеш без !!?
Калеб

Дякую. Я б подумав, що це може бути проблемою з портативністю, але використання set +Hсценарію працює так само добре :) +1
Peter.O

2
@fred: дивно, загалом, розширення історії "включено" лише для інтерактивних оболонок.
enzotib

@enzo .. Ще раз дякую .. Я протестував це з командного рядка .. Ах! Якби навчання не було таким веселим, було б нудно ... я згадав про каву? це теж допомагає :)
Peter.O

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

2

ваш перший приклад:

{ echo -e "foo\nbar" | sed -nre '/foo/! p'
    echo -e "foo\nbar" | sed -nre '/foo/!p'; }

може бути зведений до

echo '! p' 
echo '!p'

У межах однієї лапки всі символи зберігають свої буквальні значення. Таким чином !втратив своє особливе значення, і розширення історії не заздалегідь формується.

ваш другий і третій приклади:

var="$(echo -e "foo\nbar" | sed -nre '/foo/! p')"; echo "$var"

var="$(echo -e "foo\nbar" | sed -nre '/foo/!p')"; echo "$var"

може бути зведений до

echo "'! p'"

echo "'!p'"

'! p'і '!p'по суті є частинами подвійних цитуваних рядків.

У подвійних лапках все символи зберігають свої буквальні значення , за винятком $ , `, \та !.

Це означає, що єдині цитати '! p'та '!p'втратили своє особливе значення (тобто: не в змозі уникнути !), але !все ще зберігають своє особливе значення, таким чином відбувається розширення історії.

Однак, коли !супроводжується символом пробілу, розширення історії не виконується.

Цитуючи man bash:

ЦИТУВАННЯ

[...]

Замикання символів в одиничних лапках зберігає буквальне значення кожного символу в лапках. [...]

Замикання символів у подвійних лапках зберігає буквальне значення всіх символів у лапках, за винятком $, `, \ та, коли розширення історії включено, !. [...] Якщо увімкнено, розширення історії буде виконано, якщо тільки! З'явлення подвійних лапок уникне за допомогою зворотної косої риски. Нахил, що передує! не видаляється.

РОЗШИРЕННЯ ІСТОРІЇ

[...]

Розширення історії впроваджуються появою символу розширення історії, який є! за замовчуванням. Лише зворотна косою червою (\) та одиничні лапки можуть цитувати характер розширення історії.

Кілька символів перешкоджають розширенню історії, якщо його знайдено відразу після символу розширення історії, навіть якщо воно не цитується: пробіл, вкладка, нова лінія, повернення каретки та =. Якщо параметр оболонки extglob увімкнено, (також буде гальмувати розширення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.