쉘 변수를 따옴표로 묶을 때?


184

누군가 쉘 스크립트에서 변수 주위에 따옴표를 묶어야하는지 여부를 말해 줄 수 있습니까?

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

xdg-open $URL 
[ $? -eq 2 ]

또는

xdg-open "$URL"
[ "$?" -eq "2" ]

그렇다면 왜 그렇습니까?



이 질문은 많은 중복을 얻습니다. 그중 많은 것이 변수에 관한 것이 아니므로 "variable"대신 "value"로 제목을 변경했습니다. 이것이 더 많은 사람들이이 주제를 찾는 데 도움이되기를 바랍니다.
tripleee 2012 년

1
@codeforester 되 돌린 편집은 어떻게 되나요?
tripleee


답변:


131

일반 규칙 : 비어 있거나 공백 (또는 실제로 공백) 또는 특수 문자 (와일드 카드)를 포함 할 수 있으면 인용하십시오. 공백으로 문자열을 인용하지 않으면 쉘이 단일 인수를 여러 개로 나누는 경우가 많습니다.

$?숫자 값이므로 따옴표가 필요하지 않습니다. $URL그것이 필요한지 여부 는 거기에서 허용하는 것과 비어있는 경우 여전히 인수를 원하는지에 달려 있습니다.

나는 문자열이 그렇게 안전하기 때문에 항상 습관에서 벗어나 인용하는 경향이 있습니다.


2
"공백"은 실제로 "임의의 공백"을 의미합니다.
윌리엄 퍼셀

4
@Cristian : 변수에 무엇이 있는지 잘 모르면 인용하는 것이 더 안전합니다. 나는 paxdiablo와 같은 원칙을 따르는 경향이 있으며 모든 것을 인용하는 습관을들이십시오 (특별한 이유가 없다면).
Gordon Davisson

11
IFS의 가치를 모른다면 무엇이든 인용하십시오. 경우 IFS=0, 다음 echo $?매우 놀라운 일이 될 수 있습니다.
Charles Duffy 2012

3
예상 값이 아닌 상황에 따라 인용하십시오. 그렇지 않으면 버그가 악화됩니다. 예를 들어, 경로에 공백이 없다는 것을 확신하므로 쓸 수 있다고 생각 cp $source1 $source2 $dest하지만 예기치 않은 이유로 dest설정되지 않으면 세 번째 인수가 사라지고 자동 source1으로 source2대신 복사 됩니다. 빈 대상에 대한 적절한 오류 (각 인수를 인용 한 경우와 마찬가지로).
Derek Veit

3
quote it if...따옴표는 필요할 때 추가하는 것이 아니라 필요할 때 제거하는 것입니다. 당신이하지 않는 한 항상 작은 따옴표로 문자열과 스크립트를 포장 해야합니다 따옴표를 사용 (예 : 변수가 확장하도록하기 위해) 또는 필요 따옴표를 사용하지 않으려면 (예를 들어, 글 로빙 및 파일 이름 확장을 위해).
Ed Morton

92

간단히 말해, 쉘이 토큰 분할 및 와일드 카드 확장을 수행하지 않아도되는 모든 것을 인용하십시오.

작은 따옴표는 그 사이의 텍스트를 그대로 보호합니다. 쉘이 끈에 전혀 닿지 않도록해야 할 때 적절한 도구입니다. 일반적으로 변수 보간이 필요하지 않은 경우 인용 메커니즘이 선택됩니다.

$ 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이 포함 된 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도 잘 작동합니다 . 그러나 와일드 카드 일치를 반복하는 것이 일반적인 문제이며 자주 잘못 수행됩니다.)

반복 할 토큰 목록 또는 확장 할 와일드 카드가 포함 된 변수는 자주 표시되지 않으므로 "정확하게 수행중인 작업을 알지 못하는 경우 모든 것을 인용"하는 약어를 사용하기도합니다.


1
이것은 관련 질문에 게시 한 답변의 (일부) 변형입니다 . 나는 이것이 간결하고 잘 정의되어 있기 때문에 여기에 붙여 넣습니다.이 특정 문제에 대한 정식 질문이 될 수 있습니다.
tripleee

4
이 항목은 # 0 항목이며 mywiki.wooledge.org/BashPitfalls 일반적인 Bash 실수 모음 에서 반복되는 테마입니다 . 해당 목록의 많은 개별 항목이 기본적으로이 문제에 관한 것입니다.
tripleee

27

일반적으로 따옴표에 대한 3 점 공식은 다음과 같습니다.

큰 따옴표

단어 분리 및 글 로빙을 억제하려는 상황에서. 또한 리터럴을 정규식이 아닌 문자열로 처리하려는 컨텍스트에서.

작은 따옴표

백 슬래시의 보간 및 특수 처리를 억제하려는 문자열 리터럴에서. 즉, 큰 따옴표를 사용하는 것이 부적절한 상황입니다.

따옴표 없음

우리가 절대적으로 확신이없는 단어 분할 또는 로빙 문제 없습니다 또는 우리가있는 상황에서 찾는 단어 분할 및 글 로빙을 .


큰 따옴표

  • 공백이있는 리터럴 문자열 ( "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")

또한보십시오:


3
이 지침에 따르면 "ls" "/" "모든 문자열 컨텍스트"라는 문구를보다 신중하게 작성해야합니다.
William Pursell

5
에서 [[ ]], 인용하는 것은 오른쪽에 중요합니까 =/ ===~: 그것은 패턴 / 정규식 또는 문자로 문자열을 해석의 차이를 만든다.
Benjamin W.

6
좋은 개요이지만 @BenjaminW.의 의견은 통합 가치가 있으며 ANSI C 인용 문자열 ( $'...')에는 자체 섹션이 있어야합니다.
mklement0

3
@ mklement0은 실제로 동일합니다. 이 지침은 항상 "ls" "/"보다 일반적으로 입력하는 것이 아니라 ls /지침의 주요 결함으로 생각합니다.
William Pursell

4
들어 따옴표 는 변수 할당 또는 추가 할 수 있습니다 case:
PesaThe

4

나는 공간이 포함되어 있지 않다고 "$var"확신하지 않는 한 일반적으로 안전을 위해 인용 된 것과 같이 사용 $var합니다.

나는 $var라인을 결합하는 간단한 방법으로 사용 합니다.

lines="`cat multi-lines-text-file.txt`"
echo "$lines"                             ## multiple lines
echo $lines                               ## all spaces (including newlines) are zapped

마지막 의견은 다소 오해의 소지가 있습니다. 줄 바꿈은 단순히 제거되지 않고 공백으로 효과적으로 대체됩니다.
tripleee

-1

쉘 스크립트에서 변수를 사용하려면 인용 된 변수로 ""인용 된 변수를 사용하십시오. 변수에 공백 또는 특수 문자가 포함되어 쉘 스크립트의 실행에 영향을 미치지 않을 수 있음을 의미합니다. 변수 이름에 공백이나 특수 문자가없는 경우 ""없이 사용할 수 있습니다.

예:

echo "$ url name"-(항상 사용 가능)

echo "$ url name"-(이런 상황에서는 사용할 수 없으므로 사용하기 전에 예방 조치를 취하십시오)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.