「変数のコマンド」での引用/エスケープ/拡張の問題

「変数のコマンド」での引用/エスケープ/拡張の問題

Bashスクリプトで次のコマンドを実行したいと思います。

freebcp <authentication and other parameters> -t "," -r "\r\n"

このコマンドは、コマンドラインから直接実行すると期待どおりに機能します。しかし、bashスクリプトの変数に入れると、次のエラーが返されます。

Msg 20104, Level 3
Unexpected EOF encountered in bcp datafile

Msg 20074, Level 11
Attempt to bulk copy an oversized row to the server

bcp copy in failed

コマンドが変数に配置され、二重引用符がエスケープされる場合:

cmd="freebcp ${db_name}.dbo.${table_name} in ${p_file} -S ${server_name} -U ${username} -P ${password} -t \",\" -r \"\r\n\" -c"
`$cmd`

ノート:以下をスクリプトに入れると、期待どおりに動作します。

`freebcp ${db_name}.dbo.${table_name} in ${p_file} -S ${server_name} -U ${username} -P ${password} -t "," -r "\r\n" -c`

したがって、引用/エスケープ/拡張の問題があることを知っていますが、解決策がわかりません。

ノート2: 一重引用符 -t -r パラメーターも効果がありません。

ベストアンサー1

短い答え:参照BashFAQ#50:コマンドを変数に入れようとしていますが、複雑な場合は常に失敗します!

長い答え:シェルはコマンドラインの解析中に、特に引用符を処理してエスケープした後に変数拡張を実行します。したがって、変数に引用符とエスケープを追加することは、コマンドラインに直接引用符とエスケープを追加することとは異なる効果があります。

あなたの答えの解決策(エスケープ文字を2倍にすること)はほとんどの場合うまくいきますが、うまくいくと思う理由でうまくいきません。注文する:

cmd="freebcp ... -t "," -r "\\r\\n" -c"

二重引用符で囲まれた文字列freebcp ... -t、引用符のない文字列、二重引用符,付きの文字列-r、引用符のない文字列 '\\r\\n'が続きます(引用符がないという事実は二重エスケープの理由が必要です)その後に二重引用符で囲まれた文字列 "-c"が続きます。文字列の一部として使用したい二重引用符は、文字列の一部として扱われず、区切り文字として扱われ、文字列の他の部分が解析される方法を変更します(実際には、意図した効果とほぼ反対)。その理由は、元のコマンドでは二重引用符が実際に多くの操作を実行しないため、その役割を変更しても多くの操作を実行しないためです。実際、実際に何が起こっているのかについて誤解の余地が少ないので、内部アイテムのみを削除することをお勧めします。これはうまくいきますが、壊れやすいです。動作する唯一の理由は、実際に二重引用符が必要ないためです。そして、状況(たとえば、スペースを含むパスワードまたはファイル名)が発生した場合に引用が必要な場合、そうであれば、あなたは問題になります。

より良いオプションがいくつかあります。

  • コマンドを変数にまったく保存せず、直接実行してください。コマンドを保存するのは難しいので(あなたが見つけたように)、本当に必要でなければしないでください。

  • 機能を使用してください。同じコマンドを繰り返し実行するなどの操作を実行する場合は、それを関数として定義して使用してください。

    loaddb() {
        freebcp "${db_name}.dbo.${table_name}" in "${p_file}" -S "${server_name}" -U "${username}" -P "${password}" -t \",\" -r \"\r\n\" -c"
    }
    loaddb
    

    二重引用符を使用したことに注意してください。みんな変数参照数 - スペース、ワイルドカード、またはシェルが認識しないその他の項目が含まれている場合、通常は良いスクリプト衛生です。する変数の値を解析します。

  • 通常の変数の代わりに配列を使用してください。これを正しく行うと、各コマンド引数が配列の別々の要素として格納されます。その後、イディオムを使用して"${arrayname[@]}"全体をインポートできます。

    cmdarray=(freebcp "${db_name}.dbo.${table_name}" in "${p_file}" -S "${server_name}" -U "${username}" -P "${password}" -t \",\" -r \"\r\n\" -c")
    "${cmdarray[@]}"
    

    ここでも二重引用符を使用することに注意してください。ここでは、配列要素が配列に格納されている値の一部ではなく、正しく定義されていることを確認するために使用されます。また、すべてのシェルで配列を使用できるわけではありません。 bash、zsh、または同様のものを使用していることを確認してください。

いくつかの最終注意事項:使用時:

`somecommand`

バックティックは思うように動作せず、実際に潜在的に危険です。彼らがすることはコマンドを実行し、その出力をキャプチャし、その出力を次のように実行するだけです。その他注文する。コマンドが何も印刷しないかどうかは問題ではありませんが、何かを印刷する場合、出力は有効なコマンドではない可能性があります。バックティックを失います。

最後に、パスワードをコマンド引数として渡すのは安全ではありません。プロセステーブル(つまり、コマンドがps表示できる場所)にコマンド引数を公開し、公開場所にパスワードを公開するのは非常に悪い考えです。freebcpこれ以外には特に代替がないようですが、パッチを見つけましたこれにより、標準入力からパスワードが読み取られます(echo "$password" | freebcp -P - ...--noteこれはechoシェル組み込みであるため、その引数はプロセステーブルに表示されません)。私はパッチの正確性、セキュリティなどについて何の主張もしません(特にパッチがかなり古いため)。しかし、私があなたなら確認してみます。

おすすめ記事