누군가 쉘 스크립트에서 변수 주위에 따옴표를 묶어야하는지 여부를 말해 줄 수 있습니까?
예를 들어 다음과 같습니다.
xdg-open $URL
[ $? -eq 2 ]
또는
xdg-open "$URL"
[ "$?" -eq "2" ]
그렇다면 왜 그렇습니까?
답변
일반 규칙 : 비어 있거나 공백 (또는 실제로 공백) 또는 특수 문자 (와일드 카드)를 포함 할 수 있으면 인용하십시오. 공백으로 문자열을 인용하지 않으면 쉘이 단일 인수를 여러 개로 나누는 경우가 많습니다.
$?
숫자 값이므로 따옴표가 필요하지 않습니다. $URL
그것이 필요한지 여부 는 거기에서 허용하는 것과 비어있는 경우 여전히 인수를 원하는지에 달려 있습니다.
나는 문자열이 그렇게 안전하기 때문에 항상 습관에서 벗어나 인용하는 경향이 있습니다.
답변
간단히 말해, 쉘이 토큰 분할 및 와일드 카드 확장을 수행하지 않아도되는 모든 것을 인용하십시오.
작은 따옴표는 그 사이의 텍스트를 그대로 보호합니다. 쉘이 끈에 전혀 닿지 않도록해야 할 때 적절한 도구입니다. 일반적으로 변수 보간이 필요하지 않은 경우 인용 메커니즘이 선택됩니다.
$ 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"
)
또한보십시오:
답변
나는 공간이 포함되어 있지 않다고 "$var"
확신하지 않는 한 일반적으로 안전을 위해 인용 된 것과 같이 사용 $var
합니다.
나는 $var
라인을 결합하는 간단한 방법으로 사용 합니다.
lines="`cat multi-lines-text-file.txt`"
echo "$lines" ## multiple lines
echo $lines ## all spaces (including newlines) are zapped
답변
쉘 스크립트에서 변수를 사용하려면 인용 된 변수로 “”인용 된 변수를 사용하십시오. 변수에 공백 또는 특수 문자가 포함되어 쉘 스크립트의 실행에 영향을 미치지 않을 수 있음을 의미합니다. 변수 이름에 공백이나 특수 문자가없는 경우 “”없이 사용할 수 있습니다.
예:
echo “$ url name”-(항상 사용 가능)
echo “$ url name”-(이런 상황에서는 사용할 수 없으므로 사용하기 전에 예방 조치를 취하십시오)