bash에서 와일드 카드 / 별표 문자를 어떻게 피할 수 있습니까?


136

예를 들면 다음과 같습니다.

me$ FOO="BAR * BAR"
me$ echo $FOO
BAR file1 file2 file3 file4 BAR

그리고 사용하여 \이스케이프 문자를 :

me$ FOO="BAR \* BAR"
me$ echo $FOO
BAR \* BAR

분명히 바보 같은 짓을하고 있습니다.

출력은 어떻게 얻 BAR * BAR습니까?

답변:


139

설정 $FOO이 충분하지 않을 때 인용 . 변수 참조도 인용해야합니다.

me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR

8
이것은 신비 롭습니다. 왜 이럴까요? 무슨 일이야?
tofutim

변수가 확장되기 때문에
Daniel

103

짧은 답변

다른 사람들이 말했듯이 이상한 행동을 막기 위해 항상 변수를 인용해야합니다. 사용 그래서 "$ foo는"에코 대신에 단지 $ foo는 에코 .

긴 대답

나는이 예가 그 얼굴에 보이는 것보다 더 많은 일이 있기 때문에 더 많은 설명이 필요하다고 생각합니다.

첫 번째 예제를 실행 한 후 쉘이 분명히하고 있다고 생각했기 때문에 혼란이 어디에서 발생했는지 알 수 있습니다.

  1. 파라미터 확장
  2. 파일 이름 확장

첫 번째 예에서 :

me$ FOO="BAR * BAR"
me$ echo $FOO

매개 변수 확장 후는 다음과 같습니다.

me$ echo BAR * BAR

그리고 파일 이름 확장 후 다음과 같습니다.

me$ echo BAR file1 file2 file3 file4 BAR

그리고 echo BAR * BAR명령 행에 입력하면 그것들이 동등한 것을 볼 수 있습니다.

"*를 피하면 파일 이름 확장을 막을 수 있습니다."

두 번째 예에서 :

me$ FOO="BAR \* BAR"
me$ echo $FOO

매개 변수 확장 후는 다음과 같아야합니다.

me$ echo BAR \* BAR

파일 이름 확장 후 다음과 같아야합니다.

me$ echo BAR \* BAR

명령 행에 "echo BAR \ * BAR"을 직접 입력하면 이스케이프로 파일 이름 확장이 방지되므로 실제로 "BAR * BAR"이 인쇄됩니다.

그렇다면 왜 $ foo를 사용할 수 없었습니까?

견적 제거라는 세 번째 확장이 있기 때문입니다. bash 수동 따옴표 제거는 다음과 같습니다.

앞의 확장 후에는 위의 확장 중 하나에서 발생하지 않은 인용되지 않은 문자 '\', '' '및' " '가 모두 제거됩니다.

따라서 명령 행에 명령을 직접 입력하면 이스케이프 문자는 이전 확장의 결과가 아니므로 에코 명령으로 보내기 전에 BASH가이를 제거하지만 두 번째 예에서는 "\ *" 이전 매개 변수 확장의 결과이므로 제거되지 않습니다. 결과적으로 echo는 "\ *"를 수신하고 인쇄됩니다.

첫 번째 예의 차이점- "*"는 따옴표 제거로 제거 될 문자에 포함되지 않습니다.

이것이 의미가 있기를 바랍니다. 결국 같은 결론-따옴표 만 사용하십시오. 방금 매개 변수 및 파일 이름 확장 만 작동하는 경우 논리적으로 작동 해야하는 탈출이 작동하지 않는 이유를 설명하려고 생각했습니다.

BASH 확장에 대한 자세한 설명은 다음을 참조하십시오.

http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions


1
좋은 대답입니다! 이제 나는 그런 바보 같은 질문을 한 느낌이 들지 않습니다. :-)
andyuk

1
특수 문자를 이스케이프하는 유틸리티가 있습니까?
Jigar Joshi

"이상한 행동을 막기 위해 항상 변수를 인용해야한다"– 문자열로 사용하고자 할 때
Angelo


위의 @finnw의 답변이 질문에 직접 대답하지만이 답변은 그 이유 를 설명 하는 데 훨씬 뛰어나서 자체 시나리오에서 사용하는 방법을 추정하는 데 훨씬 도움이됩니다. 이것은 우리가 더 많이 볼 필요가있는 종류의 대답입니다.
Nicolas Coombs

54

이 오래된 스레드에 약간을 추가하겠습니다.

보통은

$ echo "$FOO"

그러나이 구문에도 문제가 있습니다. 다음 스크립트를 고려하십시오.

#!/bin/bash
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"

*그대로 전달해야 curl하지만 같은 문제가 발생합니다. 위의 예제는 작동하지 않으며 (현재 디렉토리의 파일 이름으로 확장됩니다) 어느 것도 작동하지 않습니다 \*. 또한 $curl_opts단일 (유효하지 않은) 옵션으로 인식되므로 인용 할 수 없습니다 curl.

curl: option -s --noproxy * -O: is unknown
curl: try 'curl --help' or 'curl --manual' for more information

따라서 전역 패턴에 적용하거나 내장 플래그를 사용하는 경우 파일 이름 확장을 방지하기 위해 bash변수 $GLOBIGNORE를 사용하는 것이 좋습니다 set -f.

#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"  ## no filename expansion

원래 예제에 적용 :

me$ FOO="BAR * BAR"

me$ echo $FOO
BAR file1 file2 file3 file4 BAR

me$ set -f
me$ echo $FOO
BAR * BAR

me$ set +f
me$ GLOBIGNORE=*
me$ echo $FOO
BAR * BAR

4
좋은 설명 감사합니다! 내 유스 케이스는 SELECT * FROM etc.이것이 유일한 방법입니다.
knutole

1
set -f 솔루션을 제시해 주셔서 감사합니다!
hachre

5
FOO='BAR * BAR'
echo "$FOO"

이것은 효과가 있지만 첫 번째 줄의 작은 따옴표를 큰 따옴표로 변경할 필요는 없습니다.
finnw

1
아니요. 그렇지 않지만 특수 쉘 문자를 포함하고 대체를 원하지 않는 경우 작은 따옴표를 사용하는 습관이 선호됩니다.
tzot


3

printf오히려 사용 습관을들이는 것이 좋습니다.echo커맨드 라인 .

이 예제에서는 많은 이점을 제공하지 않지만 더 복잡한 출력으로 더 유용 할 수 있습니다.

FOO="BAR * BAR"
printf %s "$FOO"

왜 지옥에? printf는 별도의 프로세스 (적어도 내장 bash가 아님)이며 printf를 사용하면 echo보다 이점이 없습니다.
ddaa

2
printf 내장 된 bash이며 내 대답에서 "이 예제에서는 많은 이점을 제공하지 않지만 더 복잡한 출력으로 더 유용 할 수 있습니다.
Dave Webb
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.