heredoc (EOF) 中是否有类似 echo -n 的东西?

heredoc (EOF) 中是否有类似 echo -n 的东西?

我正在编写一个巨大的脚本工厂脚本,为我的服务器生成大量的维护脚本。

到目前为止,我写了一些需要写在一行中的行,echo -ne例如

echo -n "if (( " | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null

# Generate exitCode check for each Server
IFS=" "
COUNT=0
while read -r name ipAddr
do
    if(($COUNT != 0))
    then
        echo -n " || " | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null
    fi

    echo -n "(\$"$name"_E != 0 && \$"$name"_E != 1)" | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null

    COUNT=$((COUNT+1))

    JOBSDONE=$((JOBSDONE+1))
    updateProgress $JOBCOUNT $JOBSDONE
done <<< "$(sudo cat /root/.virtualMachines)"

echo " ))" | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null

这将生成如下代码(例如,如果我的配置文件中有 2 个服务器)

if (( ($server1_E != 0 && $server1_E != 1) || ($server2_E != 0 && $server2_E != 1) ))

所有其他不需要内联编写的代码块我都使用 heredocs 生成,因为我发现它们更易于编写和维护。例如,在上面的代码之后,我生成了其余的代码块,如下所示

cat << EOF | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null
then
    # Print out ExitCode legend
    echo " ExitCode 42 - upgrade failed"
    echo " ExitCode 43 - Upgrade failed"
    echo " ExitCode 44 - Dist-Upgrade failed"
    echo " ExitCode 45 - Autoremove failed"
    echo ""
    echo ""
fi
EOF

所以最终的代码块看起来像

if (( ($server1_E != 0 && $server1_E != 1) || ($server2_E != 0 && $server2_E != 1) ))
then
    # Print out ExitCode legend
    echo " ExitCode 42 - upgrade failed"
    echo " ExitCode 43 - Upgrade failed"
    echo " ExitCode 44 - Dist-Upgrade failed"
    echo " ExitCode 45 - Autoremove failed"
    echo ""
    echo ""
fi

我的问题
有没有办法让 heredoc 的行为类似于echo -ne没有行尾符号的行为?

答案1

不,heredoc 总是以换行符结尾。但你仍然可以删除最后一个换行符,例如使用 Perl:

cat << 'EOF' | perl -pe 'chomp if eof'
First line
Second line
EOF
  • -p逐行读取输入,运行中指定的代码-e,并打印(可能已修改)行
  • 咀嚼删除最后一个换行符(如果存在),结束语在文件末尾返回 true。

答案2

Heredocs 总是以换行符结尾,但您可以简单地将其删除。我知道的最简单的方法是使用程序head

head -c -1 <<EOF | sudo tee file > /dev/null
my
heredoc
content
EOF

答案3

有没有办法让 heredoc 的行为类似于没有行尾符号的 echo -ne?

简短的回答是 heredoc 和 herestrings 就是这样构建的,所以否 - here-doc 和 herestring 将始终添加尾随换行符,并且没有选项或本机方法来禁用它(也许在未来的 bash 版本中会有?)。

但是,通过 bash-only 方式解决此问题的一种方法是通过标准while IFS= read -r方法读取 heredoc 给出的内容,但添加延迟并打印最后一行printf而不打印尾随换行符。以下是使用 bash-only 可以执行的操作:

#!/usr/bin/env bash

while true
do
    IFS= read -r line || { printf "%s" "$delayed";break;}
    if [ -n "$delayed" ]; 
    then
        printf "%s\n" "$delayed"
    fi
    delayed=$line
done <<EOF
one
two
EOF

您在这里看到的是通常的 while 循环方法IFS= read -r line,但是每行都存储在一个变量中,因此会延迟(因此我们跳过打印第一行,存储它,并且只有当我们读到第二行时才开始打印)。这样我们就可以捕获最后一行,并且当下一次迭代read返回退出状态 1 时,我们就会通过 打印最后一行而不打印换行符printf。冗长吗?是的。但它有效:

$ ./strip_heredoc_newline.sh                                      
one
two$ 

请注意,$提示符被向前推,因为没有换行符。作为奖励,此解决方案是可移植的且符合 POSIX 规范,因此它也应该可以在ksh和中工作。dash

$ dash ./strip_heredoc_newline.sh                                 
one
two$

答案4

您可以按照自己喜欢的任何方式删除换行符,管道tr可能是最简单的方法。当然,这会删除所有换行符。

$ tr -d '\n' <<EOF 
if ((
EOF

但是,如果您正在构建的语句不需要这样做,(( .. ))即使它包含换行符,算术表达式也应该正常工作。

所以你可以这样做

cat <<EOF 
if ((
EOF
for name in x y; do 
    cat << EOF
    ( \$${name}_E != 0 && \$${name}_E != 1 ) ||
EOF
done
cat <<EOF
    0 )) ; then 
    echo do something
fi
EOF

生产

if ((
    ( $x_E != 0 && $x_E != 1 ) ||
    ( $y_E != 0 && $y_E != 1 ) ||
    0 )) ; then 
    echo do something
fi

不过,将其构建在循环中仍然很丑陋。|| 0当然,这样做是为了我们不需要对第一行或最后一行进行特殊处理。


此外,每个输出中都有这个| sudo tee ...。我认为我们可以通过只打开一次重定向(使用进程替换)并将其用于稍后的打印来摆脱它:

exec 3> >(sudo tee outputfile &>/dev/null)
echo output to the pipe like this >&3
echo etc. >&3
exec 3>&-         # close it in the end

坦率地说,我仍然认为从外部脚本中的变量构建变量名(像这样\$${name}_E)有点丑陋,它可能应该被替换为关联数组

相关内容