heredoc(EOF)にecho -nに似たものはありますか?


12

私は、サーバー用の多くのメンテナンススクリプトを生成する巨大なスクリプトファクトリスクリプトを書いています。

今までは、echo -neたとえば、1行で記述する必要がある行をいくつか書きました。

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

私の質問行末記号なしの
ように、ヒアドキュメントを動作させる方法はありecho -neますか?


1
sudoスクリプトで必要な場合、それは間違っています:代わりにスクリプト全体をルートとして実行してください!
デザート

1
いいえ、ルートとして実行したくない他のことも行っているため
derHugo


私のようにそれをやっている問題は何ですか?
-derHugo

4
もちろん、問題がありsudoます。ユーザー名なしでスクリプトを入力すると、遅延を入力しない限り、パスワードを要求されます。ターミナルでスクリプトを実行しないと、さらに悪いことに、パスワードクエリも表示されず、機能しません。sudo -uアプローチは、これらの問題のいずれかを持っていません。
デザート

回答:


9

いいえ、heredocは常に改行で終わります。ただし、たとえばPerlを使用すると、最後の改行を削除できます。

cat << 'EOF' | perl -pe 'chomp if eof'
First line
Second line
EOF
  • -p入力を行ごとに読み取り、で指定されたコードを実行し-e、(変更された可能性のある)行を出力します
  • chompは最後の改行が存在する場合はそれを削除し、eofはファイルの終わりでtrueを返します。

1
@Yaronファイルの終わり(この場合は閉じたパイプ)に遭遇すると、最後の行から改行文字を切り詰めます(heredocとherestringは自動的に追加されます)
Sergiy Kolodyazhnyy

7

行末記号なしでecho -neと同様に動作するheredocを持つ方法はありますか?

簡単な答えは、heredocとherestringsはそのように構築されているだけなので、no-here-docとherestringは常に末尾の改行を追加し、それを無効にするオプションやネイティブな方法はありません(おそらく将来のbashリリースにありますか?)

ただし、bashのみの方法でアプローチする方法の1つは、heredocが標準のwhile IFS= read -r方法で提供するものを読み、遅延を追加し、最後の行をprintf末尾の改行なしで印刷することです。bashのみでできることは次のとおりです。

#!/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アプローチですが、各行は変数に格納されるため遅延します(したがって、最初の行の印刷をスキップして格納し、2行目を読み取ったときにのみ印刷を開始します)。そうすれば、最後の行をキャプチャでき、次の繰り返しでread終了ステータス1が返されたとき、つまり、改行なしで最後の行を印刷しますprintf。冗長?はい。しかし、それは動作します:

$ ./strip_heredoc_newline.sh                                      
one
two$ 

$改行がないため、プロンプト文字が前方に押し出されていることに注意してください。それはで動作するはずですので、ボーナスとして、このソリューションは、ポータブルおよびPOSIXっぽいですkshdash同様。

$ dash ./strip_heredoc_newline.sh                                 
one
two$

6

別のアプローチを試すことをお勧めします。生成されたスクリプトを複数のechoステートメントとheredocsからつなぎ合わせるのではなく。単一のheredocを使用することをお勧めします。

ヒアドキュメント内で変数展開とコマンド置換を使用できます。この例のように:

#!/bin/bash
cat <<EOF
$USER $(uname)
EOF

これにより、出力を1つずつ作成するよりもはるかに読みやすい最終結果が得られることがよくあります。

また#!、スクリプトの最初の行を忘れているようです。この#!行は、正しくフォーマットされたスクリプトでは必須です。#!行なしでスクリプトを実行しようとすると、フォーマットの悪いスクリプトを回避するシェルから呼び出す場合にのみ機能し、その場合でも、意図したものとは異なるシェルによって解釈される可能性があります。


忘れて#!いませんが、私のコードブロックは2000行のスクリプトのほんの一部です;)whileループでコードの1行を生成する問題をあなたの提案がどのように解決するかは今のところわかりません...
derHugo

@derHugoループが必要な行の部分のコマンド置換は、それを行う1つの方法です。別の方法は、ヒアドキュメントの前に変数でその部分を構築することです。どちらが最も読みやすいかは、ループで必要なコードの長さと、問題の行の前にあるヒアドキュメントの行数によって決まります。
カスペルド

スクリプトの前半で定義されたシェル関数を呼び出す@derHugoコマンド置換は、その1行を生成するために大量のコードが必要な場合に読みやすい3番目のアプローチです。
カスペルド

@derHugo:(1)必要なすべてのコマンドを変数に入れるループを作成できます。(2)$( ...command...)ヒアドキュメント内で使用して、ヒアドキュメント内のその時点でこれらのコマンドの出力をインラインで挿入できます。(3)Bashには配列変数があり、これをインラインで展開し、必要に応じて開始/終了時に置換を使用して追加できます。これらを合わせると、kasperdの提案はより強力になります(そして、スクリプトは潜在的にはるかに読みやすくなります)。
-psmears

私は、そのようなテンプレートの動作(wysiwyg)のため、ヒアドキュメントを使用しています。私の意見では、テンプレート全体を別の場所で作成し、それをヒアドキュメントに渡すことは、物事の読みやすさや保守を実際に容易にするものではありません。
derHugo

2

ヒアドキュメントは常に改行文字で終わりますが、単に削除できます。私が知っている最も簡単な方法は、headプログラムを使用することです:

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

-cまた、負の値を取ることを知りませんでした!でも、以上のようなpearl解決策
derHugo

1

あなたが好きな方法で改行を削除することができますtr。もちろん、これはすべての改行を削除します。

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

ただし、作成しているifステートメントではその必要はありません(( .. ))。改行が含まれていても、算術式は正常に機能します。

だから、あなたはできる

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)外側のスクリプトの変数から変数名を構築することは少しいもので、おそらく連想配列に置き換える必要があると思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.