HEREDOC 텍스트를 쉘 스크립트 변수에 어떻게 넣나요?

HEREDOC 텍스트를 쉘 스크립트 변수에 어떻게 넣나요?

POSIX 호환 방식으로 HEREDOC 텍스트를 쉘 스크립트 변수에 넣으려고 합니다. 나는 이렇게 시도한다:

#!/bin/sh

NEWLINE="
"

read_heredoc2() {
  while IFS="$NEWLINE" read -r read_heredoc_line; do
    echo "${read_heredoc_line}"
  done
}

read_heredoc2_result="$(read_heredoc2 <<'HEREDOC'

                        _                            _ _
                       | |                          | (_)
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \
 | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
             __/ | |
            |___/|_|



HEREDOC
)"

echo "${read_heredoc2_result}"

이로 인해 다음 오류가 발생했습니다.

                        _                            _ _
                       | |                          | (_)
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _  | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
             __/ | |
            |___/|_|

다음 방법은 효과가 있지만 무작위 출력 변수를 사용하는 것이 얼마나 다루기 힘든지 마음에 들지 않습니다.

#!/bin/sh

NEWLINE="
"

read_heredoc1() {
  read_heredoc_first=1
  read_heredoc_result=""
  while IFS="$NEWLINE" read -r read_heredoc_line; do
    if [ ${read_heredoc_first} -eq 1 ]; then
      read_heredoc_result="${read_heredoc_line}"
      read_heredoc_first=0
    else
      read_heredoc_result="${read_heredoc_result}${NEWLINE}${read_heredoc_line}"
    fi
  done
}

read_heredoc1 <<'HEREDOC'

                        _                            _ _            
                       | |                          | (_)           
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___ 
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \
 | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
             __/ | |                                                
            |___/|_|                                                



HEREDOC

echo "${read_heredoc_result}"

올바른 출력:

                        _                            _ _            
                       | |                          | (_)           
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___ 
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \
 | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
             __/ | |                                                
            |___/|_|                                                

어떤 아이디어가 있나요?

답변1

문제는,큰 타격을 입은 상태에서, 내부 $( ... )이스케이프(및 기타) 시퀀스는 구분 기호 자체에 해당 시퀀스가 ​​없더라도 구문 분석됩니다. 개행 문자가 이스케이프 처리 되었으므로 \이중선이 표시됩니다. 당신이 보고 있는 것은 실제로 Bash의 구문 분석 문제입니다. 다른 쉘에서는 이 작업을 수행하지 않습니다. 백틱은 이전 버전에서도 문제가 될 수 있습니다. 나는 가지고있다이것이 Bash의 버그임을 확인했습니다., 향후 버전에서 수정될 예정입니다.

최소한 기능을 크게 단순화할 수 있습니다.

func() {
    res=$(cat)
}
func <<'HEREDOC'
...
HEREDOC

출력 변수를 선택하려면 매개변수화할 수 있습니다.

func() {
    eval "$1"'=$(cat)'
}
func res<<'HEREDOC'
...
HEREDOC

또는 다음이 없는 다소 추악한 것 eval:

{ res=$(cat) ; } <<'HEREDOC'
...
HEREDOC

필요한 것은 나중에 변수를 계속 사용할 수 있도록 하는 {}것이 아닙니다 .()

이 작업을 얼마나 자주 수행하고 어떤 목적으로 수행하는지에 따라 하나 또는 다른 옵션을 선호할 수 있습니다. 마지막은 가장 간결한 일회성입니다.


을 사용할 수 있는 경우 zsh원래 명령 대체 + heredoc은 그대로 작동하지만 이 모든 항목을 더 축소할 수도 있습니다.

x=$(<<'EOT'
...
EOT
)

Bash는 이것을 지원하지 않으며 다른 쉘에는 당신이 가진 문제가 없을 것이라고 생각합니다.

답변2

OP 솔루션 정보:

  • 특정 상수 변수가 허용되면 변수를 할당하는 데 eval이 필요하지 않습니다.

  • HEREDOC을 수신하는 함수를 호출하는 일반 구조를 구현할 수도 있습니다.

모든 (합리적인) 쉘에서 작동하는 두 가지 문제에 대한 해결책은 다음과 같습니다.

#!/bin/bash
nl="
"

read_heredoc(){
    var=""
    while IFS="$nl" read -r line; do
        var="$var$line$nl"
    done 
}


