Kev*_*vin 12 shell-script posix
我正在尝试以符合 POSIX 的方式将 HEREDOC 文本带入 shell 脚本变量中。我试过这样:
#!/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}"
Run Code Online (Sandbox Code Playgroud)
这产生了以下错误:
_ _ _
| | | (_)
_ __ ___ _ _ _ __ | | __ _ ___ ___ ___ _ __ | |_ _ __ ___
| '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ | | | | | | |_| | |_) | | (_| | (_| __/ (_) | | | | | | | | | __/
|_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
__/ | |
|___/|_|
Run Code Online (Sandbox Code Playgroud)
以下工作,但我不喜欢使用随机输出变量有多么笨重:
#!/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}"
Run Code Online (Sandbox Code Playgroud)
正确的输出:
_ _ _
| | | (_)
_ __ ___ _ _ _ __ | | __ _ ___ ___ ___ _ __ | |_ _ __ ___
| '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \
| | | | | | |_| | |_) | | (_| | (_| __/ (_) | | | | | | | | | __/
|_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
__/ | |
|___/|_|
Run Code Online (Sandbox Code Playgroud)
有任何想法吗?
Mic*_*mer 15
问题是,在 Bash 中,内部$( ... )转义(和其他)序列被解析,即使heredoc 本身没有它们。你会得到一条双线,因为\逃脱了换行符。您所看到的实际上是 Bash 中的解析问题 - 其他 shell 不会这样做。反引号在旧版本中也可能是一个问题。我已经确认这是 Bash 中的一个错误,它将在未来的版本中修复。
您至少可以大大简化您的功能:
func() {
res=$(cat)
}
func <<'HEREDOC'
...
HEREDOC
Run Code Online (Sandbox Code Playgroud)
如果要选择输出变量,可以对其进行参数化:
func() {
eval "$1"'=$(cat)'
}
func res<<'HEREDOC'
...
HEREDOC
Run Code Online (Sandbox Code Playgroud)
或者一个相当丑陋的没有eval:
{ res=$(cat) ; } <<'HEREDOC'
...
HEREDOC
Run Code Online (Sandbox Code Playgroud)
的{}需要,而不是(),这样变量仍然可用之后。
根据您执行此操作的频率和目的,您可能更喜欢这些选项中的一个或另一个。最后一个是一次性最简洁的。
如果您能够使用zsh,您的原始命令替换 + heredoc 将按原样工作,但您也可以进一步折叠所有这些:
x=$(<<'EOT'
...
EOT
)
Run Code Online (Sandbox Code Playgroud)
Bash 不支持这一点,我认为任何其他 shell 也不会遇到您遇到的问题。
关于OP解决方案:
如果允许使用某个常量变量,则不需要 eval 来分配变量。
也可以实现调用接收 HEREDOC 的函数的一般结构。
在解决了这两个项目的所有(合理)shell 中都有效的解决方案是:
#!/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}"
Run Code Online (Sandbox Code Playgroud)
自 bash 2.04(以及最近的 zsh、lksh、mksh)以来有效的解决方案。
查看下面的更便携 (POSIX) 版本。
#!/bin/bash
read_heredoc() {
IFS='' read -d '' -r var <<'HEREDOC'
_ _ _
| | | (_)
_ __ ___ _ _ _ __ | | __ _ ___ ___ ___ _ __ | |_ _ __ ___
| '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \
| | | | | | |_| | |_) | | (_| | (_| __/ (_) | | | | | | | | | __/
|_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
__/ | |
|___/|_|
HEREDOC
}
read_heredoc
echo "$var"
Run Code Online (Sandbox Code Playgroud)
核心命令
IFS='' read -d '' -r var <<'HEREDOC'
Run Code Online (Sandbox Code Playgroud)
工作原理如下:
HEREDOC被(单)引用,以避免对后面的文本进行任何扩展。<<。-d ''强制read吞下“here doc”的全部内容。-r选项避免解释反斜杠引号字符。read var.IFS='',这将避免读取删除默认 IFS: 中的前导或尾随字符spacetabnewline。在 ksh 中,该-d ''选项的空值不起作用。
作为一种解决方法,如果文本没有“回车”,则 a-d $'\r'有效(当然,如果将 a$'\r'添加到每行的末尾)。
添加的(在评论中)要求是生成符合 POSIX 的解决方案。
扩展这个想法,使其仅与 POSIX 选项一起运行。
这意味着主要没有-dfor 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"
Run Code Online (Sandbox Code Playgroud)
这在所有合理的外壳中都有效(并且已经过测试)。