큰 따옴표는 언제 필요합니까? 단일 항목으로 해석되기를 원한다면을 포함하는

이전의 조언 $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.



답변

먼저 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)}"명령 출력에서 ​​행 배열을 가져 오는 데 사용 합니다.


답변