[linux] 쉘 변수를 따옴표로 묶을 때?

누군가 쉘 스크립트에서 변수 주위에 따옴표를 묶어야하는지 여부를 말해 줄 수 있습니까?

예를 들어 다음과 같습니다.

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”-(이런 상황에서는 사용할 수 없으므로 사용하기 전에 예방 조치를 취하십시오)


답변