我正在编写一个巨大的脚本工厂脚本,为我的服务器生成大量的维护脚本。
到目前为止,我写了一些需要写在一行中的行,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
答案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
)有点丑陋,它可能应该被替换为关联数组。