방금 변수를 할당했지만 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"

이것은 주어진 모든 예에서 예상 값을 제공합니다. 항상 변수 참조를 인용하십시오!


왜?

따옴표 가없는 변수는 다음과 같습니다.

  1. 값이 공백에서 여러 단어로 분할 되는 필드 분할을 수행 합니다 (기본값).

    전에: /* Foobar is free software */

    후 : /*, Foobar, is, free, software,*/

  2. 이러한 각 단어는 패턴이 일치하는 파일로 확장되는 경로 이름 확장 을 거치게됩니다 .

    전에: /*

    후 : /bin, /boot, /dev, /etc, /home, …

  3. 마지막으로, 모든 인수는 그들을 기록하는, 에코에 전달되는 하나의 공백으로 구분 주고,

    /bin /boot /dev /etc /home Foobar is free software Desktop/ Downloads/

    변수의 값 대신.

변수를 인용 하면 다음과 같습니다.

  1. 그 가치로 대체하십시오.
  2. 2 단계는 없습니다.

그렇기 때문에 특별히 단어 분할 및 경로 이름 확장이 필요하지 않는 한 항상 모든 변수 참조를 인용 해야합니다 . shellcheck 와 같은 도구 가 도움이 될 수 있으며 위의 모든 경우에 따옴표 누락에 대해 경고합니다.


답변

왜 이런 일이 발생하는지 알고 싶을 수 있습니다. 다른 사람의 훌륭한 설명 과 함께 셸 스크립트가 공백이나 기타 특수 문자로 인해 질식하는 이유에 대한 참조를 찾으십시오 . 작성한 유닉스 및 리눅스 :

왜 글을 써야 "$foo"합니까? 따옴표 없이는 어떻게 되나요?

$foo“변수의 값을 취한다”는 의미가 아닙니다 foo. 이것은 훨씬 더 복잡한 것을 의미합니다.

  • 먼저 변수의 값을 취하십시오.
  • 필드 분할 : 해당 값을 공백으로 구분 된 필드 목록으로 처리하고 결과 목록을 작성합니다. 변수가 포함되어 있으면, 예를 들면, foo * bar ​이 단계의 결과는 3 요소 목록 foo, *,bar .
  • 파일 이름 생성 : 각 필드를 glob, 즉 와일드 카드 패턴으로 취급하고이 패턴과 일치하는 파일 이름 목록으로 대체합니다. 패턴이 어떤 파일과도 일치하지 않으면 수정되지 않은 상태로 유지됩니다. 이 예에서는 foo현재 디렉토리의 파일 목록, 마지막으로
    bar. 현재 디렉토리가 비어있는 경우, 결과는 foo, *,
    bar.

결과는 문자열 목록입니다. 쉘 구문에는 목록 컨텍스트와 문자열 컨텍스트의 두 가지 컨텍스트가 있습니다. 필드 분할 및 파일 이름 생성은 목록 컨텍스트에서만 발생하지만 대부분의 경우입니다. 큰 따옴표는 문자열 컨텍스트를 구분합니다. 큰 따옴표로 묶인 전체 문자열은 분할되지 않는 단일 문자열입니다. (예외 :
"$@"위치 매개 변수 목록으로 확장하는 경우, 예를 들어 위치 매개 변수가 세 개인 경우 "$@"와 동일합니다 "$1" "$2" "$3". $ *와 $ @의 차이점무엇입니까? 참조 )

$(foo)또는 로 명령 대체도 마찬가지 입니다
`foo`
. 참고로, 사용하지 마십시오 `foo`. 인용 규칙은 이상하고 휴대가 불가능하며 모든 최신 쉘은$(foo) 은 직관적 인 인용 규칙을 제외하고는 절대적으로 동등한 을 제공합니다.

산술 대체의 출력도 동일한 확장을 거치지 만 확장 할 IFS수없는 문자 만 포함하므로 일반적으로 문제 가되지 않습니다 ( 숫자 또는를 포함하지
않는다고 가정 -).

큰 따옴표는 언제 필요합니까?를 참조하십시오 . 따옴표를 생략 할 수있는 경우에 대한 자세한 내용은

이 모든 rigmarole이 발생하는 것을 의미하지 않는 한, 변수 및 명령 대체 주위에 항상 큰 따옴표를 사용하는 것을 기억하십시오. 주의하세요. 따옴표를 생략하면 오류뿐만 아니라 보안 허점이 발생할 수 있습니다
.


답변

다른 문제뿐만 아니라 인용 실패에 의해 발생 -n하고 -e소비 할 수있는 echo인수로. (전자 만 POSIX 사양에 따라 합법적 echo이지만 몇 가지 일반적인 구현은 사양을 위반하고 소비 -e합니다).

이를 방지하려면 세부 사항이 중요한 경우 대신 사용 하십시오.printfecho

그러므로:

$ vars="-e -n -a"
$ echo $vars      # breaks because -e and -n can be treated as arguments to echo
-a
$ echo "$vars"
-e -n -a

그러나 다음을 사용할 때 올바른 인용이 항상 당신을 구하는 것은 아닙니다 echo.

$ vars="-n"
$ echo $vars
$ ## not even an empty line was printed

… 그것은 반면 것이다 당신을 저장합니다 printf:

$ vars="-n"
$ printf '%s\n' "$vars"
-n


답변

정확한 값을 얻으려면 사용자 큰 따옴표. 이렇게 :

echo "${var}"

그리고 그것은 당신의 가치를 정확하게 읽을 것입니다.


답변

echo $var출력은 IFS변수 의 값에 크게 의존 합니다. 기본적으로 공백, 탭 및 개행 문자가 포함됩니다.

[ks@localhost ~]$ echo -n "$IFS" | cat -vte
 ^I$

즉, 쉘이 필드 분할 (또는 단어 분할)을 수행 할 때 이러한 모든 문자를 단어 구분 기호로 사용합니다. 이것은 큰 따옴표없이 변수를 참조하여 에코 ($var )하여 예상 출력이 변경 될 .

단어 분할을 방지하는 한 가지 방법 (큰 따옴표 사용 외에)은 IFSnull 로 설정 하는 것입니다. http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_05 참조 :

IFS 값이 null이면 필드 분할이 수행되지 않습니다.

null로 설정하면 빈 값으로 설정됩니다.

IFS=

테스트:

[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 ~]$ 


답변

ks1322답변은 사용하는 동안 문제를 식별하는 데 도움이되었습니다.docker-compose exec .

-T플래그 를 생략하고 docker-compose exec출력을 중단하는 특수 문자를 추가하면 b대신 다음이 표시 됩니다 1b.

$ test=$(/usr/local/bin/docker-compose exec db bash -c "echo 1")
$ echo "${test}b"
b
echo "${test}" | cat -vte
1^M$

-T플래그를 사용하면 docker-compose exec예상대로 작동합니다.

$ test=$(/usr/local/bin/docker-compose exec -T db bash -c "echo 1")
$ echo "${test}b"
1b


답변

변수를 따옴표로 묶는 것 외에도 tr공백을 사용 하고 개행 문자로 변환 하여 변수의 출력을 변환 할 수도 있습니다 .

$ echo $var | tr " " "\n"
foo
bar
baz

이것은 조금 더 복잡하지만 배열 변수 사이의 구분 기호로 모든 문자를 대체 할 수 있으므로 출력에 더 많은 다양성을 추가합니다.