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'
작동 방식은 다음과 같습니다.
HEREDOC
후속 텍스트의 확장을 피하기 위해 단어는 (작은) 인용부호로 표시됩니다.- "here doc"의 내용은 표준 입력에
<<
. - 이 옵션은 Document Here의 전체 내용을
-d ''
강제 로 읽습니다.read
- 이
-r
옵션은 백슬래시로 묶인 문자의 해석을 방지합니다. - 핵심 명령은 와 유사합니다
read var
. - 마지막 세부 사항 중 하나는
IFS=''
기본 IFS: 에서 선행 또는 후행 문자를 제거하여 읽기를 방지한다는 것입니다 spacetabnewline.
ksh에서 이 옵션의 null 값은 -d ''
효과가 없습니다.
해결 방법으로 텍스트에 "캐리지 리턴" 문자가 없으면 a가 -d $'\r'
작동합니다( $'\r'
물론 a가 각 줄의 끝에 추가되는 경우).
추가된 요구 사항(주석)은 POSIX 호환 솔루션을 생성하는 것입니다.
POSIX
POSIX 옵션으로만 실행되도록 이 아이디어를 확장하세요. 이는 주로 for 가
없음을 의미합니다 . 이렇게 하면 모든 줄을 강제로 읽게 됩니다. 이로 인해 한 번에 한 줄씩 캡처해야 합니다. 그런 다음 후행 새 줄을 작성하려면 추가해야 합니다(읽기 시 제거되기 때문).-d
read
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)"