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

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

과거의 오래된 조언은 $VARIABLE최소한 셸이 이를 단일 항목으로 해석하기를 원하는 경우 a와 관련된 모든 표현식을 큰따옴표로 묶는 것이었습니다. 그렇지 않으면 내용의 공백으로 인해 $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반면에 에서는 동일한 세 가지 명령이 성공합니다. 따라서 이 실험에 따르면 에서는 bash큰따옴표를 내부적으로 생략할 수 있지만 [[ ... ]]내부적 [ ... ]으로나 명령줄 인수에서는 생략할 수 없는 반면, 에서는 zsh이러한 모든 경우에 큰따옴표를 생략할 수 있는 것으로 보입니다 .

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

답변1

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

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

요컨대,단어 목록이나 패턴이 필요할 때마다 큰따옴표가 필요합니다.. 파서가 단일 원시 문자열을 기대하는 상황에서는 선택 사항입니다.

따옴표 없이 무슨 일이 일어나는지

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

  1. $foo먼저 확장 결과( or 와 같은 인수 대체에 대한 변수 값 ${foo}또는 와 같은 명령 대체에 대한 명령 출력 $(foo))가 공백이 포함된 단어로 분할됩니다. 보다 정확하게는 확장 결과가
    변수 value 에 나타나는 IFS모든 문자(구분 기호)에서 분할됩니다 . 구분 기호 문자 시퀀스에 공백(공백, 탭 또는 줄 바꿈)이 포함된 경우 공백은 단일 문자로 계산됩니다. 공백이 아닌 구분 기호는 선행, 후행 또는 반복되어 빈 필드가 됩니다. 예를 들어, (공백 및 콜론)을 사용하면 IFS=" :"이전, 및 사이, 및 사이에 :one::two : three: :four 빈 필드가 생성됩니다 .oneonetwothreefour
  2. 분할로 인해 생성된 각 필드에 문자 중 하나가 포함된 경우 해당 필드는 glob(와일드카드 패턴)으로 해석됩니다 [*?. 패턴이 하나 이상의 파일 이름과 일치하는 경우 패턴은 일치하는 파일 이름 목록으로 대체됩니다.

따옴표가 없는 변수 확장은 단지 변수의 값을 취하는 $foo것과는 반대로 "split+glob 연산자"로 구어적으로 알려져 있습니다 . 명령 대체도 마찬가지입니다. 예 명령 대체, 예 명령 대체 다음에 분할+glob이 따릅니다."$foo"foo"$(foo)"$(foo)

큰따옴표를 어디에서 생략할 수 있나요?

다음은 큰따옴표 없이 변수나 명령 대체를 작성하고 값을 문자 그대로 해석할 수 있는 Bourne 스타일 쉘에서 생각할 수 있는 모든 사례입니다.

  • 스칼라(비배열) 변수 할당의 오른쪽.

     var=$stuff
     a_single_star=*
    

    exportor 뒤에 큰따옴표가 필요하다는 점에 유의하세요. readonly일부 쉘에서는 여전히 키워드가 아닌 일반적인 내장 함수이기 때문입니다. 이는 일부 이전 버전의 dash, 이전 버전의 zsh(sh 에뮬레이션), yash 또는 posh와 같은 일부 쉘에서만 발생합니다. 최신 버전의 bash, ksh, dash 및 zsh에서는 export/ readonly및 co가 다음과 같이 특별히 처리됩니다. POSIX에서는 이제 더 명시적으로 요구하기 때문에 이중 내장 / 키워드(특정 조건에서)입니다.

     export VAR="$stuff"
    
  • 성명서 에서 case.

     case $var in …
    

    실제로 대소문자 패턴에는 큰따옴표가 필요합니다. 대소문자 패턴에서는 단어 분리가 발생하지 않지만, 따옴표가 없는 변수는 glob 스타일 패턴으로 해석되고, 따옴표 붙은 변수는 리터럴 문자열로 해석됩니다.

     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 또는 !=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 셸의 리디렉션( bash또는 아님)에서 ksh88.

     echo "hello world" >$filename
    

    일부 쉘에서는 대화식으로 변수 값을 와일드카드 패턴으로 처리합니다. POSIX는 비대화형 쉘에서 이 동작을 금지하지만 bash(POSIX 모드 제외) 및 ksh88( sh일부 상용 Unices(Solaris 등)에 대해 POSIX인 것으로 알려진 경우 포함)를 포함한 일부 쉘에서는 여전히 이를 수행합니다( bash또한 다음을 시도하십시오. )나뉘다그렇지 않으면 리디렉션이 실패합니다.분할 + 와일드카드sh결과는 정확히 한 단어임) 그렇기 때문에 언젠가 스크립트로 변환하고 싶거나 bash이를 준수하지 않는 시스템에서 실행하려는 경우를 대비하여 스크립트에서 리디렉션 대상을 참조하는 것이 좋습니다 . 아니면 sh어쩌면원천대화형 쉘에서.

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

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

    그러나 산술 확장에는 따옴표가 필요합니다. 왜냐하면 대부분의 셸(!?)에서 POSIX가 요구하는 대로 토큰화를 수행하기 때문입니다.

  • 연관 배열 첨자에서.

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

