web-dev-qa-db-ja.com

sshコマンドラインのawkが「予期しない改行または文字列の終わり」と文句を言う解決方法

以下のスクリプトの実行中に問題が発生しました、^予期しない改行または文字列の終わりこれを解決するにはどうすればよいですか?

[root@emrbldbgdapd2 ~]# ./collectdata.sh
collect the data of 10.209.61.124
awk: cmd. line:1: {print
awk: cmd. line:1:       ^ unexpected newline or end of string
collect the data of 10.209.61.125
awk: cmd. line:1: {print
awk: cmd. line:1:       ^ unexpected newline or end of string
[root@emrbldbgdapd2 ~]#
==========
[root@emrbldbgdapd2 ~]# more collectdata.sh
for i in `cat test`
do echo "collect the data of $i"
ssh -o LogLevel=error -o ConnectTimeout=5 $i 'hostname;
echo "############################";
free -g;
echo "######################################";
echo "######################################";
netstat -nr | grep [0-9] | tr [a-z] [A-Z];
echo "######################################";
echo "######################################";
mount|awk '{print $1,$3,$5}'|sort;
echo "######################################";
echo "######################################";
ip a s | grep -i eth*;
echo "######################################"'>output/$i-`date "+%d%b%Y"`
done
[root@emrbldbgdapd2 ~]#
4
Levyle

単一引用符で囲まれた文字列内で単一引用符を使用しようとしています。 mount | awk | sortパイプラインと同じ行にある最初の内部単一引用符は、ssh行で開始された単一引用符を終了します。

実際のエラーはawkが原因です。 {print ,,}のようなコマンドラインが渡されます($1$3$5は、呼び出し側のシェルによって空の文字列に展開され、一重引用符がないためこの時点で)。これは、awkプログラムとして解釈され、}というファイルで実行される構文エラー(,,}がない)が発生します。

スクリプトを実際のスクリプトファイルに入れて、代わりにリモートホストで実行することをお勧めします。これにより、引用の問題を回避し、より保守しやすい手順を作成できるようになります。

さらに、

  1. readの結果に対してwhileループを実行するのではなく、forループでcatを使用します。
  2. 特に内部スクリプトは、いくつかの場所で必要な引用を欠いています。これは、たとえばgrep [0-9]はファイル名のグロビングをトリガーしません。
  3. grep eth*は奇妙に見えます。もしかしてgrep -e 'eth.'?現在のディレクトリでethで始まるすべての名前を取得するファイル名グロビングパターンとして機能することとは別に、パターン(正規表現として解釈される場合)はetの後にゼロまたはその他h
  4. 使用するバックティックについては、 * shシェルのバックティックを使用する(つまり、 `cmd`)は非推奨ですか? を参照してください。

変更されたスクリプト:

#!/bin/sh

now=$( date +%d%b%Y )

if [ -d output ]; then
    echo 'Output directory missing' >&2
    exit 1
fi

while read Host; do
    printf 'Collecting data from "%s"...\n' "$Host"

    ssh -n -o LogLevel=error -o ConnectTimeout=5 "$Host" \
      sh <<'END_SCRIPT' >output/"$Host-$now"
        echo '## HOSTNAME:'
        hostname
        echo '## FREE:'
        free -g
        echo '## NETSTAT:'
        netstat -nr | grep '[0-9]' | tr 'a-z' 'A-Z'
        echo '## MOUNT:'
        mount | awk '{ print $1, $3, $5 }' | sort
        echo '## IP:'
        ip a s | grep -i 'eth.*'
END_SCRIPT
done <hosts.txt
10
Kusalananda

他の回答で述べたように、問題は、リモートで実行しているコマンドで単一引用符を使用することです。 SEの構文の色分けが示すように、{print $1,$3,$5}は引用符で囲まれておらず、その周りの単一引用符はリモートシェルに到達していません。

これを回避する1つの方法は、ヒアドキュメントを使用して、標準入力を介してスクリプトをリモートに送信することです。

ssh somehost /bin/sh > outputfile <<"EOF"
hostname
free -g
netstat -nr | grep "[0-9]" | tr "[a-z]" "[A-Z]"
mount | awk '{print $1,$3,$5}' | sort
# and so on
EOF

ヒアドキュメントの区切り記号"EOF"を引用符で囲んで、$1変数がローカルシェルによって展開されないようにします。ローカルシェルでの引用の問題はこれで対処できますが、グロブ([0-9およびfoo*)のように見えるもののように、リモート用のものを引用する必要があります。

tra-z引数に角かっこを必要としません(これらは何の害もありません。[[に変更し、 ]と同じ)。そのnetstatの出力にはインターフェース名が含まれていると思いますが、それらを大文字にすると名前が異なります。

5
ilkkachu

行を変更します。

mount|awk '{print $1,$3,$5}'|sort;

に:

mount|awk '\''{print $1,$3,$5}'\''|sort;
0
Isaac