누군가 쉘 스크립트에서 변수 주위에 따옴표를 묶어야하는지 여부를 말해 줄 수 있습니까?
예를 들어 다음과 같습니다.
xdg-open $URL
[ $? -eq 2 ]
또는
xdg-open "$URL"
[ "$?" -eq "2" ]
그렇다면 왜 그렇습니까?
누군가 쉘 스크립트에서 변수 주위에 따옴표를 묶어야하는지 여부를 말해 줄 수 있습니까?
예를 들어 다음과 같습니다.
xdg-open $URL
[ $? -eq 2 ]
또는
xdg-open "$URL"
[ "$?" -eq "2" ]
그렇다면 왜 그렇습니까?
답변:
일반 규칙 : 비어 있거나 공백 (또는 실제로 공백) 또는 특수 문자 (와일드 카드)를 포함 할 수 있으면 인용하십시오. 공백으로 문자열을 인용하지 않으면 쉘이 단일 인수를 여러 개로 나누는 경우가 많습니다.
$?
숫자 값이므로 따옴표가 필요하지 않습니다. $URL
그것이 필요한지 여부 는 거기에서 허용하는 것과 비어있는 경우 여전히 인수를 원하는지에 달려 있습니다.
나는 문자열이 그렇게 안전하기 때문에 항상 습관에서 벗어나 인용하는 경향이 있습니다.
IFS=0
, 다음 echo $?
매우 놀라운 일이 될 수 있습니다.
cp $source1 $source2 $dest
하지만 예기치 않은 이유로 dest
설정되지 않으면 세 번째 인수가 사라지고 자동 source1
으로 source2
대신 복사 됩니다. 빈 대상에 대한 적절한 오류 (각 인수를 인용 한 경우와 마찬가지로).
quote it if...
따옴표는 필요할 때 추가하는 것이 아니라 필요할 때 제거하는 것입니다. 당신이하지 않는 한 항상 작은 따옴표로 문자열과 스크립트를 포장 해야합니다 따옴표를 사용 (예 : 변수가 확장하도록하기 위해) 또는 필요 따옴표를 사용하지 않으려면 (예를 들어, 글 로빙 및 파일 이름 확장을 위해).
간단히 말해, 쉘이 토큰 분할 및 와일드 카드 확장을 수행하지 않아도되는 모든 것을 인용하십시오.
작은 따옴표는 그 사이의 텍스트를 그대로 보호합니다. 쉘이 끈에 전혀 닿지 않도록해야 할 때 적절한 도구입니다. 일반적으로 변수 보간이 필요하지 않은 경우 인용 메커니즘이 선택됩니다.
$ 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
도 잘 작동합니다 . 그러나 와일드 카드 일치를 반복하는 것이 일반적인 문제이며 자주 잘못 수행됩니다.)
반복 할 토큰 목록 또는 확장 할 와일드 카드가 포함 된 변수는 자주 표시되지 않으므로 "정확하게 수행중인 작업을 알지 못하는 경우 모든 것을 인용"하는 약어를 사용하기도합니다.
일반적으로 따옴표에 대한 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"
)또한보십시오:
"ls" "/"
"모든 문자열 컨텍스트"라는 문구를보다 신중하게 작성해야합니다.
[[ ]]
, 인용하는 것은 오른쪽에 중요합니까 =
/ ==
과 =~
: 그것은 패턴 / 정규식 또는 문자로 문자열을 해석의 차이를 만든다.
$'...'
)에는 자체 섹션이 있어야합니다.
"ls" "/"
보다 일반적으로 입력하는 것이 아니라 ls /
지침의 주요 결함으로 생각합니다.
case
:
나는 공간이 포함되어 있지 않다고 "$var"
확신하지 않는 한 일반적으로 안전을 위해 인용 된 것과 같이 사용 $var
합니다.
나는 $var
라인을 결합하는 간단한 방법으로 사용 합니다.
lines="`cat multi-lines-text-file.txt`"
echo "$lines" ## multiple lines
echo $lines ## all spaces (including newlines) are zapped