作成中のWindowsバッチファイルはありますが、大きな複雑な文字列をエコーする必要があるため、両端に二重引用符を付ける必要があります。問題は、私が書いているファイルに引用符がエコーされていることです。どのようにしてそのような文字列をエコーし、引用符を取り除くのですか?
更新:
過去2日間、この作業に費やし、最終的に一緒に何かを整理することができました。リチャードの答えは引用符を取り除いたが、ECHOをサブルーチンに入れて直接文字列を出力した場合でも、Windowsは文字列の文字にハングアップしたままだった。リチャードの質問に答えるので、リチャードの答えを受け入れます。
私は最終的にGregのsedソリューションを使用することになりましたが、sed/windowsのバグ/機能のために修正する必要がありました(ドキュメントが付属していないのは役に立ちませんでした)。 Windowsでsedを使用するにはいくつかの注意事項があります。一重引用符の代わりに二重引用符を使用する必要があります。文字列内の二重引用符を直接エスケープすることはできません。また、誰かがsedに入力をパイプすると、文字列にパイプがあるというバグがあることを指摘しました(最終的な解決策でこれを確認できませんでした。文字列の途中にすべての引用符を入れずに、すべての引用符を削除するだけの方法エンドクォートを単独で削除することはできませんでした。)すべての助けをありがとう。
Callコマンドにはこの機能が組み込まれています。callのヘルプを引用するには:
Substitution of batch parameters (%n) has been enhanced. You can
now use the following optional syntax:
%~1 - expands %1 removing any surrounding quotes (")
基本的な例を次に示します。
@echo off
setlocal
set mystring="this is some quoted text"
echo mystring=%mystring%
call :dequote %mystring%
echo ret=%ret%
endlocal
goto :eof
:dequote
setlocal
rem The tilde in the next line is the really important bit.
set thestring=%~1
endlocal&set ret=%thestring%
goto :eof
出力:
C:\>dequote
mystring="this is some quoted text"
ret=this is some quoted text
「環境変数トンネリング」手法(endlocal&set ret =%thestring%)をティムヒル、 'Windows NT Shell Scripting' に任せるべきです。これは私がこれまでに発見した唯一の本で、あらゆる深さのバッチファイルを扱っています。
すべてのx
をy
に置き換える%var:x=y%
構文を使用できます。
この例をご覧ください:
set I="Text in quotes"
rem next line replaces " with blanks
set J=%I:"=%
echo original %I%
rem next line replaces the string 'in' with the string 'without'
echo stripped %J:in=without%
次の方法を使用して、引用符なしで文字列を印刷できます。
echo|set /p="<h1>Hello</h1>"
セット変数からすべての引用符を削除するには、変数を安全に展開して処理するための遅延変数展開が必要です。パーセント記号(つまり、%VAR%
および%1
)を使用した展開は本質的に安全ではありません(コマンドインジェクションに対して脆弱です; 詳細はこちらを参照してください )。
SETLOCAL EnableDelayedExpansion
SET VAR=A ^"quoted^" text.
REM This strips all quotes from VAR:
ECHO !VAR:^"=!
REM Really that's it.
テキストファイルまたはコマンド出力から引用符を削除するには、遅延拡張では、テキストドキュメント内の!VAR!
などの文字列が(%%i
のFOR /F
拡張内で拡張されるため、事態は複雑になります。 )すべきでないとき。 (これは別の脆弱性、つまり情報漏えいであり、他の場所では文書化されていません。)
ドキュメントを安全に解析するには、遅延拡張が有効な環境と無効な環境を切り替える必要があります。
REM Suppose we fetch the text from text.txt
SETLOCAL DisableDelayedExpansion
REM The FOR options here employs a trick to disable both "delims"
REM characters (i.e. field separators) and "eol" character (i.e. comment
REM character).
FOR /F delims^=^ eol^= %%L IN (text.txt) DO (
REM This expansion is safe because cmd.exe expands %%L after quotes
REM parsing as long as DelayedExpansion is Disabled. Even when %%L
REM can contain quotes, carets and exclamation marks.
SET "line=%%L"
CALL :strip_quotes
REM Print out the result. (We can't use !line! here without delayed
REM expansion, so do so in a subroutine.)
CALL :print_line
)
ENDLOCAL
GOTO :EOF
REM Reads !line! variable and strips quotes from it.
:strip_quotes
SETLOCAL EnableDelayedExpansion
SET line=!line:^"=!
REM Make the variable out of SETLOCAL
REM I'm expecting you know how this works:
REM (You may use ampersand instead:
REM `ENDLOCAL & SET "line=%line%"`
REM I just present another way that works.)
(
ENDLOCAL
SET "line=%line%"
)
GOTO :EOF
:print_line
SETLOCAL EnableDelayedExpansion
ECHO !line!
ENDLOCAL
GOTO :EOF
上のコードのdelims^=^ eol^=
にはおそらく説明が必要です。これにより、「デリム」文字(つまりフィールド区切り文字)と「eol」文字(つまりコメント文字)の両方が事実上無効になります。これがないと、「delims」はデフォルトでタブとスペースになり、「eol」はデフォルトでセミコロンになります。
eol=
トークンは、等号の後の次の文字を常に読み取ります。これを無効にするには、このトークンをオプション文字列の最後に配置する必要があります。これにより、「eol」に文字を使用できなくなり、事実上無効になります。オプション文字列が引用符で囲まれている場合、引用符( ")を" eol "として使用する可能性があるため、オプション文字列を引用符で囲まないでください。delims=
オプションは、オプション文字列の最後のオプションではない場合、スペースで終了します。 (「デリム」にスペースを含めるには、FOR /F
オプションの最後のオプションである必要があります。)delims=
の後にスペースが続き、別のオプションが「デリム」を無効にします。私はそれが実際には著者のためではないことを知っていますが、引用符なしでファイルにテキストを送信する必要がある場合-以下の解決策が私のために機能します。 echoコマンドで引用符を使用する必要はありません。コマンド全体を角かっこで囲むだけです。
(
echo first very long line
echo second very long line with %lots% %of% %values%
) >"%filename%"
これは、"C:\Program Files\somefile.txt"
やC:\Program Files\somefile.txt
などのケースを保持しながら、Height=5'6"
をSymbols="!@#
に変換します
:DeQuote
SET _DeQuoteVar=%1
CALL SET _DeQuoteString=%%!_DeQuoteVar!%%
IF [!_DeQuoteString:~0^,1!]==[^"] (
IF [!_DeQuoteString:~-1!]==[^"] (
SET _DeQuoteString=!_DeQuoteString:~1,-1!
) ELSE (GOTO :EOF)
) ELSE (GOTO :EOF)
SET !_DeQuoteVar!=!_DeQuoteString!
SET _DeQuoteVar=
SET _DeQuoteString=
GOTO :EOF
例
SetLocal EnableDelayedExpansion
set _MyVariable = "C:\Program Files\ss64\"
CALL :dequote _MyVariable
echo %_MyVariable%
上記の回答(:DeQuoteで始まる)は、遅延環境変数の展開がオンに設定されていることを前提としています。 cmd /?から:
遅延環境変数の展開はデフォルトでは有効になっていません。/V:ONまたは/ V:OFFスイッチを使用して、CMD.EXEの特定の呼び出しに対して遅延環境変数の展開を有効または無効にすることができます。 REGEDT32.EXEを使用してレジストリで次のREG_DWORD値のいずれかまたは両方を設定することにより、マシンおよび/またはユーザーログオンセッションでのCMD.EXEのすべての呼び出しの完了を有効または無効にできます。
HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\DelayedExpansion
and/or
HKEY_CURRENT_USER\Software\Microsoft\Command Processor\DelayedExpansion
0x1または0x0のいずれかに。ユーザー固有の設定は、マシンの設定よりも優先されます。コマンドラインスイッチは、レジストリ設定よりも優先されます。
遅延環境変数の展開が有効になっている場合、感嘆符を使用して、実行時に環境変数の値を置き換えることができます。
次のバッチファイルは、一連のプログラムを開始しますが、各プログラムの後に遅延があります。
問題は、各プログラムのパラメーターを含むコマンドラインを渡すことです。これには、プログラム引数を引用符で囲む必要がありますが、引用符は呼び出しが行われたときに削除されます。これは、バッチファイル処理のいくつかの手法を示しています。
引用符で囲まれた引数がどのように渡されるかについては、ローカルサブルーチン:mystartを調べ、引用符は削除されます。
@echo off
rem http://www.Microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/if.mspx?mfr=true
rem Start programs with delay
rem Wait n seconds
rem n number retries to communicate with the IP address
rem 1000 milliseconds between the retries
rem 127.0.0.1 is the LocalHost
rem start /b (silent) /min (minimized) /belownormal (lower priority)
rem /normal provides a no-op switch to hold the place of argument 1
rem start /normal "Opinions" %SystemRoot%\Explorer.exe /e,d:\agar\jobs\opinion
rem ping 127.0.0.1 -n 8 -w 1000 > nul
rem Remove quotes in Batch
rem http://ss64.com/nt/syntax-dequote.html
rem String manipulation in Batch
rem http://www.dostips.com/DtTipsStringManipulation.php
rem ^ line continuation
rem
rem set p="One Two" p has the exact value "One Two" including the quotes
rem set p=%p:~1,-1% Removes the first and last characters
rem set p=%p:"=% Removes all double-quotes
rem set p=%p:cat=mouse% Replaces cat with mouse
rem ping 127.0.0.1 -n 12 -w 1000 > nul
rem 1 2 3 4
@echo on
call :mystart /b/min "Opinions" "%SystemRoot%\Explorer.exe /e,d:\agar\jobs\opinion" 8
@echo on
call :mystart /b/min "Notepad++" D:\Prog_D\Notepad++\notepad++.exe 14
@echo on
call :mystart /normal "Firefox" D:\Prog_D\Firefox\firefox.exe 20
@rem call :mystart /b/min "ProcessExplorer" D:\Prog_D\AntiVirus\SysInternals\procexp.exe 8
@echo on
call :mystart /b/min/belownormal "Outlook" D:\Prog_D\MSOffice\OFFICE11\Outlook.exe 2
@echo off
goto:eof
:mystart
@echo off
rem %3 is "program-path arguments" with the quotes. We remove the quotes
rem %4 is seconds to wait after starting that program
set p=%3
set p=%p:"=%
start %1 %2 %p%
ping 127.0.0.1 -n %4 -w 1000 > nul
goto:eof
FORコマンドを使用して周囲の引用符を削除するのが、これを実現する最も効率的な方法です。コンパクトなフォーム(例2)では、1ライナーです。
例1:5行の(コメント付き)ソリューション。
REM Set your string
SET STR=" <output file> (Optional) If specified this is the name of your edited file"
REM Echo your string into the FOR loop
FOR /F "usebackq tokens=*" %%A IN (`ECHO %STR%`) DO (
REM Use the "~" syntax modifier to strip the surrounding quotation marks
ECHO %%~A
)
例2:1ライナーの実世界の例。
SET STR=" <output file> (Optional) If specified this is the name of your edited file"
FOR /F "usebackq tokens=*" %%A IN (`ECHO %STR%`) DO @ECHO %%~A
内側のエコーがリダイレクト文字「<」と「>」を無視することは興味深いと思います。ECHO asdfsd>asdfasd
を実行すると、std outの代わりにファイルを書き出します。
お役に立てれば :)
編集:
私はそれについて考え、同じことを達成するためのさらに簡単な(そしてよりハッキングの少ない)方法があることに気付きました。次のように、拡張された変数の置換/拡張(HELP SET
を参照)を使用します。
SET STR=" <output file> (Optional) If specified this is the name of your edited file"
ECHO %STR:~1,-1%
これは、最初と最後の文字(引用符)を除くすべてを印刷します。 SETLOCAL ENABLEDELAYEDEXPANSION
も使用することをお勧めします。文字列内の引用符の位置を把握する必要がある場合は、FINDSTRを使用して文字#を取得できます。
DanielBudzyńskiの反応は素晴らしいです。出力に特殊文字が含まれる状況でも機能します。例えば:
C:\> for /f "usebackq tokens=2 delims=:" %i in (`%comspec%\..\ping -n 1 -w 200 10.200.1.1 ^| \
findstr /c:"TTL="`) do echo|set /p="%i"
bytes=32 time<1ms TTL=255
引用符なしで単純なエコーを実行しようとすると、変数の「<」が原因でエラーが発生します。
C:\> set "output=bytes=32 time<1ms TTL=255"
C:\> echo %output%
The system cannot find the file specified.
C:\> echo|set /p="%output%"
bytes=32 time<1ms TTL=255
ブルートフォース法:
echo "foo <3 bar" | sed -e 's/\(^"\|"$\)//g'
もちろん、これにはsed
の適切なWin32バージョンを見つける必要があります。
http://unxutils.sourceforge.net/ は、GNU sed、gawk、grep、wgetを含むユーティリティの束のネイティブwin32ポートです。これをコメントとして投稿するのに十分な担当者がいない!)