따옴표가 없는 변수 및 명령 대체는 다음과 같은 드문 상황에서 유용할 수 있습니다.

  • 변수 값 또는 명령 출력에 glob 패턴 목록이 포함되어 있고 해당 패턴을 일치하는 파일 목록으로 확장하려는 경우.
  • 값에 와일드카드 문자가 포함되어 있지 않다는 것을 알면 값이 $IFS수정되지 않으며 공백(공백, 탭 및 줄 바꿈) 문자로 분할하려고 합니다.
  • 특정 문자에서 값을 분할하려는 경우: set -o noglob/disable 와일드카드를 사용 set -f하고 구분 기호로 설정한 다음 IFS(또는 공백에서 분할되도록 남겨둔 후) 확장합니다.

지쉬

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

  • $var결코 여러 단어로 확장되지 않습니다(배열이 아니라고 가정). 그러나 값이 빈 문자열인 경우 var빈 목록(단일 빈 단어를 포함하는 목록이 아님)으로 확장됩니다. var비교:

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

    마찬가지로 "${array[@]}"배열의 모든 요소로 확장되지만 $arraynull이 아닌 요소로만 확장됩니다.

  • ksh 및 bash에서와 같이 [[ … ]]or 연산자 오른쪽에 있는 변수는 문자열이 포함된 경우 큰따옴표로 묶어야 합니다. 패턴/정규 표현식이 포함된 경우 큰따옴표로 묶을 필요가 없습니다. : 사실이지만 거짓입니다.==!==~p='a*'; [[ abc == $p ]]p='a*'; [[ abc == "$p" ]]

  • 매개변수 @확장 플래그에서는 전체 대체 항목을 큰따옴표로 묶어야 하는 경우가 있습니다 "${(@)foo}".

  • 따옴표가 없으면 명령 대체는 필드 분할을 거칩니다. 대신 echo $(echo 'a'; echo '*')인쇄 a *(단일 공백 ​​사용)는 echo "$(echo 'a'; echo '*')"수정되지 않은 두 줄 문자열을 인쇄합니다. "$(somecommand)"끝에 개행 문자 없이 명령의 한 단어를 출력하는 데 사용됩니다 . "${$(somecommand; echo .)%?}"최종 줄 바꿈을 포함하여 명령의 정확한 출력을 얻는 데 사용됩니다 . "${(@f)$(somecommand)}"명령 출력에서 ​​줄 배열을 가져오는 데 사용됩니다 (뒤에 공백 줄이 있는 경우 제거).

관련 정보