1

GNU sedが、同じ式で複数の式が使用されている場合とは異なる方法で、別のsedインスタンスへのパイプ出力による置換を処理することがあるのはなぜですか?

具体的には、msys / mingwセッションの場合、/ etc / profileスクリプトに、環境変数PATHの順序を「再配置」し、重複するエントリを削除する一連の操作があります。

通常、sedは入力の各行を個別に処理しますが(したがって、入力ストリームの'\ n'を簡単に置き換えることはできませんが、このsedステートメントは':'を'\ n'に置き換えるため、 1行のような入力ストリーム全体('\ n'文字が含まれます)。この動作は、sedの同じインスタンス内のすべてのsed式に当てはまります(基本的に、出力を別のプログラムにリダイレクトまたはパイプするまで)。

必須の仕様は次のとおりです。

    Windows 7 Professional Service Pack 1
    HP Pavilion dv7-6b78us
    16 GB DDR3 RAM
    MinGW-w64 (x86_64-w64-mingw32-gcc-4.7.1.2-release-win64-rubenvb) mounted on /mingw/
    MSYS (20111123) mounted on / and on /usr/
    $ uname -a="MINGW32_NT-6.1 CHRIV-L09 1.0.17(0.48/3/2) 2011-04-24 23:39 i686 Msys"
    $ which sed="/bin/sed.exe" (it's part of MSYS)
    $ sed --version="GNU sed version 4.2.1"

操作前のPATHの内容は次のとおりです。

    PATH='.:/usr/local/bin:/mingw/bin:/bin:/c/PHP:/c/Program Files (x86)/HP SimplePass 2011/x64:/c/Program Files (x86)/HP SimplePass 2011:/c/Windows/system32:/c/Windows:/c/Windows/System32/Wbem:/c/Windows/System32/WindowsPowerShell/v1.0:/c/si:/c/android-sdk:/c/android-sdk/tools:/c/android-sdk/platform-tools:/c/Program Files (x86)/WinMerge:/c/ntp/bin:/c/GnuWin32/bin:/c/Program Files/MySQL/MySQL Server5.5/bin:/c/Program Files (x86)/WinSCP:/c/Program Files (x86)/Overlook Fing 2.1/bin:/c/Program Files/7-zip:.:/c/Program Files/TortoiseGit/bin:/c/Program Files (x86)/Git/bin:/c/VS10/VC/bin/x86_amd64:/c/VS10/VC/bin/amd64:/c/VS10/VC/bin'

これは/etc/ profileの抜粋です(ここでPATH操作を開始しました):

    set | grep --color=never ^PATH= | sed -e "s#^PATH=##" -e "s#'##g" \
    -e "s/:/\n/g" -e "s#\n\(/[^\n]*tortoisegit[^\n]*\)#\nZ95-\1#ig" \
    -e "s#\n\(/[a-z]/win\)#\nZ90-\1#ig" -e "s#\n\(/[a-z]/p\)#\nZ70-\1#ig" \
    -e "s#\.\n#A10-.\n#g" -e "s#\n\(/usr/local/bin\)#\nA15-\1#ig" \
    -e "s#\n\(/bin\)#\nA20-\1#ig" -e "s#\n\(/mingw/bin\)#\nA25-\1#ig" \
    -e "s#\n\(/[a-z]/vs10/vc/bin\)#\nA40-\1#ig"

その行の最後のsed式は、基本的に「/ c / VS10 / VC / bin」で始まる行を探し、次のように「A40-」を前に付けます。

    ...
    /c/si
    A40-/c/VS10/VC/bin
    A40-/c/VS10/VC/bin/amd64
    A40-/c/VS10/VC/bin/x86_amd64
    /c/GnuWin32/bin
    ...

sed式が柔軟である(パス構造が変更される)のが好きですが、amd64またはx86_amd64で終わる行と一致させたくありません(これらには別の文字列が付加されます)。したがって、最後の式を次のように変更します。

    -e "s#\n\(/[a-z]/vs10/vc/bin\)\n#\nA40-\1\n#ig"

これは機能します:

    ...
    /c/si
    A40-/c/VS10/VC/bin
    /c/VS10/VC/bin/amd64
    /c/VS10/VC/bin/x86_amd64
    /c/GnuWin32/bin
    ...

次に、(擬似コード "/ x /.../ bin"に一致する任意の「行」に一致させるために)最後の式を次のように変更します。

    -e "s#\n\(/[a-z]/.*/bin\)\n#\nA40-\1\n#ig"

生成するもの:

    ...
    /c/si
    /c/VS10/VC/bin
    /c/VS10/VC/bin/amd64
    /c/VS10/VC/bin/x86_amd64
    /c/GnuWin32/bin
    ...

??? --sedは、行の途中でどの文字('。')にも一致しませんでした('*') ???

しかし、出力をsedの別のインスタンスにパイプする場合(およびsedが各「行」を個別に処理することを補正する場合)、次のようになります。

    | sed -e "s#^\(/[a-z]/.*/bin\)$#A40-\1#ig"

私は得る:

    sed: -e expression #1, char 30: unterminated `s' command

??? それはどのように終わっていないのですか? sの後に3つすべての「#」文字があり、3番目の「#」の後に修飾子「i」と「g」があり、式全体が二重引用符('"')になっています。また、エスケープはありません( '\')区切り文字の直前であり、区切り文字は検索または置換の一部ではありません。'〜'のように'#'とは異なる区切り文字を試してみましょう。

私が使用するもの:| sed -e "s〜^(/ [az]/.*/bin)$~A40-\1~ig"

そして、私は得る:

    ...
    /c/si
    A40-/c/VS10/VC/bin
    /c/VS10/VC/bin/amd64
    /c/VS10/VC/bin/x86_amd64
    A40-/c/GnuWin32/bin
    ...

そして、それは正しいです!私が変更したのは、「#」から「〜」へのデリメータだけで、それは機能しました???

sedが私にとって説明のつかない結果を生み出したのはこれが初めてではありません。

なぜ、ああ、なぜ、sedは同じインスタンスの式の構文と一致しないのに、sedの別のインスタンスにパイプされたときに一致するのですか?そして、なぜ、ああ、なぜ、これを行うときに別のデリメータを使用する必要があるのですか(「終了していない」コマンドを取得しないために)?

そして私が尋ねている本当の理由:これはsedのバグですか、それとも私が理解していない正しい動作ですか(もしそうなら、誰かがこの動作が正しい理由を説明できますか?) 私はそれが間違っているのか、それとも別の/より良いツールが必要なのか(または両方、相互に排他的である必要はありません)を知りたいです。

誰かがこの動作が正しい理由を証明できる場合、またはそれがバグである理由を証明できる場合は、応答を回答としてマークします。 他のツールやsedのさまざまな使用方法に関するアドバイスは喜んで受け入れますが、それらは質問に答えません。

sedは説明のつかない結果で時間がかかりすぎるため、他のテキストプロセッサ(awk、trなど)で改善する必要があります。

PSこれは私のPATH操作の完全なロジックではありません。完全なロジックは、すべての行に「A00-」から「Z99-」までの値を追加し、出力を「sort -u -f」にパイプしてsedに戻し、各行の同じプレフィックスを削除して、行('\ n')をコロン(':')に戻します。次に、「export PATH ='」が1行に追加され、「'」が追加されます。次に、その出力は一時ファイルにリダイレクトされます。次に、その一時ファイルがソースされます。そして最後に、その一時ファイルが削除されます。

/ etc / profileスクリプトは、ソートの前後のPATHの内容も表示します(パスを台無しにした場合)。

PPSこれを行うにはもっと良い方法があると確信しています。それはいくつかの非常に単純なsed操作として始まり、ここに表示されるモンスターに成長しました。より良い方法があるとしても、sedがなぜこれらの結果を私に与えているのかを知る必要があります。

4

1 に答える 1

3
sed -e "s#^\(/[a-z]/.*/bin\)$#A40-\1#ig"

シェルが「$#A」を展開しようとしているため、は終了していません。これを避けるために、式を一重引用符で囲んでください。

表現

-e "s#\n\(/[a-z]/.*/bin\)\n#\nA40-\1\n#ig"

.複数行の式の改行と一致するため、失敗するか、期待どおりに機能しません。出力全体を確認してください。これA40-は最初の段階です。に変更します

-e "s#\n\(/[a-z]/[^\n]*/bin\)\n#\nA40-\1\n#ig"

そしてそれはあなたが期待するものよりも多いかもしれません。これは、複数行の変更に関するほとんどの問題に当てはまる可能性があります。

また、ステートメントを1行に1つずつスタンドアロンファイルに入れて、sedをで呼び出すこともできますsed -f editscript。これのメンテナンスが少し簡単になるかもしれません。

于 2012-09-19T19:03:28.527 に答える