큰 따옴표는 언제 필요합니까?


120

이전의 조언 $VARIABLE은 적어도 하나의 쉘이 하나의 단일 항목으로 해석되기를 원한다면을 포함하는 표현을 이중 인용하는 것이 었습니다. 그렇지 않으면 내용의 모든 공백 $VARIABLE이 쉘을 버릴 것입니다.

그러나 최신 버전의 셸에서는 더 이상 큰 따옴표가 더 이상 필요하지 않습니다 (적어도 위에서 설명한 목적으로). 예를 들어,에 bash:

% FOO='bar baz'
% [ $FOO = 'bar baz' ] && echo OK
bash: [: too many arguments
% [[ $FOO = 'bar baz' ]] && echo OK
OK
% touch 'bar baz'
% ls $FOO
ls: cannot access bar: No such file or directory
ls: cannot access baz: No such file or directory

에서 zsh, 다른 한편으로는, 같은 세 가지 명령은 성공. 따라서이 실험에 따르면 in bash에서 큰 따옴표를 생략 할 수 [[ ... ]]있지만 내부 [ ... ]또는 명령 줄 인수에서는 생략 할 수 없지만 ,에서 zsh따옴표는 생략 할 수 있습니다.

그러나 위와 같은 일화적인 예에서 일반적인 규칙을 추론하는 것은 찬성입니다. 큰 따옴표가 필요한 경우에 대한 요약을 보는 것이 좋습니다. 나는에 주로 관심이 있어요 zsh, bash하고 /bin/sh.


10
zsh에서 관찰 된 동작은 설정에 따라 다르며 SH_WORD_SPLIT옵션의 영향을받습니다 .
Ulrich Dangel 2016 년


3
제쳐두고-모든 대문자 변수 이름은 운영 체제와 쉘을 의미하는 변수에 의해 사용됩니다. POSIX 사양에서는 응용 프로그램 정의 변수에 소문자 이름을 사용하도록 명시 적으로 권장합니다. 인용 된 사양은 환경 변수에 특별히 초점을 맞추고 있지만 환경 변수 및 셸 변수는 네임 스페이스를 공유합니다. 환경 변수에서 이미 사용하는 이름으로 셸 변수를 만들려고하면 후자가 덮어 씁니다. 네 번째 단락 인 pubs.opengroup.org/onlinepubs/009695399/basedefs/…를 참조하십시오 .
찰스 더피

답변:


128

먼저 zsh를 나머지와 분리하십시오. 오래된 쉘과 현대 쉘의 문제는 아닙니다. zsh는 다르게 동작합니다. zsh 디자이너는 기존 쉘 (Bourne, ksh, bash)과 호환되지 않지만 사용하기 쉽게하기로 결정했습니다.

둘째, 큰 따옴표를 필요할 때 기억하는 것보다 항상 사용하는 것이 훨씬 쉽습니다. 대부분의 시간이 필요하므로 필요할 때가 아니라 필요하지 않은시기를 배워야합니다.

간단히 말해서 단어 목록이나 패턴이 예상되는 곳에는 큰 따옴표가 필요 합니다. 파서가 원시 문자열을 예상하는 상황에서는 선택 사항입니다.

따옴표없이 일어나는 일

큰 따옴표가 없으면 두 가지 일이 발생합니다.

  1. 먼저, 확장 결과 (와 같은 매개 변수 대체에 대한 변수 값 ${foo}또는 같은 명령 대체에 대한 명령의 출력 $(foo))는 공백이 포함 된 곳마다 단어로 분할됩니다.
    보다 정확하게는 확장 결과는 IFS변수 값 (분리 문자)에 나타나는 각 문자로 분할됩니다 . 일련의 구분 문자에 공백 (공백, 탭 또는 줄 바꿈)이 포함 된 경우 공백은 단일 문자로 계산됩니다. 공백이 아닌 구분 기호를 선행, 후행 또는 반복하면 빈 필드가 생깁니다. 예를 들어,와 IFS=" :", :one::two : three: :four 이전에 빈 필드를 생산 one사이 onetwo사이에, 그리고 (하나 하나) threefour.
  2. 분할 결과로 생성 된 각 필드는 문자 중 하나가 포함 된 경우 글로브 (와일드 카드 패턴)로 해석됩니다 \[*?. 해당 패턴이 하나 이상의 파일 이름과 일치하면 패턴이 일치하는 파일 이름 목록으로 바뀝니다.

인용 부호가없는 변수 확장 $foo은 구어체 적으로 "split + glob operator"로 알려져 있지만 "$foo", 변수의 값만 취합니다 foo. 명령 대체도 마찬가지 "$(foo)"입니다. 명령 대체 $(foo)입니다. 명령 대체는 split + glob입니다.

큰 따옴표를 생략 할 수있는 곳

Bourne 스타일 쉘에서 생각할 수있는 모든 경우는 큰 따옴표없이 변수 또는 명령 대체를 쓸 수 있으며 값은 문자 그대로 해석됩니다.

  • 과제의 오른쪽에.

    var=$stuff
    a_single_star=*

    뒤에 큰 따옴표가 필요합니다 export. 키워드가 아닌 일반적인 내장 기능이기 때문입니다. 이것은 대시, zsh (sh 에뮬레이션에서), yash 또는 posh와 같은 일부 쉘에서만 적용됩니다. bash와 ksh는 모두 export특별하게 취급 합니다.

    export VAR="$stuff"
  • A의 case문.

    case $var in 

    대소 문자 패턴에는 큰 따옴표가 필요합니다. 대소 문자 패턴에서는 단어 분리가 발생하지 않지만 인용되지 않은 변수는 패턴으로 해석되고 인용 된 변수는 리터럴 문자열로 해석됩니다.

    a_star='a*'
    case $var in
      "$a_star") echo "'$var' is the two characters a, *";;
       $a_star) echo "'$var' begins with a";;
    esac
  • 이중 괄호 안에 이중 괄호는 쉘 특수 구문입니다.

    [[ -e $filename ]]

    패턴 또는 정규식이 예상되는 경우에는 큰 따옴표가 필요하다는 점을 제외하고 : =또는 ==or !=또는 오른쪽에 =~.

    a_star='a*'
    if [[ $var == "$a_star" ]]; then echo "'$var' is the two characters a, *"
    elif [[ $var == $a_star ]]; then echo "'$var' begins with a"
    fi

    작은 괄호 안에는 큰 따옴표가 필요합니다. 대괄호 [ … ]는 일반적인 셸 구문이기 때문입니다 (라고하는 명령입니다 [). 단일 또는 이중 괄호 참조

  • 비 대화식 POSIX 셸에서 리디렉션 (not bash및 nor ksh88)

    echo "hello world" >$filename

    일부 쉘은 대화식 일 때 변수 값을 와일드 카드 패턴으로 처리합니다. POSIX는 비 대화식 쉘에서의 동작을 금지하지만 bash (POSIX 모드 제외) 및 ksh88 ( shSolaris와 같은 일부 상용 Unices의 POSIX로 발견 된 경우 포함)을 포함한 일부 쉘은 여전히 그렇게합니다 ( 분할bash 시도도 함) 그 않는 한 리디렉션이 실패 분할이 글 로빙 + 정확히 하나 개의 단어에 결과를 그것에서 리디렉션의 목표를 인용하는 것이 좋습니다 이유입니다,) 당신이로 변환 할 경우에 스크립트 위치를 시스템에 언젠가 스크립트 또는 실행 입니다 해당 지점에서 호환되지 않거나 대화식 쉘에서 제공 될 수 있습니다 .shbashsh

  • 산술 표현식 내부. 실제로 변수를 산술 표현식으로 구문 분석하려면 따옴표를 생략해야합니다.

    expr=2*2
    echo "$(($expr))"

    그러나 POSIX에서 요구하는대로 대부분의 쉘에서 단어 분할이 적용되므로 산술 확장에 따옴표가 필요합니다 (!?).

  • 연관 배열 아래 첨자.

    typeset -A a
    i='foo bar*qux'
    a[foo\ bar\*qux]=hello
    echo "${a[$i]}"

