Windows コマンドライン ImageMagick パーセントエスケープ

Windows コマンドライン ImageMagick パーセントエスケープ

Windows 7 のコマンド ラインから ImageMagick を使用して、一度に多数の画像を変換しようとしています。-set オプション インターフェイスを使用したいのですが、予期しない結果が出ています。私が見ている奇妙な現象の簡単な例を以下に示します。

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world "%[filename:foo]_%[filename:bar].png"
example.jpg=>helloworld.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.125u 0:00.135

出力ファイル名からアンダースコア文字が不可解に削除されていることに注意してください。アンダースコアを保持するために、キャレット (^) を使用して 2 番目のパーセント記号をエスケープすることができます。

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world "%[filename:foo]_^%[filename:bar].png"
example.jpg=>hello_world.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.125u 0:00.126

何が起こっているのでしょうか? キャレットを挿入すると、なぜアンダースコアが保持されるのでしょうか? キャレット (^) は、試すべき正しいエスケープ文字なのでしょうか? この動作は、コマンド シェルの環境変数の拡張によるものでしょうか、それとも ImageMagick がパーセント エスケープを処理する方法に起因して発生する奇妙な動作なのでしょうか? いずれにしても、予測可能な動作を引き起こすために従うことができる、コマンド ライン引数でのパーセント記号の使用に関する一般原則はありますか?

私が実際にやりたいのは、上記のコマンドにさらに多くのプロパティを設定し、スペースやパーセント記号などの特殊文字を含むファイル名を処理できるバージョンですが、この非常に単純な例を理解できないのにそれを試すのは愚かなようです。最終的には、.NET プログラムからコマンドを実行する必要があります。

私は見てきましたhttp://www.robvanderwoude.com/escapechars.phpそしてコマンドラインでファイル名/フォルダ名の%をエスケープするしかし、そのアドバイスを自分の問題にどのように適用すればよいのか分かりませんでした。

アップデート

@dbenham と @Synetech からのコメントの質問に答えるために、さらにいくつかの例を追加します。

上記の後者の例のように、アンダースコアを 2 番目のパーセント記号の前からアンダースコアの前に移動すると、次の名前のファイルが生成されますhello^world.png

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world "%[filename:foo]^_%[filename:bar].png"
example.jpg=>hello^world.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.141u 0:00.132

helloworld.png周囲の引用符をキャレット エスケープすると、キャレットがどこに置かれていても、常に になります。

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world ^"^%[filename:foo]_^%[filename:bar].png^"
example.jpg=>helloworld.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.109u 0:00.115

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world ^"%[filename:foo]_^%[filename:bar].png^"
example.jpg=>helloworld.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.141u 0:00.143

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world ^"^%[filename:foo]_%[filename:bar].png^"
example.jpg=>helloworld.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.125u 0:00.126

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world ^"%[filename:foo]_%[filename:bar].png^"
example.jpg=>helloworld.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.125u 0:00.131

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world ^"%[filename:foo]^_%[filename:bar].png^"
example.jpg=>helloworld.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.125u 0:00.111

答え1

発生している奇妙な動作について、うまく説明できません。 cmd.exe のパーセント処理の結果のように思えますが、私が理解している cmd.exe の動作とは完全には一致しません。

バッチ ファイル内でパーセントをエスケープするのは簡単です。単に 2 倍にするだけです。ただし、コマンド ラインでは機能しません。技術的には、Windows cmd.exe コマンド ラインでパーセントをエスケープする方法はありません。

コマンドラインでは、次の 2 つの方法のいずれかでパーセントを展開できます。

1) 環境変数を展開します。

2 つのパーセントの間のテキストが環境変数の名前と一致する場合、パーセント構造は値に置き換えられます。

C:\test>set test=hello

C:\test>echo %test%
hello

パーセント間のテキストが環境変数名と一致しない場合は、元のテキスト全体が保持されます。

C:\test>echo %notDefined%
%notDefined%

Microsoft のドキュメントでは、先頭にキャレットを置くことでパーセントを回避できると記載されています。

C:\test>echo ^%test^%
%test%

しかし、これは実際にはパーセントをエスケープしているわけではありません。 という名前の変数を探しているのですtest^が、見つかりません。そのため、元のテキストはそのままです。その後、通常のエスケープ処理中にキャレットが削除されます。これは、名前にキャレットを含む変数を定義することで証明できます。

C:\test>set "test^=goodbye"

C:\test>echo ^%test^%
goodbye

最初のパーセントの前のキャレットは何も行いません。パーセント間のどこにでもキャレットを置くことができ、名前にキャレットを含む変数が存在しない限り、変数の展開を防ぐことができます。

C:\test>echo %te^st%
%test%

テキストが引用符で囲まれている場合は、引用符もエスケープしない限りキャレットは削除されません。

C:\test>echo "%te^st%"
"%te^st%"

C:\test>echo ^"te^st%^"
"%test%"

コロンがある場合は状況が少し複雑になります。コロンは、置換および部分文字列操作のための変数拡張で使用されます。変数名は、最初のパーセントとコロンの間のテキストです。

C:\test>echo %test:el=a%
halo

C:\test>echo %test:~1,2%
el

最初のパーセントとコロンの間のテキストが変数名と一致しない場合、またはコロンと最後のパーセントの間のテキストが有効な置換または部分文字列構造でない場合は、元のテキストが置換されずに保持されます。

C:\test>echo %test:1,2%
%test:1,2%

この最後の例はあなたの状況に最も似ています。最初のパーセントとコロンの間のテキストは変数と一致しておらず、コロンと最後のパーセントの間のテキストは置換または部分文字列構造ではありません。したがって、全体の構造は保存されるべきであり、アンダースコアを含むこのため、cmd.exe がアンダースコアを削除する方法がわかりません。しかし、2 番目のパーセントの前にキャレットを追加してもアンダースコアが保持されるという事実は、私には疑わしいです。代わりにキャレットをアンダースコアの前に移動した場合に何が起こるのかを本当に知りたいです。


2) FOR変数を展開する

あなたのケースでは問題ではありませんが、FOR ループが有効な場合はパーセントも問題を引き起こす可能性があります。この場合、キャレットを使用してパーセントを保護する方法はありません。キャレットは FOR 変数の展開前に削除されるためです。この例では、出力を としたいのです%A=1が、うまくいきません。

C:\test>for %A in (1 2) do @echo ^%^A=%A
1=1
2=2

パーセントを保護する最も便利な方法は、別のFOR変数を導入することです。

C:\test>for %C in (%) do @for %A in (1 2) do @echo %CA=%A
%A=1
%A=2

A別の変数に入れても同様に機能します

C:\test>for %C in (A) do @for %A in (1 2) do @echo %%C=%A
%A=1
%A=2

アップデート

アンダースコアが削除される場所をテストする良い方法の 1 つは、コマンドの前に ECHO を置くことです。元のコマンドがアンダースコア付きで保持されている場合は、変換ユーティリティが原因であることがわかります。

C:\>echo convert example.jpg -verbose -set filename:foo hello -set filename:bar world "%[filename:foo]_%[filename:bar].png"

私のマシンではアンダースコアは保持されます :-)


コマンドパーサーの動作についてさらに興味がある場合は、以下を参照してください。https://stackoverflow.com/a/4095133/1012053この回答は主にバッチ解析に関するものですが、ルールは非常に似ており、最後のセクションでは主な違いについて簡単に説明しています。

関連情報