read_heredoc <<'HEREDOC'

                        _                            _ _
                       | |                          | (_)
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \
 | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
             __/ | |
            |___/|_|

HEREDOC

read_heredoc2_result="$str"

printf '%s' "${read_heredoc2_result}"

원래 문제에 대한 해결책.

bash 2.04(최근에는 zsh, lksh, mksh)부터 작동한 솔루션입니다.
아래의 더 이식성이 뛰어난(POSIX) 버전을 참조하세요.

#!/bin/bash
read_heredoc() {
    IFS='' read -d '' -r var <<'HEREDOC'

                        _                            _ _
                       | |                          | (_)
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \
 | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
             __/ | |
            |___/|_|



HEREDOC

}

read_heredoc
echo "$var"

핵심 명령

IFS='' read -d '' -r var <<'HEREDOC'

작동 방식은 다음과 같습니다.

  1. HEREDOC후속 텍스트의 확장을 피하기 위해 단어는 (작은) 인용부호로 표시됩니다.
  2. "here doc"의 내용은 표준 입력에 <<.
  3. 이 옵션은 Document Here의 전체 내용을 -d ''강제 로 읽습니다.read
  4. -r옵션은 백슬래시로 묶인 문자의 해석을 방지합니다.
  5. 핵심 명령은 와 유사합니다 read var.
  6. 마지막 세부 사항 중 하나는 IFS=''기본 IFS: 에서 선행 또는 후행 문자를 제거하여 읽기를 방지한다는 것입니다 spacetabnewline.

ksh에서 이 옵션의 null 값은 -d ''효과가 없습니다.
해결 방법으로 텍스트에 "캐리지 리턴" 문자가 없으면 a가 -d $'\r'작동합니다( $'\r'물론 a가 각 줄의 끝에 추가되는 경우).


추가된 요구 사항(주석)은 POSIX 호환 솔루션을 생성하는 것입니다.

POSIX

POSIX 옵션으로만 실행되도록 이 아이디어를 확장하세요. 이는 주로 for 가
없음을 의미합니다 . 이렇게 하면 모든 줄을 강제로 읽게 됩니다. 이로 인해 한 번에 한 줄씩 캡처해야 합니다. 그런 다음 후행 새 줄을 작성하려면 추가해야 합니다(읽기 시 제거되기 때문).-dread

var

#!/bin/sh

nl='
'

read_heredoc() {
    unset var
    while IFS="$nl" read -r line; do
        var="$var$line$nl"
    done <<\HEREDOC

                        _                            _ _
                       | |                          | (_)
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \ 
 | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/ 
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___| 
             __/ | | 
            |___/|_| 



HEREDOC

}

read_heredoc
printf '%s' "$var"

이것은 모든 합리적인 쉘에서 작동하고 테스트되었습니다.

답변3

후행 줄 바꿈을 지원하기 위해 @MichaelHomer의 답변을 원래 솔루션과 결합했습니다. @EliahKagan이 지적한 링크에서 제안된 해결 방법을 사용하지 않았습니다. 첫 번째는 마법 문자열을 사용하고 마지막 두 개는 POSIX와 호환되지 않기 때문입니다.

#!/bin/sh

NEWLINE="
"

read_heredoc() {
  read_heredoc_result=""
  while IFS="${NEWLINE}" read -r read_heredoc_line; do
    read_heredoc_result="${read_heredoc_result}${read_heredoc_line}${NEWLINE}"
  done
  eval $1'=${read_heredoc_result}'
}

read_heredoc heredoc_str <<'HEREDOC'

                        _                            _ _
                       | |                          | (_)
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \
 | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
             __/ | |
            |___/|_|




HEREDOC

echo "${heredoc_str}"

답변4

cat의 쓸모없는 사용(따옴표 \ 및 `):

myplaceonline="
                       _                            _ _            
 _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | (_)_ __   ___ 
| '_ \` _ \\| | | | '_ \\| |/ _\` |/ __/ _ \\/ _ \\| '_ \\| | | '_ \\ / _ \\
| | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
|_| |_| |_|\\__, | .__/|_|\\__,_|\\___\\___|\\___/|_| |_|_|_|_| |_|\\___|
       |___/|_
"

또는 따옴표 없이:

myplaceonline="$(figlet myplaceonline)"

관련 정보