10 Stimmen

Parsing von cmdline-Argumenten, die = enthalten

Nachdem ich viele Jahre lang Batchdateien verwendet habe, war ich überrascht, als ich feststellte, dass das Gleichheitszeichen "=" als Argumenttrennzeichen gilt.

Mit diesem Testskript:

echo arg1: %1
echo arg2: %2
echo arg3: %3

und eine Aufforderung:

test.bat a=b c

ist die Ausgabe:

arg1: a
arg2: b
arg3: c

Warum ist das so und wie kann man es vermeiden? Ich möchte nicht, dass der Benutzer des Skripts diese Eigenart berücksichtigt und "a=b" zitiert, was kontra-intuitiv ist.

Dieses Batch-Skript wurde unter Windows 7 ausgeführt.

\===== EDIT =====

Ein wenig mehr Hintergrund: Ich stieß auf dieses Problem, als ich eine Bat-Datei zum Starten einer Java-Anwendung schrieb. Ich wollte einige Args in der Bat-Datei verbrauchen und dann den Rest an die Java-Anwendung weitergeben. Mein erster Versuch war also, Folgendes zu tun shift und bauen dann die Args-Liste neu auf (da %* wird nicht beeinflusst von shift ). Es sah in etwa so aus, und da habe ich das Problem entdeckt:

rem Rebuild the args, %* does not work after shift
:args
if not "%1" == "" (
  set ARGS=!ARGS! %1
  shift
  goto args
)

Der nächste Schritt bestand darin, die Umschaltfunktion nicht mehr zu verwenden, sondern sie von Hand zu implementieren, indem ein Zeichen nach dem anderen aus %* bis ein Leerzeichen auftaucht:

rem Remove the 1st arg if it was the profile
set ARGS=%*
if not "%FIRST_ARG%" == "%KNOA_PROFILE%" goto remove_first_done
:remove_first
if not defined ARGS goto remove_first_done
if "%ARGS:~0,1%" == " " goto remove_first_done
set ARGS=%ARGS:~1%
goto remove_first
:remove_first_done

Aber das ist hässlich und könnte in einigen Fällen, die ich nicht bedacht habe, immer noch scheitern. Also beschloss ich schließlich, ein Java-Programm zu schreiben, um die Argumente zu parsen! In meinem Fall ist das in Ordnung, da ich einen Server starte und der Nachteil eines zusätzlichen Java-Aufrufs minimal ist. Es ist verblüffend, was man am Ende manchmal tut.

Sie fragen sich vielleicht, warum ich mich nicht um die Args in der Java-Anwendung selbst gekümmert habe? Die Antwort ist, dass ich in der Lage sein möchte, JVM-Optionen wie -Xmx zu übergeben, die vor dem Aufruf von java verarbeitet werden müssen.

6voto

Anders Punkte 89514

Ich vermute, dass es dies tut, damit /param=data ist dasselbe wie /param data

Ich kenne keine Tricks, um dieses blöde (wahrscheinlich gewollte) Parsing-Problem zu beheben, aber ich habe einen superhässlichen Workaround gefunden:

@echo off
setlocal ENABLEEXTENSIONS
set param=1
:fixnextparam
set p=
((echo "%~1"|find " ")>nul)||(
    call :fixparam %param% "%~1" "%~2" %* 2>nul
)
if "%p%"=="" (set "p%param%=%1") else shift
shift&set /A param=%param% + 1
if not "%~1"=="" goto fixnextparam

echo.1=%p1%
echo.2=%p2%
echo.3=%p3%
echo.4=%p4%
echo.5=%p5%
goto:EOF

:fixparam
set p%1=
for /F "tokens=4" %%A in ("%*") do (
    if "%%~A"=="%~2=%~3" set p=!&set "p%1=%%A"
)
goto:EOF

Wenn ich ausführe test.cmd foo=bar baz "fizz buzz" w00t Ich verstehe:

1=foo=bar
2=baz
3="fizz buzz"
4=w00t
5=

Das Problem dabei ist natürlich, dass Sie keine Variablenerweiterung im Stil von %~dp1 durchführen können. Es ist nicht möglich zu tun call :mylabel %* und dann entweder %1 verwenden, weil der Aufruf :batchlabel das gleiche Problem beim Parsen der Parameter hat!

