Bash에서 heredoc 값을 변수에 할당하는 방법은 무엇입니까?


374

이 여러 줄 문자열 (따옴표 포함)이 있습니다.

abc'asdf"
$(dont-execute-this)
foo"bar"''

Bash의 heredoc을 사용하여 변수에 어떻게 할당합니까?

줄 바꿈을 유지해야합니다.

문자열의 문자를 피하고 싶지 않아서 성 가실 것입니다 ...


@JohnM- cd` 명령 'EOF'으로 이스케이프 된 줄 ` in the content: if the second line has 바꿈을 사용 하여 작은 따옴표 로 heredoc 할당을 시도했습니다 . " .sh : line X : cd : command not found "; 그러나 내가 큰 따옴표로 묶으면 "EOF"; bash 변수 ${A}는 문자열로 유지되지 않습니다 (확장됩니다). 그러나 줄 바꿈 유지됩니다. 그리고 두cd 번째 줄에서 명령을 실행하는 데 문제가 없습니다 ( 그리고 'EOF'와 "EOF"는에 eval저장된 일련의 명령을 실행 하기 위해 잘 작동하는 것 같습니다 . 문자열 변수 ). 건배!
sdaau 8:24에

1
... 그리고 내 이전 주석에 추가하려면 : "EOF"via으로 호출하면 double-qouted 변수의 bash 주석 "#"은 eval $VAR$ 스크립트가 한 줄로 표시되므로 나머지 스크립트는 모두 주석 처리됩니다 ; 여러 줄 #스크립트에서 bash 주석 을 사용할 수 있도록 eval call: eval "$ VAR"의 큰 따옴표도 사용합니다 .
sdaau

@ sdaau : eval이 방법에 문제가 있었지만 eval구성 파일에 정의 된 변수 가있는 패키지의 일부이기 때문에 추적하지 않았습니다 . 오류 메시지였다 /usr/lib/network/network: eval: line 153: syntax error: unexpected end of file. 방금 다른 솔루션으로 전환했습니다.
Golar Ramblar

답변:


515

쓸모없는 cat따옴표 사용을 피하고 일치하지 않는 따옴표를 더 잘 처리 할 수 있습니다 .

$ read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

반향 할 때 변수를 인용하지 않으면 줄 바꿈이 사라집니다. 인용하면 보존됩니다.

$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

소스 코드에서 가독성을 위해 들여 쓰기를 사용하려면보다 작음 뒤에 대시를 사용하십시오. 들여 쓰기는 공백없이 탭만 사용하여 수행해야합니다.

$ read -r -d '' VAR <<-'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
    EOF
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

대신 결과 변수의 내용에서 탭을 유지하려면에서에서 탭을 제거해야합니다 IFS. here doc ( EOF) 의 터미널 마커는 들여 쓰기해서는 안됩니다.

$ IFS='' read -r -d '' VAR <<'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
EOF
$ echo "$VAR"
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''

Ctrl- 를 눌러 명령 줄에 탭을 삽입 할 수 있습니다 V Tab. 편집기를 사용하는 경우 편집기에 따라 작동하거나 탭을 공백으로 자동 변환하는 기능을 해제해야 할 수도 있습니다.


117
스크립트에 set -o errexit(aka set -e)가 있고 이것을 사용 read하면 EOF에 도달 할 때 0이 아닌 반환 코드를 반환 하기 때문에 스크립트가 종료 된다는 것을 언급 할 가치가 있다고 생각합니다 .
Mark Byers

14
@MarkByers : 이것이 내가 결코 사용하지 않는 이유 중 하나 set -e이며 항상 사용하지 않는 것이 좋습니다. 대신 적절한 오류 처리를 사용하는 것이 좋습니다. trap당신의 친구입니다. 다른 친구들 : else그리고 ||다른 사람들.
추후 공지가있을 때까지 일시 중지되었습니다.

7
cat그러한 경우 에 정말로 가치 가없는 것을 피하는가 ? heredoc을 변수에 할당하는 cat것은 잘 알려진 관용구입니다. 어떻게 든 read난독 화를 사용하면 imho에 거의 이점이 없습니다.
Gregory Pakosz

6
@ulidtko d빈 문자열과 빈 문자열 사이에 공백이 없기 때문입니다 . 의 인수를보기 전에 단순히 bash축소 -rd''하기 때문에 의 인수로 취급됩니다 . -rdreadVAR-d
chepner

6
이 형식에서는 read0이 아닌 종료 코드와 함께 반환됩니다. 따라서 오류 검사가 활성화 된 스크립트 (예 :)에서이 방법이 적합하지 않습니다 set -e.
Swiss

245

$ ()를 사용 cat하여 다음과 같이 변수 의 출력을 지정하십시오 .

