Bash:複数行を1行コマンドで解析

Bash:複数行を1行コマンドで解析

他のBashスクリプトに特定のコマンドラインが含まれていることを確認するBashスクリプトを作成する必要があります。 Bashを使用すると、コマンドラインを複数行に分割できるため、スクリプトは実際のパターンマッチングを実行する前にその行をマージできる必要がありました。

Bashスクリプトのすべての複数行コマンドを1行コマンドに解析する方法は?

はい

特定のスクリプトに含まれていることを確認したいと思います。エルエスコマンド - 次を含む場合エルエスコマンドでどのパラメータが渡されるかを知りたいです。エルエス注文する。この質問に答えるために使用できますsed。ただし、最初にすべての複数行コマンドをマージする必要があります。

入力する:

# My comment \
ls \
-a \
-l

出力:

# My comment \
ls -a -l

誤った出力の例:

# My comment ls -a -l

答え1

私はシェルショックの直前にStackOverflowに関する質問に回答しました。Bashスクリプトからコメントを削除するため。私の答えは、スクリプトファイルの内容をラップし、tmp_() { ... }それdeclare -f tmp_を使用して関数をきれいに印刷する簡単な方法を使用します。きれいな印刷出力にはコメントがなく、バックスラッシュの改行で続く行は単一行に解析されます。 (内部逆引用符コマンドの置換は除く)

他のフォーマットも完了しました。たとえば、複合コマンドは複数行に分けられます。そして、ライン連続のいくつかのフォームは再フォーマットされません。たとえば、パイプ記号で終わる行は変更されません。ただし、この質問のユースケースを満たす必要があります。 (下記のサンプル出力を参照してください。)

もちろん、関数定義を評価する必要があります。これは、美しく印刷されたスクリプトに注入攻撃を含めることができることを意味します。私が提案するコードでは、関数定義は関数をエクスポートして子プロセスと共有できるbash関数によって評価されます。この小さなハックを書くとき、私はメカニズムが呼び出しより安全であると信じていましたが、私は思っていたので間違ってevalいました。

関数定義を取得するためにbashが使用するコードは、shellshock以来非常に改善されました。これは、少なくともいくつかの注入攻撃を防止するが、明らかにプロセスが完全に安全であるという保証はない。

分析中のスクリプトを実行している場合は、このプロセスを使用して脆弱性を増やすことはできません。攻撃者は潜在的にバイパス可能なセキュリティチェックを使用してコードをインポートし、隠すことなくスクリプトに直接危険なコードを挿入する可能性があります。攻撃。

それにもかかわらず、この小さなプログラムであるかランダムなスクリプトを実行する必要があるか、セキュリティ上の問題を慎重に検討する必要があります。

以下は、shellshockパッチが適用されたbashで動作する(以前のbashバージョンでは機能しなかった)きれいなプリンタバージョンです。

env "BASH_FUNC_tmp_%%=() {
$(<script_name)
}" bash -c 'declare -f tmp_' | tail -n+2

script_name2行目をスクリプトを含むファイル名に置き換えます。コマンドを調整すると、tailラッパー関数名は削除されますが、スクリプト本体の周囲の中かっこは削除されません。

元のバージョンはbashのシェルショック以前のバージョン用で、参照されているSOの回答にあります。


サンプル。

提供された入力のテストスティーブン・チャジェラス:

{ 
    echo \\;
    echo a#b;
    echo 'foo\
bar';
    cat  <<EOF
thisis joined
this 'aswell'
$(ls -l)
EOF

    cat  <<'EOF'
this is\
not joined
EOF

    echo "$(ls -l)";
    echo `ls \\
-l`
}

これはStéphaneが提案した出力とは異なります。

  • 行はインデントされており、多くの行がセミコロンで終わります。多くの行でスペースが追加および/または削除されました。
  • cat << E\OFcat <<'EOF'同じ意味を持つに変更されました。
  • 最後にバックティックコマンドを置き換えるネストされた連続行は変更されませんでした。 (コマンド置換から連続した行は$(...)削除されます。)

答え2

今これはより多くの場合に動作し、期待どおりに動作することを確認してください。

sed ':loop /^[^#].*[^\\]\\$/N; s/\\\n//; t loop' input

デフォルトでは各行を印刷します。行の末尾にバックスラッシュ($)があり(特殊文字であるためエスケープされている)、行の先頭にハッシュマークがない場合は、修正された次の行と比較します。 line(N)検索を連結し、バックスラッシュ(再エスケープ)と改行文字を置き換えます。何もない。検索と置換が一部の操作を実行している場合は、[ループ]タブに戻って検索を再実行してください。

入力する:

# My comment \
ls \
-al

# leading comment
echo some \
long \
text
# trailing comment

ls -al

出力:

# My comment \
ls -al

# leading comment
echo some long text
# trailing comment

ls -al

答え3

これは実際には答えではなく、一般的なケースで動作する解決策のために考慮すべき事項に関する注意事項です。

#! /bin/sh -
echo \\
echo a#\
b
echo 'foo\
bar'
cat << EOF
this\
is joined
this 'as\
well'
$(ls \
-l)
EOF
cat << E\OF
this is\
not joined
EOF
echo "$(ls \
-l)"
echo `ls \\
-l`

質問の意図の私の理解は、次のように変換する必要があるということです。

#! /bin/sh -
echo \\
echo a#b
echo 'foo\
bar'
cat << EOF
thisis joined
this 'aswell'
$(ls -l)
EOF
cat << E\OF
this is\
not joined
EOF
echo "$(ls -l)"
echo `ls -l`

答え4

改行を空白に置き換えるには:

tr '\n' ' '

これにより、コードにいくつかのバックスラッシュが残る可能性があるため、バックスラッシュの前の改行文字を空白に置き換える必要があります。

 perl -pe 's/\\\n/ /'

これはまだ間違っている可能性がありますが(ここではドキュメント、説明などがあります)、100%正確にするには完全なシェルパーサーを作成する必要があります。

関連情報