Wenn Sie die %~dp1-Behandlung wirklich brauchen, können Sie die WSH/Batch-Hybrid hacken:

@if (1==1) @if(1==0) @ELSE
@echo off
@SETLOCAL ENABLEEXTENSIONS
if "%SPECIALPARSE%"=="*%~f0" (
    echo.1=%~1
    echo.2=%~2
    echo.3=%~3
    echo.4=%~4
    echo.5=%~5
) else (
    set "SPECIALPARSE=*%~f0"
    cscript //E:JScript //nologo "%~f0" %*
)
@goto :EOF
@end @ELSE
w=WScript,wa=w.Arguments,al=wa.length,Sh=w.CreateObject("WScript.Shell"),p="";
for(i=0;i<al;++i)p+="\""+wa.Item(i)+"\" ";
function PipeStream(i,o){for(;!i.AtEndOfStream;)o.Write(i.Read(1))}
function Exec(cmd,e){
    try{
        e=Sh.Exec(cmd);
        while(e.Status==0){
            w.Sleep(99);
            PipeStream(e.StdOut,w.StdOut);
            PipeStream(e.StdErr,w.StdErr);
        }
        return e.ExitCode;
    }catch(e){return e.number;}
}
w.Quit(Exec("\""+WScript.ScriptFullName+"\" "+p));
@end

2voto

ghostdog74 Punkte 305138

Ich antworte nur auf diese Frage: >> Warum ist das so und wie kann man es vermeiden?

Mein Vorschlag: Eine bessere Sprache zu verwenden. Nein, ich scherze nicht. Batch hat zu viele Macken/Nuancen wie diese plus eine Menge anderer Einschränkungen, es ist einfach nicht die Zeit wert, mit hässlichen/ineffizienten Workarounds zu kommen. Wenn Sie Windows 7 und höher verwenden, warum nicht versuchen, mit vbscript oder sogar powershell. Diese Tools/Sprachen werden Ihnen bei Ihren täglichen Programmier-/Administrationsaufgaben sehr helfen. Ein Beispiel dafür, wie vbscript sich um ein solches Problem kümmern kann:

For i=0 To WScript.Arguments.Count-1
    WScript.Echo WScript.Arguments(i)
Next

Ausgabe:

C:\test>cscript //nologo myscript.vbs a=b c
a=b
c

Beachten Sie, dass die Argumente ordnungsgemäß berücksichtigt werden.

2voto

walid2mi Punkte 2566
@echo off
setlocal enabledelayedexpansion

if %1 neq ~ (
 set "n=%*"
 for %%a in ("!n: =" "!") do (
 set "s=%%a"
 if !s:~0^,2!==^"^" (
   set "s=!s:" "= !"
   set "s=!s:""="!"
)
 set "m=!m! !s!"
 )
 %0 ~ !m!
)
endlocal
shift

echo arg1: %~1
echo arg1: %~2
echo arg1: %~dp3
exit /b

c:> untested.bat a=b c "%USERPROFILE%"

1voto

walid2mi Punkte 2566

@jeb: hier eine andere Methode

ungetestet.cmd

@echo off
setlocal enabledelayedexpansion

set "param=%*"
set param="%param: =" "%"

for /f "delims=" %%a in ('echo %param: =^&echo.%') do (
  set "str=%%a"
  if !str:~0^,2!==^"^" (
     set "str=!str:^&echo.=!"
     set "str=!str:"^"= !"
     set str="!str:~1,-1!"
  )
  set "m=!m! !str!"
)

Call :sub %m%
endlocal & goto :eof

:sub
 echo arg1: %~1
 echo arg2: %~2
 echo arg3: %~3
 echo arg4: %~4
goto :eof

0voto

tahoar Punkte 1758

Auch hier kann ich mir das unerwartete Verhalten nicht erklären, aber es gibt eine sehr einfache Lösung. Ändern Sie einfach Ihre Argument-Referenzen, dann zitieren Sie das Argument beim Aufruf als solches:

test.bat-Skript:

echo arg1: %~1
echo arg2: %~2
echo arg3: %~3

Aufrufen:

test.bat "a=b" c

Ausgabe:

arg1: a=b
arg2: c
arg3:

Beachten Sie, dass beim Verweis auf die Argumente mit einer Tilde (~) die führenden/nachlaufenden Anführungszeichen aus dem Argument entfernt werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X