Programming

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

procodes 2020. 6. 24. 08:06
반응형

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


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

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

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). 공백이 아닌 문자 사이에 단일 공백 ​​이외의 공백이 포함 된 것은 따옴표로 묶어야합니다 (그렇지 않으면 쉘은 공백을 효과적으로 단일 공백으로 축소하고 앞뒤 공백을 잘라냅니다).

When you know that a variable can only contain a value which contains no shell metacharacters, quoting is optional. Thus, an unquoted $? is basically fine, because this variable can only ever contain a single number. However, "$?" is also correct, and recommended for general consistency and correctness (though this is my personal recommendation, not a widely recognized policy).

Values which are not variables basically follow the same rules, though you could then also escape any metacharacters instead of quoting them. For a common example, a URL with a & in it will be parsed by the shell as a background command unless the metacharacter is escaped or quoted:

$ wget http://example.com/q&uack
[1] wget http://example.com/q
-bash: uack: command not found

(Of course, this also happens if the URL is in an unquoted variable.) For a static string, single quotes make the most sense, although any form of quoting or escaping works here.

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

The last example also suggests another useful concept, which I like to call "seesaw quoting". If you need to mix single and double quotes, you can use them adjacent to each other. For example, the following quoted strings

'$HOME '
"isn't"
' where `<3'
"' is."

can be pasted together back to back, forming a single long string after tokenization and quote removal.

$ echo '$HOME '"isn't"' where `<3'"' is."
$HOME isn't where `<3' is.

This isn't awfully legible, but it's a common technique and thus good to know.

As an aside, scripts should usually not use ls for anything. To expand a wildcard, just ... use it.

$ 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

(The loop is completely superfluous in the latter example; printf specifically works fine with multiple arguments. stat too. But looping over a wildcard match is a common problem, and frequently done incorrectly.)

A variable containing a list of tokens to loop over or a wildcard to expand is less frequently seen, so we sometimes abbreviate to "quote everything unless you know precisely what you are doing".


Here is a three-point formula for quotes in general:

Double quotes

In contexts where we want to suppress word splitting and globbing. Also in contexts where we want the literal to be treated as a string, not a regex.

Single quotes

In string literals where we want to suppress interpolation and special treatment of backslashes. In other words, situations where using double quotes would be inappropriate.

No quotes

In contexts where we are absolutely sure that there are no word splitting or globbing issues or we do want word splitting and globbing.


Examples

Double quotes

  • literal strings with whitespace ("StackOverflow rocks!", "Steve's Apple")
  • variable expansions ("$var", "${arr[@]}")
  • command substitutions ("$(ls)", "`ls`")
  • globs where directory path or file name part includes spaces ("/my dir/"*)
  • to protect single quotes ("single'quote'delimited'string")
  • Bash parameter expansion ("${filename##*/}")

Single quotes

  • command names and arguments that have whitespace in them
  • literal strings that need interpolation to be suppressed ( 'Really costs $$!', 'just a backslash followed by a t: \t')
  • to protect double quotes ('The "crux"')
  • regex literals that need interpolation to be suppressed
  • use shell quoting for literals involving special characters ($'\n\t')
  • use shell quoting where we need to protect several single and double quotes ($'{"table": "users", "where": "first_name"=\'Steve\'}')

No quotes

  • around standard numeric variables ($$, $?, $# etc.)
  • in arithmetic contexts like ((count++)), "${arr[idx]}", "${string:start:length}"
  • inside [[ ]] expression which is free from word splitting and globbing issues (this is a matter of style and opinions can vary widely)
  • where we want word splitting (for word in $words)
  • where we want globbing (for txtfile in *.txt; do ...)
  • where we want ~ to be interpreted as $HOME (~/"some dir" but not "~/some dir")

See also:


I generally use quoted like "$var" for safe, unless I am sure that $var does not contain space.

I do use $var as a simple way to join lines:

lines="`cat multi-lines-text-file.txt`"
echo "$lines"                             ## multiple lines
echo $lines                               ## all spaces (including newlines) are zapped

For using the variables in the shell script use " " quoted variables as the quoted one means that the variable may contain spaces or special character which won't affect the execution of your shell script. Else if you are sure of not having any spaces or special character in your variable name then you may use them without " ".

Example:

echo "$url name" -- ( Can be used at all times )

echo "$url name" -- ( Cannot be used at such situations so take precaution before using it )

참고URL : https://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable

반응형