느낌표 "!"가 때때로 배쉬를 엉망으로 만드는 이유는 무엇입니까?

느낌표 "!"가 때때로 배쉬를 엉망으로 만드는 이유는 무엇입니까?

!명령줄 기록의 맥락에서 이것이 명령줄에 특별한 의미가 있다는 것을 알고 있지만 , 그 외에도 느낌표는 때때로 스크립트 실행 시 구문 분석 오류를 일으킬 수 있습니다.
a와 관련이 있는 것 같은데 eventa가 무엇인지 모르겠습니다.이벤트그것이 무엇인지, 무엇을 하는지. 그럼에도 불구하고 동일한 명령이 상황에 따라 다르게 작동할 수 있습니다.
아래의 마지막 예에서는 오류가 발생하지만 동일한 코드가 명령 대체 외부에서 작동할 때 왜 작동합니까? ..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

답변1

!역할은 bash의 기록 대체를 호출합니다(대화형 셸에서 기본적으로 활성화됨). 실패한 예에서와 같이 문자열이 뒤에 오면 해당 문자열로 시작하는 마지막 기록 이벤트로 확장을 시도합니다. 마치 $var이 문자열의 값을 확장하면 !echo기록의 마지막 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

역사 확장에서도 같은 일이 일어납니다. 느낌표( !)는 기록 대체 시퀀스를 시작하지만 뒤에 문자열이 오는 경우에만 해당됩니다. 교체 시퀀스의 일부가 아닌 문자 그대로의 폭발을 만들려면 공백을 추가하세요.

작은따옴표를 사용하면 이러한 변수 대체 및 기록 확장을 방지할 수 있습니다. 첫 번째 예에서는 작은따옴표를 사용하므로 제대로 작동합니다. 마지막 예는 큰따옴표로 묶여 있으므로 bash는 다른 작업을 수행하기 전에 확장 시퀀스를 검색합니다. 첫 번째가 트립되지 않는 유일한 이유는 위에 표시된 것처럼 공백이 깨지는 문자이기 때문입니다.

답변2

이미케일럽 라고, !bash의 기록 교체를 호출하는 데 사용됩니다.

나처럼 그러한 기능이 필요하지 않다고 생각되면 다음 줄을 삽입하여 기능을 비활성화할 수 있습니다 ~/.bashrc.

set +H

위쪽 화살표 및 -증분 역방향 검색으로 기록을 복원할 수 있기 때문에 필요하지 않습니다 Ctrl. rbash의 매뉴얼 페이지 섹션을 참조하십시오.기록을 조작하는 명령자세한 단축키 목록에 대해 알아보세요.

답변3

첫 번째 예:

{ 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 쉘 옵션이 활성화된 경우 확장도 억제됩니다.

관련 정보