1

DD-WRTルーター用に作成されたbashスクリプトがあります。これは、NASデーモンを定期的に再起動して、iPhoneのワイヤレス接続に関するランダムな問題を修正します。

これは私の最初のbashスクリプトの1つであり、この問題の原因を特定する方法を理解しようとして迷っています。

スクリプトは実行され、想定どおりにサービスが再起動されます。ただし、eval: line 1: $: not foundスクリプトの実行時にターミナルで次のエラーが発生します。


#!/bin/sh

##
# Restarts the nas daemon on the specified interval
##

# Restart interval, in seconds
#T=3600 # hourly
T=60 # for testing

# Log file
if [ $# -ne 0 ]; then
        log=$1
else
        log=/tmp/restart.log
fi

while [ true ]; do
        # Wait
        sleep $T

        # Find commands to relaunch all nas daemons
        nascmd=`ps ww | grep nas | awk '{if($5!="grep"){$1=$2=$3=$4=""; print $0";"}}'`
        echo [`date`] Existing pid: `ps | grep nas | awk '{ORS=",";if($5!="grep"){print $1}}'` >> $log

        # Restart nas with original arguments
        killall -HUP nas
        echo [`date`] Running command: $nascmd >> $log

        # Strip special characters prior to eval
        safecmd=`echo $nascmd | sed 's/\$/\\$/g'`
        eval $safecmd

        echo [`date`] Finished, new pid: `ps | grep nas | awk '{ORS=",";if($5!="grep"){print $1}}'` >> $log
done

evalちょうど1行の長さなので、エラーの原因だと思います。私はそれが何で$あり、なぜそれが見つからないのかを理解しようとしています。ドル記号が存在する場合は、すべてエスケープします。あるべきではありませんが、誰かが暗号化パスフレーズをドル記号の付いたものに変更した場合に備えて、私は安全でした。

4

1 に答える 1

1

の定義に問題がありますsafecmd

safecmd=`echo $nascmd | sed 's/\$/\\$/g'`

シェルがバッククォートされた文字列を解析するとき、バックスラッシュを次の文字をクォートするものとして扱います。したがって、出力が格納されるコマンドsafecmd

echo $nascmd | sed 's/$/\$/g'

sedに渡される正規表現は$、つまり、行の終わりで一致します。置換テキストは\$です。だからsafecmd、次のようなものになってしまいます

/usr/bin/nas --whatever\$

実行中のプロセスがない場合は、nascmd空になります。つまり、名前が。のコマンドを実行します。safecmd\$$

バッククォート内の引用に関するルールは複雑で、シェルごとに異なります(BusyBox hush、dash、bash、ksh93、およびpdkshはすべて、この特定のケースを同じように扱います)。修正は簡単です。バッククォートを使用せず、$(…)代わりに使用してください。ドル括弧は、非常に古いUnixサーバーを維持しない限り遭遇する可能性が低い古いBourneシェルを除くすべてのシェルでサポートされています。$(…)直感的な構文があります。引用符を追加せずにコマンドを記述するだけです。

あなたが割り当てる方法に別の問題があるかもしれませnascmdん、私はそれほど考えていません。nasプロセスが実行されていない場合、スクリプトはケースをサポートしていません。


スクリプトには多くの引用符の問題があるため、引数nasに特殊文字が含まれていない場合、同じコマンドを実行することはできません。兆候を引用するあなたの失敗した試み$は海の低下です。まず、変数の置換とコマンドの置換を常に二重引用符で囲みます"$foo""$(foo)"eval次に、シェルコマンドではなく、コマンド名とその引数の間にスペース文字を入れて呼び出します。どのスペースが引数の一部であり、どのスペースが引数の区切り文字であるかを判断する方法がないため、元のコマンドを確実に復元することはできません。

/proc/$pid/cmdline引数はコマンド内では発生しないヌルバイトで区切られているため、コマンドラインをから確実に回復できます。ただし、シェルはnullバイトを処理できないため、シェルスクリプトでこれを行うのは面倒です。引数内で改行が発生しないと仮定すると、実行するのはそれほど難しくありません。

nas_pid=$(pidof nas)
# <insert a check that $nas_pid contains exactly one PID here>
set -f
IFS='
'
set -- $(</proc/$nas_pid/cmdline tr \\0 \\n)
set +f
# Kill the old NAS pid
kill $nas_pid
# Start a new instance
"$@"

しかし、これはすべて無意味に思えます。どこかでnas、デバイスの起動時に開始するものが必要です。そのことを見つけて、サービスを再起動するときにもう一度実行します。

PSこれはbashスクリプトではなく、shスクリプトです。いくつかのshの実装があります。DD-WRTは、ほとんどの組み込みデバイスと同様に、BusyBoxのshを使用します。Bashは別のsh実装です。BusyBoxshにbashのすべての機能が含まれているわけではありません。

于 2013-01-03T21:06:38.700 に答える