VAR=$(cat <<'END_HEREDOC'
abc'asdf"
$(dont-execute-this)
foo"bar"''
END_HEREDOC
)

# this will echo variable with new lines intact
echo "$VAR"
# this will echo variable without new lines (changed to space character)
echo $VAR

작은 따옴표로 END_HEREDOC 시작을 구분해야합니다.

enddoc 구분 기호 END_HEREDOC는 줄에 단독으로 있어야하므로 끝 괄호는 다음 줄에 있습니다.

@ephemient답변 주셔서 감사합니다 .


1
실제로 이것은 어떤 상황에서는 따옴표를 통해 이루어집니다. 나는 이것을 Perl에서 쉽게 할 수 있었다 ...
Neil

32
+1. 이것은 적어도 내 눈에는 가장 읽기 쉬운 솔루션입니다. read 명령에 변수를 포함시키지 않고 페이지의 가장 왼쪽에 변수 이름을 그대로 둡니다.
Clayton Stanley

12
PSA : 개행을 유지 하려면 변수 인용 해야합니다 . echo "$VAR"대신에 echo $VAR.
sevko

7
이것은 지원하지 않는 ashOpenWRT 와 함께 훌륭 read합니다 -d.
David Ehrmann

3
내가 추측 할 수없는 이유 때문에 heredoc에 짝이없는 틱 이 있으면 "예기치 않은 EOF"오류와 함께 실패합니다 .
Radon Rosborough

79

이것은 Dennis 방법의 변형이며 스크립트에서 더 우아해 보입니다.

기능 정의 :

define(){ IFS='\n' read -r -d '' ${1} || true; }

용법:

define VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

echo "$VAR"

즐겨

ps는 지원하지 않는 쉘을 위해 '읽기 루프' 버전을 만들었습니다 read -d. 백틱 과 함께 작동 set -eu하고 짝이 맞지 않지만 잘 테스트되지는 않습니다.

define(){ o=; while IFS="\n" read -r a; do o="$o$a"'
'; done; eval "$1=\$o"; }

1
이것은 피상적으로 만 작동하는 것 같습니다. define 함수는 1의 상태를 반환 할 것이고, 무엇이 수정되어야하는지 잘 모르겠습니다.
fny

1
이것은 bash ( 개행을 유지 read하는 -d ''데 필요한 bashism 을 피하기 위해 함수 의 루프) 외에도 POSIX sh를 지원하도록 수정 될 수 있기 때문에 허용 된 답변보다 우수합니다 .
ELLIOTTCABLE

cat-in-a-subshell 옵션과 달리, 이것은 heredoc에서 없는 백틱 과 함께 작동합니다 . 감사합니다!
Radon Rosborough

이 솔루션은 set -e세트로 작동 하지만 선택한 답변은 작동하지 않습니다. 이 때문에이 될 것 같다http://unix.stackexchange.com/a/265151/20650
ffledgling

2
@fny ps 반환 상태가 오랫동안 수정되었습니다
ttt

36
VAR=<<END
abc
END

stdin을 신경 쓰지 않는 것, 즉 할당으로 리디렉션하기 때문에 작동하지 않습니다.

export A=`cat <<END
sdfsdf
sdfsdf
sdfsfds
END
` ; echo $A

작동하지만이를 사용하지 못하게 할 수있는 백틱이 있습니다. 또한 백틱을 사용하지 않아야하므로 명령 대체 표기법을 사용하는 것이 좋습니다 $(..).

export A=$(cat <<END
sdfsdf
sdfsdf
sdfsfds
END
) ; echo $A

$ (실행 가능)를 포함하도록 내 질문을 업데이트했습니다. 또한 줄 바꿈을 어떻게 유지합니까?
Neil

2
@ l0st3d : 닫습니다 ... $(cat <<'END'대신 사용하십시오 . @Neil : 마지막 줄 바꿈은 변수의 일부가 아니지만 나머지는 유지됩니다.
ephemient

1
줄 바꿈이 유지되지 않는 것 같습니다. 위의 예제를 실행하면 "sdfsdf sdfsdf sdfsfds"... 아! 그러나 글을 쓰면 echo "$A"(즉, $ A를 큰 따옴표로 묶음) 개행을 볼 수 있습니다!
대런 쿡

1
@ 대런 : 아하! 줄 바꿈 문제를 발견했으며 출력 변수 주위에 따옴표를 사용하면 문제가 해결됩니다. 고마워!
javadba

1
흥미롭게도 첫 번째 예제의 단점 때문에 핀치에서 다음과 같이 임시 주석 블록에 사용할 수 있습니다 REM=<< 'REM' ... comment block goes here ... REM. 또는 더 간결하게 : << 'REM' .... "REM"은 "NOTES"또는 "SCRATCHPAD"등이 될 수 있습니다.
Beejor

34

줄 바꿈을 유지하는 솔루션은 여전히 ​​없습니다.

이것은 사실이 아닙니다-아마도 echo의 동작에 의해 잘못 인도되었을 것입니다.

echo $VAR # strips newlines

echo "$VAR" # preserves newlines


5
실제로 이것은 변수를 인용하는 방식의 동작입니다. 따옴표가 없으면 공간을 빼고 다른 매개 변수로 삽입합니다. 따옴표를 사용하면 전체 변수 내용이 하나의 인수로 처리됩니다.
Czipperz

11

Neil의 대답 에서 벗어나 면 종종 var가 필요하지 않으며 변수와 거의 같은 방식으로 함수를 사용할 수 있으며 인라인 또는 read기반 솔루션 보다 훨씬 쉽게 읽을 수 있습니다.

$ complex_message() {
  cat <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
}

$ echo "This is a $(complex_message)"
This is a abc'asdf"
$(dont-execute-this)
foo"bar"''

9

배열은 변수이므로이 경우 mapfile이 작동합니다

mapfile y <<z
abc'asdf"
$(dont-execute-this)
foo"bar"''
z

다음과 같이 인쇄 할 수 있습니다

printf %s "${y[@]}"

3

heredoc 값을 변수에 할당

VAR="$(cat <<'VAREOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
VAREOF
)"

명령의 인수로 사용

echo "$(cat <<'SQLEOF'
xxx''xxx'xxx'xx  123123    123123
abc'asdf"
$(dont-execute-this)
foo"bar"''
SQLEOF
)"

