「予期しない改行文字または文字列の終わり」について文句を言うsshコマンドラインのawkを修正する方法は?

「予期しない改行文字または文字列の終わり」について文句を言う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 ~]#

答え1

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

実際のエラーはawk次のコマンドラインを取得します{print ,,}(呼び出しシェルによって$1$3$5の文字列に展開され、現在単一引用符が存在しません)。これは、というファイルで実行されるプログラムの構文awkエラー(欠落)として解釈されます。},,}

参照の問題を回避し、メンテナンスが簡単なプロセスを作成できるように、スクリプトを実際のスクリプトファイルに配置してリモートホストで実行することをお勧めします。

また、

  1. read結果を繰り返すwhile代わりにループで使用してください。forcat
  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

答え2

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

この問題を解決する1つの方法は、次のドキュメントを使用してstdinを介してリモートでスクリプトを送信することです。

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

ローカルシェルが変数を拡張しないように、here-doc区切り文字を"EOF"引用符で囲みます。これはローカルシェルの引用の問題を解決しますが、リモートについてはglob(および)$1のように見えるものを引用する必要があります。[0-9foo*

tr引数には角括弧は必要ありませんa-z(害を及ぼさず、aと同様にaを[aに変更するように指示するだけです)。出力にインターフェイス名が含まれていると思いますが、それを大文字として使用すると名前が異なります。[]netstat

答え3

ルート変更:

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

到着する:

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

関連情報