web-dev-qa-db-ja.com

bashスクリプト関数内からsshにスクリプト文字列を渡す-変数評価の問題

以下のように、ssh経由でリモートスクリプトを実行するbashスクリプトを作成しようとしています。

#!/bin/bash
logfilepattern="*drupal*.gz php*.gz error*.gz"
function getlogcounts {
    echo "in getlogcounts"
    echo  $1
    echo  $2

    ssh $1 bash -c '  \  #script string starts here
    cd $2
    for file in `ls $logfilepattern`
    do
     ls -l $file
    done
'
}

getlogcounts [email protected] "/var/log/mylogs"

$1$2は関数に問題がないように見えますが、スクリプト文字列内では値がないようです...したがって、cd $2cdと評価され、ls $logfilepatternlsと評価されます。

3
Max

これは簡略化されたバージョンであり、さらに重要なことに、引用符が修正されています(変数を二重引用符で囲み、必要に応じて非変数テキストを一重引用符で囲みます)

#!/bin/bash
logfilepattern='*drupal*.gz php*.gz error*.gz'

function getlogcounts {
    echo in getlogcounts
    echo "$1"
    echo "$2"

    ssh "$1" "cd $2 ; ls -l $logfilepattern"
}

getlogcounts [email protected] /var/log/mylogs
2
cas

リモートマシンでスクリプトを実行しています。そのスクリプトにはたまたま_$2_と_$logfilepattern_が含まれています(一重引用符で囲まれたテキストは文字通りsshコマンドの引数として渡されるため、反対側で実行するコードスニペットです) 。これらは、削除側で実行されているシェルの変数を参照します。

実際には、リモート側で2つのシェルを実行していますが、あまり役に立ちません。 SSHは常にシェルを呼び出します。複数の引数を渡すことができますが、それらは間にスペースを入れて結合されています。リモート側のシェルが実行されます

_bash -c   \  #script string starts here
cd $2
…
_

最初の行は、_-c_と単一のスペースの2つの引数を使用してbashを呼び出します。これにより、シェルが呼び出されて何も実行されません。リモートコマンドの残りの部分は、SSHによって呼び出されたシェルで実行されます。

あなたがやろうとしていることをする簡単な方法は

_ssh "$1" "cd $2 && ls -l $logfilepattern"
_

ノート:

  • lsの出力の解析は無意味です :_for file in `ls *`_は、lsの出力の解析が中断することを除いて、_for file in *_の複雑な記述方法です。ファイル名に特殊文字が含まれている場合。
  • _$2_と_$logfilepattern_はどちらも、リモート側で実行されるスクリプトの一部として解析されます。 $(touch foo)のようなものが含まれている場合、そのコードが実行されます。この特定のユースケースでは、それは必ずしも問題ではありませんが、注意する必要があるものです。
  • cdの後に_&&_を使用すると、sshコマンドが失敗した場合に、cdコマンドがすぐに失敗ステータスで返されるようになります。

変更せずにコンテンツをSSH経由でリモートシェルに渡す場合、渡すものはすべてリモート側で展開されるため、コマンドラインだけを使用するのは問題があります。よく機能するトリックの1つは(常にではありませんが、クライアントとサーバーの両方でSSHの構成に依存します)、名前が_LC__で始まる変数を使用することです。これらはロケールに関する情報であると見なされ、多くの場合送信されます。

_LC_DIRECTORY=$2 LC_LOGFILEPATTERN=$logfilepattern ssh "$1" 'cd "$LC_DIRECTORY" && ls -l $LC_LOGFILEPATTERN'
_

引用に注意してください:

  • リモートマシンで実行するコマンドは_cd "$LC_DIRECTORY" && ls -l $LC_LOGFILEPATTERN_です。そのリテラルテキストを引数としてsshコマンドに渡すのは一重引用符です。
  • _$LC_DIRECTORY_は、リモート側で実行されるスクリプト内で二重引用符で囲まれているため、変数の内容に対してcdコマンドが呼び出されます。
  • _LC_LOGFILEPATTERN_は、空白で区切られたワイルドカードパターンのリストであるため、二重引用符で囲まれていません。これは、引用符で囲まれていない変数展開の処理方法です。

変数を送信できない場合は、値に特殊文字が含まれている変数を保護するために、引用符のレイヤーを追加する必要があります。

_q_directory=$(printf %sa "$2" | sed s/\'/\'\\\'\'/g)
q_directory="'${directory%a}'"
q_logfilepattern=$(printf %sa "$logfilepattern" | sed s/\'/\'\\\'\'/g)
q_logfilepattern="'${q_logfilepattern%a}'"
ssh "$1" "logfilepattern=$q_logfilepattern; cd $q_logfilepattern && ls \$logfilepattern"
_