방금 변수를 할당했지만 echo $ variable은 다른 것을 보여줍니다.
다음은 echo $var
방금 할당 된 값과 다른 값을 표시 할 수 있는 일련의 사례 입니다. 이는 할당 된 값이 "큰 따옴표", "작은 따옴표"또는 따옴표없는 여부에 관계없이 발생합니다.
셸에서 내 변수를 올바르게 설정하려면 어떻게해야합니까?
별표
예상 출력은 /* Foobar is free software */
이지만 대신 파일 이름 목록이 표시됩니다.
$ var="/* Foobar is free software */"
$ echo $var
/bin /boot /dev /etc /home /initrd.img /lib /lib64 /media /mnt /opt /proc ...
대괄호
예상 값은 [a-z]
이지만 때로는 대신 단일 문자가 표시됩니다!
$ var=[a-z]
$ echo $var
c
줄 바꿈 (줄 바꿈)
예상 값은 별도의 줄 목록이지만 대신 모든 값이 한 줄에 있습니다!
$ cat file
foo
bar
baz
$ var=$(cat file)
$ echo $var
foo bar baz
여러 공간
신중하게 정렬 된 표 머리글을 예상했지만 대신 여러 공백이 사라지거나 하나로 축소됩니다!
$ var=" title | count"
$ echo $var
title | count
탭
두 개의 탭으로 구분 된 값을 예상했지만 대신 두 개의 공백으로 구분 된 값을 얻습니다!
$ var=$'key\tvalue'
$ echo $var
key value
위의 모든 경우에서 변수가 올바르게 설정되었지만 올바르게 읽히지 않았습니다! 올바른 방법은 다음을 참조 할 때 큰 따옴표 를 사용하는 것입니다 .
echo "$var"
이것은 주어진 모든 예에서 예상 값을 제공합니다. 항상 변수 참조를 인용하십시오!
왜?
따옴표 가없는 변수는 다음과 같습니다.
값이 공백에서 여러 단어로 분할 되는 필드 분할을 수행 합니다 (기본값).
전에:
/* Foobar is free software */
후 :
/*
,Foobar
,is
,free
,software
,*/
이러한 각 단어는 패턴이 일치하는 파일로 확장되는 경로 이름 확장 을 거치게됩니다 .
전에:
/*
후 :
/bin
,/boot
,/dev
,/etc
,/home
, ...마지막으로, 모든 인수는 그들을 기록하는, 에코에 전달되는 하나의 공백으로 구분 주고,
/bin /boot /dev /etc /home Foobar is free software Desktop/ Downloads/
변수의 값 대신.
변수를 인용 하면 다음과 같습니다.
- 그 가치로 대체하십시오.
- 2 단계는 없습니다.
그렇기 때문에 특별히 단어 분할 및 경로 이름 확장이 필요하지 않는 한 항상 모든 변수 참조를 인용 해야합니다 . shellcheck 와 같은 도구 가 도움이 될 수 있으며 위의 모든 경우에 따옴표 누락에 대해 경고합니다.
You may want to know why this is happening. Together with the great explanation by that other guy, find a reference of Why does my shell script choke on whitespace or other special characters? written by Gilles in Unix & Linux:
Why do I need to write
"$foo"
? What happens without the quotes?
$foo
does not mean “take the value of the variablefoo
”. It means something much more complex:
- First, take the value of the variable.
- Field splitting: treat that value as a whitespace-separated list of fields, and build the resulting list. For example, if the variable contains
foo * bar
then the result of this step is the 3-element listfoo
,*
,bar
.- Filename generation: treat each field as a glob, i.e. as a wildcard pattern, and replace it by the list of file names that match this pattern. If the pattern doesn't match any files, it is left unmodified. In our example, this results in the list containing
foo
, following by the list of files in the current directory, and finallybar
. If the current directory is empty, the result isfoo
,*
,bar
.Note that the result is a list of strings. There are two contexts in shell syntax: list context and string context. Field splitting and filename generation only happen in list context, but that's most of the time. Double quotes delimit a string context: the whole double-quoted string is a single string, not to be split. (Exception:
"$@"
to expand to the list of positional parameters, e.g."$@"
is equivalent to"$1" "$2" "$3"
if there are three positional parameters. See What is the difference between $* and $@?)The same happens to command substitution with
$(foo)
or with`foo`
. On a side note, don't use`foo`
: its quoting rules are weird and non-portable, and all modern shells support$(foo)
which is absolutely equivalent except for having intuitive quoting rules.The output of arithmetic substitution also undergoes the same expansions, but that isn't normally a concern as it only contains non-expandable characters (assuming
IFS
doesn't contain digits or-
).See When is double-quoting necessary? for more details about the cases when you can leave out the quotes.
Unless you mean for all this rigmarole to happen, just remember to always use double quotes around variable and command substitutions. Do take care: leaving out the quotes can lead not just to errors but to security holes.
In addition to other issues caused by failing to quote, -n
and -e
can be consumed by echo
as arguments. (Only the former is legal per the POSIX spec for echo
, but several common implementations violate the spec and consume -e
as well).
To avoid this, use printf
instead of echo
when details matter.
Thus:
$ vars="-e -n -a"
$ echo $vars # breaks because -e and -n can be treated as arguments to echo
-a
$ echo "$vars"
-e -n -a
However, correct quoting won't always save you when using echo
:
$ vars="-n"
$ echo $vars
$ ## not even an empty line was printed
...whereas it will save you with printf
:
$ vars="-n"
$ printf '%s\n' "$vars"
-n
user double quote to get the exact value. like this:
echo "${var}"
and it will read your value correctly.
echo $var
output highly depends on the value of IFS
variable. By default it contains space, tab, and newline characters:
[ks@localhost ~]$ echo -n "$IFS" | cat -vte
^I$
This means that when shell is doing field splitting (or word splitting) it uses all these characters as word separators. This is what happens when referencing a variable without double quotes to echo it ($var
) and thus expected output is altered.
One way to prevent word splitting (besides using double quotes) is to set IFS
to null. See http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_05 :
If the value of IFS is null, no field splitting shall be performed.
Setting to null means setting to empty value:
IFS=
Test:
[ks@localhost ~]$ echo -n "$IFS" | cat -vte
^I$
[ks@localhost ~]$ var=$'key\nvalue'
[ks@localhost ~]$ echo $var
key value
[ks@localhost ~]$ IFS=
[ks@localhost ~]$ echo $var
key
value
[ks@localhost ~]$
Additional to putting the variable in quotation, one could also translate the output of the variable using tr
and converting spaces to newlines.
$ echo $var | tr " " "\n"
foo
bar
baz
Although this is a little more convoluted, it does add more diversity with the output as you can substitute any character as the separator between array variables.
'developer tip' 카테고리의 다른 글
수정 사항은 어떻게 사용하고 어떻게 작동합니까? (0) | 2020.09.25 |
---|---|
분산 작업 대기열 (예 : Celery)과 crontab 스크립트 (0) | 2020.09.25 |
"module"package.json 필드는 무엇입니까? (0) | 2020.09.25 |
나무가없는 이유 (0) | 2020.09.24 |
유니 코드 한자의 전체 범위는 무엇입니까? (0) | 2020.09.24 |