첫 번째 방법을 시도했을 때 줄 사이에 줄 종결자가없는 것 같습니다. 내 리눅스 머신에서 어떤 종류의 구성이어야합니까?
Kemin Zhou

이것은 아마도 변수를 반향 할 때 따옴표를 넣지 않았 음을 의미합니다. 다음과 같이 시도하십시오.echo "$VAR"
Brad Parks

1

NULL이있는 문자열을 읽어야한다는 것을 알았으므로 여기에 던지는 모든 것을 읽을 있는 솔루션이 있습니다. 실제로 NULL을 다루는 경우 16 진수 수준에서 처리해야합니다.

$ 고양이> read.dd.sh

read.dd() {
     buf= 
     while read; do
        buf+=$REPLY
     done < <( dd bs=1 2>/dev/null | xxd -p )

     printf -v REPLY '%b' $( sed 's/../ \\\x&/g' <<< $buf )
}

증명:

$ . read.dd.sh
$ read.dd < read.dd.sh
$ echo -n "$REPLY" > read.dd.sh.copy
$ diff read.dd.sh read.dd.sh.copy || echo "File are different"
$ 

HEREDOC 예 (^ J, ^ M, ^ I 사용) :

$ read.dd <<'HEREDOC'
>       (TAB)
>       (SPACES)
(^J)^M(^M)
> DONE
>
> HEREDOC

$ declare -p REPLY
declare -- REPLY="  (TAB)
      (SPACES)
(^M)
DONE

"

$ declare -p REPLY | xxd
0000000: 6465 636c 6172 6520 2d2d 2052 4550 4c59  declare -- REPLY
0000010: 3d22 0928 5441 4229 0a20 2020 2020 2028  =".(TAB).      (
0000020: 5350 4143 4553 290a 285e 4a29 0d28 5e4d  SPACES).(^J).(^M
0000030: 290a 444f 4e45 0a0a 220a                 ).DONE

0

dimo414의 답변 덕분에 이것은 훌륭한 솔루션의 작동 방식을 보여주고 텍스트에 따옴표와 변수를 쉽게 넣을 수 있음을 보여줍니다.

예제 출력

$ ./test.sh

The text from the example function is:
  Welcome dev: Would you "like" to know how many 'files' there are in /tmp?

  There are "      38" files in /tmp, according to the "wc" command

test.sh

#!/bin/bash

function text1()
{
  COUNT=$(\ls /tmp | wc -l)
cat <<EOF

  $1 Would you "like" to know how many 'files' there are in /tmp?

  There are "$COUNT" files in /tmp, according to the "wc" command

EOF
}

function main()
{
  OUT=$(text1 "Welcome dev:")
  echo "The text from the example function is: $OUT"
}

main

텍스트에서 일치하지 않는 따옴표를 사용하여 어떻게 처리하는지 보는 것이 흥미로울 것입니다. 아마도 겁내지 말고, "$ COUNT"개의 파일이있을 것이므로 아포스트로피 / 단일 인용 부호는 흥미로운 것들을 만들 수 있습니다.
dragon788

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.