인용되지 않은 변수 및 명령 대체는 드문 경우에 유용 할 수 있습니다.

  • 변수 값 또는 명령 출력이 glob 패턴 목록으로 구성되고 이러한 패턴을 일치하는 파일 목록으로 확장하려는 경우.
  • 값에 와일드 카드 문자가 포함되어 있지 않다는 것을 알면 $IFS수정되지 않았으며 공백 문자로 분할하려고합니다.
  • 특정 문자에서 값을 분할하려면을 사용하여 globbing을 비활성화 하고 구분 문자로 set -f설정 IFS하거나 공백으로 분리하려면 그대로 두십시오.

Zsh

zsh에서는 몇 가지 예외를 제외하고 대부분 큰 따옴표를 생략 할 수 있습니다.

  • $var여러 단어로 확장되지는 않지만 값이 var빈 문자열 인 경우 빈 목록 (한 개의 빈 단어가 포함 된 목록과 반대)으로 확장됩니다 . 대조:

    var=
    print -l $var foo        # prints just foo
    print -l "$var" foo      # prints an empty line, then foo

    마찬가지로, "${array[@]}"배열의 모든 요소로 $array확장되고 비어 있지 않은 요소로만 확장됩니다.

  • @매개 변수 확장 플래그 때로는 전체 대체 주위에 따옴표가 필요합니다 "${(@)foo}".

  • : 명령 치환은 필드 분할 인용 부호로 둘러싸이지 않은 경우에 거쳐 echo $(echo 'a'; echo '*')인쇄 a *반면 (하나의 공간) echo "$(echo 'a'; echo '*')"수정되지 않은 두 줄의 문자열을 인쇄합니다. 한 "$(somecommand)"줄로 명령의 출력을 얻기 위해 사용하십시오 . "${$(somecommand; echo _)%?}"최종 개행을 포함하여 명령의 정확한 출력을 얻는 데 사용하십시오 . "${(@f)$(somecommand)}"명령 출력에서 ​​행 배열을 가져 오는 데 사용 합니다.


실제로 변수를 산술 표현식으로 구문 분석하려면 따옴표를 생략해야합니다. 따옴표로 예제 작업을 수행 할 수있는 이유는 무엇입니까?echo "$(("$expr"))"
Cyker

표현은man bash 다음과 같습니다 . 표현식은 큰 따옴표 안에있는 것처럼 처리되지만 괄호 안의 큰 따옴표는 특별히 처리되지 않습니다.
Cyker

4
또한 관심있는 사람이라면 split + glob 의 공식 이름 은 단어 분할경로 이름 확장 입니다.
Cyker

3
참고 - 이상에 StackOverflow에 , 나는 했어 누군가가 인수를 인용하지 방어하기 위해이 답변 밖으로에서 "옵션 원시 문자열이 예상된다"언어를 당겨 echo. 언어를 더욱 명확하게 만드는 것이 좋습니다 ( "파서가 원시 문자열을 예상 할 때"?)
Charles Duffy

2
@CharlesDuffy Ugh, 나는이 오판을 생각하지 않았습니다. 나는 "where"를 "when"으로 변경하고 제안한대로 문장을 강화했습니다.
Gilles
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.