복잡한 여러 줄 문자열을 변수에 쓰는 깔끔한 방법


109

bash 스크립트 내의 변수에 복잡한 XML을 작성해야합니다. xml은 bash 스크립트 내에서 읽을 수 있어야합니다. xml 조각이 존재하는 곳이기 때문에 다른 파일이나 소스에서 읽을 수 없습니다.

그래서 내 질문은 내 bash 스크립트 내에서 사람이 읽을 수있는 긴 문자열이 있다면 가장 좋은 방법은 무엇입니까?

이상적으로 나는 원한다 :

  • 캐릭터를 탈출 할 필요가 없습니다
  • 사람이 읽을 수 있도록 여러 줄로 나눕니다.
  • 들여 쓰기 유지

이것을 EOF 또는 다른 것으로 할 수 있습니까? 누구든지 나에게 모범을 보여 줄 수 있습니까?

예 :

String = <<EOF
 <?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
EOF

나는 당신이 그 데이터를 다시 스트림으로 덤프 할 것이라고 기꺼이 생각합니다. 일을 더 복잡하게하고 스트림을 사용할 수 있는데 왜 변수에 저장합니까?
Zenexer

추가 참조 : 여분의 공간
Yibo Yang

답변:


140

따옴표를 이스케이프하지 않고 변수에 텍스트를 넣습니다. 또한 불균형 따옴표 (아포스트로피, 즉 ') 도 처리합니다 . 센티넬 (EOF) 주위에 따옴표를 넣으면 텍스트가 매개 변수 확장을 거치지 않습니다. -d''원인은 여러 줄 (줄 바꿈 무시) 읽을 수 있습니다. readBash 내장이므로와 같은 외부 명령을 호출 할 필요가 없습니다 cat.

IFS='' read -r -d '' String <<"EOF"
<?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
EOF

17
피하기 위해 +1 cat.
James Sneeringer

4
cat외부 명령입니다. 그것을 사용하지 않으면 그렇게 할 수 없습니다. 게다가, 어떤 사람들은 당신이 두 개 미만의 주장으로 고양이를 사용한다면 "우리가 잘못하고있다"( "무의미한 사용"과는 다른) 철학을 가지고있다 cat.
Dennis Williamson

9
절대로 두 번째 EOF를 들여 쓰지 마십시오 .... (여러 테이블에서 머리 앞머리까지)
IljaBek

9
나는 위의 진술을 사용하려고했습니다 set -e. 그것은 것 같다 read항상이 아닌 0을 반환합니다. 할 수 있습니다 두께를 사용하여이 동작! read -d .......
krissi

11
이 다중 회선 사용하는 경우 그리고 String파일에 기록 할 변수처럼 주위에 "따옴표"변수를 넣어 echo "${String}" > /tmp/multiline_file.txtecho "${String}" | tee /tmp/multiline_file.txt. 그것을 찾기 위해 한 시간 이상을 걸렸습니다.
Aditya

28

거의 다 왔습니다. 문자열 어셈블리에 cat 을 사용 하거나 전체 문자열을 인용하십시오 (이 경우 문자열 내부의 따옴표를 이스케이프해야합니다).

#!/bin/sh
VAR1=$(cat <<EOF
<?xml version="1.0" encoding='UTF-8'?>
<painting>
  <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's "Foligno" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>
EOF
)

VAR2="<?xml version=\"1.0\" encoding='UTF-8'?>
<painting>
  <img src=\"madonna.jpg\" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's \"Foligno\" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>"

echo "${VAR1}"
echo "${VAR2}"

불행히도 "Raphael 's"의 아포스트로피는 첫 번째 것이 작동하지 않게합니다.
Dennis Williamson

두 과제는 결국 저에게 효과적입니다. VAR1의 작은 따옴표는 문제가되어서는 안됩니다 (적어도 bash에는 안 됨). 어쩌면 구문 강조 표시로 잘못 인도되었을 수 있습니까?
joschi

1
스크립트에서는 작동하지만 Bash 프롬프트에서는 작동하지 않습니다. 명확하지 않아서 죄송합니다.
Dennis Williamson

1
EOF를 'EOF'또는 로 인용하는 것이 좋습니다 "EOF". 그렇지 않으면 쉘 변수가 구문 분석됩니다.
Stanislav German-Evtushenko

13
#!/bin/sh

VAR1=`cat <<EOF
<?xml version="1.0" encoding='UTF-8'?>
<painting>
  <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's "Foligno" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>
EOF
`
echo "VAR1: ${VAR1}"

이것은 Bourne 쉘 환경에서 잘 작동합니다.


1
+1이 솔루션은 $ {foo}와 같은 변수 대체를 허용합니다
Offirmo

거꾸로 : sh 호환. 단점 : 배틱은 배쉬에서 더 이상 사용되지 않으며 / 감지됩니다. 만약 sh와 bash 중 하나를 선택해야한다면 ...
Zenexer

2
언제부터 백틱이 더 이상 사용되지 않거나 감지되지 않습니까? 그냥 궁금해
Alexander Mills

6

또 같은 방법으로 ...

스크립트 들여 쓰기를 허용 하기 위해 변수와 특수 행을 사용하여 각 줄의 시작 부분에 <<- 를 삭제 하는 것을 좋아합니다 .

#!/bin/bash

mapfile Pattern <<-eof
        <?xml version="1.0" encoding='UTF-8'?>
        <painting>
          <img src="%s" alt='%s'/>
          <caption>%s, painted in
          <date>%s</date>-<date>%s</date>.</caption>
        </painting>
        eof

while IFS=";" read file alt caption start end ;do
    printf "${Pattern[*]}" "$file" "$alt" "$caption" "$start" "$end"
  done <<-eof
        madonna.jpg;Foligno Madonna, by Raphael;This is Raphael's "Foligno" Madonna;1511;1512
        eof

경고 : 더이없는 빈 공간 전에 eof표가 .

<?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
몇 가지 설명 :
  • mapfile 여기 전체를 배열로 읽습니다 .
  • 구문 "${Pattern[*]}"은이 배열을 문자열로 캐스트합니다.
  • 필요한 문자열 IFS=";"이 없기 때문에 사용 ;합니다
  • 구문 은 나머지 스크립트에 대해 수정 while IFS=";" read file ...되지 않습니다 IFS. 이 read경우 modified 만 사용하십시오 IFS.
  • 포크가 없습니다.

mapfileBash 4 이상 이 필요합니다. 구문 은 따옴표"${Pattern[*]}" 로 묶인 경우 (예제 코드에 표시된대로) 배열을 문자열로 캐스트합니다 .
Dennis Williamson

예, bash 4는 이 질문을 받았을 때 매우 새롭습니다 .
F. Hauri

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