Jak sprawdzić parametr wiersza poleceń w pliku „.bat”?

103

Mój system operacyjny to Windows Vista. Potrzebuję pliku „.bat”, w którym muszę sprawdzić, czy użytkownik wprowadza parametr wiersza polecenia, czy nie. Jeśli tak, to jeśli parametr jest równy -b, zrobię coś, w przeciwnym razie oflaguję „Invalid input”. Jeśli użytkownik nie poda żadnego parametru wiersza poleceń, coś zrobię. Utworzyłem następujący plik .bat. Działa dla przypadków -bi nie jest równy -b- ale kończy się niepowodzeniem, gdy użytkownik nie przekazuje żadnego parametru wiersza polecenia.

Zawsze otrzymuję błąd:

GOTO was unexpected at this time.

Czy ktoś może mi powiedzieć, co ja tu robię źle?


ECHO OFF
CLS
ECHO.

IF [%1]==[/?] GOTO BLANK

IF %1=="-b" GOTO SPECIFIC

IF NOT %1=="-b" GOTO UNKNOWN

:SPECIFIC

ECHO SPECIFIC

GOTO DONE

:BLANK

ECHO No Parameter

GOTO DONE

:UNKNOWN

ECHO Unknown Option

GOTO DONE

:DONE

ECHO Done!
javauser71
źródło
Jeśli dodasz nawiasy (jak w GOTO BLANKwierszu) do pozostałych dwóch IFinstrukcji, czy to rozwiąże problem?
Jeremiah Willcock

Odpowiedzi:

141

Musisz sprawdzić, czy parametr jest pusty: if "%~1"=="" goto blank

Gdy już to zrobisz, włącz if / else -b: if "%~1"=="-b" (goto specific) else goto unknown

Otaczanie parametrów cudzysłowami ułatwia sprawdzanie elementów takich jak puste / puste / brakujące parametry. "~" zapewnia, że ​​podwójne cudzysłowy są usuwane, jeśli były w argumencie wiersza poleceń.

afrazier
źródło
To działa!! Tylko „inny” nie jest akceptowane. Pojawia się błąd: „else” nie jest rozpoznawane jako polecenie wewnętrzne lub zewnętrzne, program operacyjny lub plik wsadowy. Więc dodałem kolejny IF dla przypadku „nie równe -b”. Dzięki za szybką odpowiedź.
javauser71
7
ELSE musi znajdować się na tej samej linii co IF lub musi znajdować się bezpośrednio za
drabinką
4
Wydaje się, że to nie działa, jeśli% 1 zawiera spacje, co może mieć miejsce, jeśli jest to pełna ścieżka do pliku wykonywalnego. Zobacz odpowiedź Jeremiaha Willcocka, aby znaleźć rozwiązanie, które działa nawet dla parametrów ze spacjami w wartościach.
Mark A. Fitzgerald
nie zostanie zaakceptowany, jeśli Twój argument to a File, w takim przypadku powinieneś sprawdzać tylko existlub not exist. Dlatego twoje argumenty powinny być dobrze zdefiniowane lub po prostu użyj PowerShell lub vbScript (jeśli jesteś w latach 80-tych ...)
1
To się nie powiedzie run.bat "a b". "%1"==""ulega awarii, jeśli argument ma spację. Zobacz stackoverflow.com/a/46942471
wisbucky,
27

Poszukaj odpowiedzi na http://ss64.com/nt/if.html ; polecenie jest IF [%1]==[] GOTO NO_ARGUMENTlub podobne.

Jeremiah Willcock
źródło
1
To nie tylko działa! Po prostu działa, w przeciwieństwie do "%1"=="". Muszę to rozwinąć w mojej własnej odpowiedzi.
Tomasz Gandor
1
to się zepsuje, gdy cytowany jest% 1, np foo.bat "1st parameter" 2nd_param. Zobacz stackoverflow.com/questions/2541767/…
matt wilkie
Użycie [%1]==[-b]nie będzie pasować, jeśli argument jest cytowany. Przykład run.bat "-b". Zobacz stackoverflow.com/a/46942471
wisbucky,
20

Krótka odpowiedź - użyj nawiasów kwadratowych:

if [%1]==[] goto :blank

lub (jeśli potrzebujesz obsługiwać cytowane argumenty, zobacz Edycję poniżej):

if [%~1]==[] goto :blank

Czemu? możesz zapytać. Cóż, tak jak wspomniał Jeremiah Willcock: http://ss64.com/nt/if.html - używają tego! OK, ale co jest nie tak z cudzysłowami?

Ponownie, krótka odpowiedź: są „magiczne” - czasami podwójne (podwójne) cudzysłowy są zamieniane na pojedynczy (podwójny) cudzysłów. I na początek muszą pasować.

Rozważ ten mały skrypt:

@rem argq.bat
@echo off

:loop 
if "%1"=="" goto :done
echo %1
shift
goto :loop

:done
echo Done.

Przetestujmy to:

C:\> argq bla bla
bla
bla
Done.

Wydaje się działać. Ale teraz przejdźmy na drugi bieg:

C:\> argq "bla bla"
bla""=="" was unexpected at this time.

Boom To nie zostało ocenione jako prawda, ani też nie zostało ocenione jako fałszywe. Skrypt ZMARŁ. Jeśli miałbyś wyłączyć reaktor gdzieś na linii, cóż - pech. Umrzesz teraz jak Harry Daghlian.

Możesz pomyśleć - OK, argumenty nie mogą zawierać cudzysłowów. Jeśli tak, to się dzieje. Źle Oto pociecha:

C:\> argq ""bla bla""
""bla
bla""
Done.

O tak. Nie martw się - czasem to będzie działać.

Wypróbujmy inny skrypt:

@rem args.bat
@echo off

:loop 
if [%1]==[] goto :done
echo %1
shift
goto :loop

:done
echo Done.

Możesz sam sprawdzić, czy w powyższych przypadkach działa poprawnie. To logiczne - cudzysłowy nie mają nic wspólnego z nawiasami, więc nie ma tu magii. Ale co z doprawieniem argumentów nawiasami?

D:\>args ]bla bla[
]bla
bla[
Done.

D:\>args [bla bla]
[bla
bla]
Done.

Nie ma szczęścia. Nawiasy po prostu nie mogą zablokować cmd.exeparsera.

Wróćmy na chwilę do złych cytatów. Problem pojawił się, gdy argument kończył się cytatem:

D:\>argq "bla1 bla2"
bla2""=="" was unexpected at this time.

Co jeśli zdam tylko:

D:\>argq bla2"
The syntax of the command is incorrect.

Skrypt w ogóle się nie uruchomi. To samo dotyczy args.bat:

D:\>args bla2"
The syntax of the command is incorrect.

Ale co otrzymam, gdy liczba "-znaków „pasuje” (czyli - jest parzysta), w takim przypadku:

D:\>args bla2" "bla3
bla2" "bla3
Done.

MIŁE - mam nadzieję, że nauczyłeś się czegoś o tym, jak .batpliki dzielą swoje argumenty wiersza poleceń (WSKAZÓWKA: * To nie jest dokładnie tak, jak w bashu). Powyższy argument zawiera spację. Ale cytaty nie są usuwane automatycznie.

A argq? Jak to na to reaguje? Przewidywalnie:

D:\>argq bla2" "bla3
"bla3"=="" was unexpected at this time.

A więc - pomyśl, zanim powiesz: „Wiesz co? Po prostu używaj cudzysłowów. [Bo dla mnie to wygląda ładniej]”.

Edytować

Niedawno pojawiły się komentarze na temat tej odpowiedzi - cóż, nawiasy kwadratowe „nie radzą sobie” z przekazywaniem cytowanych argumentów i traktowaniem ich tak, jakby nie były cytowane.

Składnia:

if "%~1"=="" (...)

Nie jest jakąś nowo odkrytą zaletą podwójnych cudzysłowów, ale przejawem zgrabnej funkcji usuwania cudzysłowów ze zmiennej argumentu, jeśli pierwszy i ostatni znak jest cudzysłowem podwójnym.

Ta „technologia” działa równie dobrze w przypadku nawiasów kwadratowych:

if [%~1]==[] (...)

Warto było zwrócić na to uwagę, więc głosuję również za nową odpowiedzią.

Wreszcie, fani podwójnych cudzysłowów, czy ""w waszej książce istnieje argument w formie , czy jest on pusty? Tylko pytam' ;)

Tomasz Gandor
źródło
1
Nawiasy kwadratowe [%1]==[-b]nie będą pasować, jeśli argument będzie cytowany jak run.bat "-b". Zobacz stackoverflow.com/a/46942471
wisbucky,
Uwaga historyczna: [%1]==[-b]i "%1"=="-b"były takie same dla skryptów wsadowych systemów MS / PC-DOS pod Windows 98 i wcześniejszych. Odkąd w Win 2000 / NT wprowadzono składnię, w if "%~1"=="-b"której podwójne cudzysłowy mają specjalne znaczenie, jest to sposób, w jaki należy kodować skrypty, ponieważ zapewnia bardziej solidną ochronę. Podwójne cudzysłowy zmieniają znaczenie znaków specjalnych (spróbuj & | i% znaków w linii poleceń). 99,9% przykładów działa ze składnią podwójnych cudzysłowów - w Twoim przykładzie argq bla2" "bla3jest tylko wielkość liter, aby uzasadnić nawiasy kwadratowe. Umieszczone podwójne cudzysłowy to przepis na katastrofę - wystarczy powiedzieć
Pomiń R
@SkipR - dlatego w systemach plików Windows nie możesz mieć tych znaków specjalnych w nazwach. Nie mam matematycznego dowodu, ale myślę, że po prostu NIE wszystko da się zacytować (w sensie - zrobić dosłownie przez jakąś sekwencję ucieczki itp.) W cmdpowłoce. W innych systemach można czasem zacytować wszystko, łącznie ze znakiem NUL. ( stackoverflow.com/questions/2730732/… ) - to pytanie właśnie się pokazuje, trzeba użyć jakiegoś zewnętrznego programu.
Tomasz Gandor
8

Oprócz innych odpowiedzi, które subskrybuję, możesz rozważyć użycie /Iprzełącznika IFpolecenia.

... przełącznik / I, jeśli jest określony, mówi, aby wykonać porównania ciągów bez uwzględniania wielkości liter.

może to być pomocne, jeśli chcesz zapewnić użytkownikom elastyczność bez rozróżniania wielkości liter przy określaniu parametrów.

IF /I "%1"=="-b" GOTO SPECIFIC
ROCZNIE.
źródło
5

Porównujesz struny. Jeśli argumenty zostaną pominięte, %1interpretowane jest jako puste, więc polecenia stają się IF =="-b" GOTO SPECIFICna przykład (co jest błędem składniowym). Zawiń swoje ciągi w cudzysłów (lub nawiasy kwadratowe).

REM this is ok
IF [%1]==[/?] GOTO BLANK

REM I'd recommend using quotes exclusively
IF "%1"=="-b" GOTO SPECIFIC

IF NOT "%1"=="-b" GOTO UNKNOWN
Jeff Mercado
źródło
JEŚLI [% 1] == [/?] Nie działa, ale jeśli zrobię, JEŻELI [% 1] == [] lub "% 1" == "", to działa. W każdym razie teraz mogę zacząć. Dziękuję za szybką odpowiedź.
javauser71
5

Właściwie wszystkie inne odpowiedzi mają wady. Najbardziej niezawodny sposób to:

IF "%~1"=="-b" (GOTO SPECIFIC) ELSE (GOTO UNKNOWN)

Szczegółowe wyjaśnienie:

Użycie "%1"=="-b"spowoduje załatwienie awarii przy przekazywaniu argumentów spacjami i cudzysłowami. To najmniej niezawodna metoda.

IF "%1"=="-b" (GOTO SPECIFIC) ELSE (GOTO UNKNOWN)

C:\> run.bat "a b"

b""=="-b" was unexpected at this time.

Używanie [%1]==[-b]jest lepsze, ponieważ nie spowoduje awarii ze spacjami i cudzysłowami, ale nie będzie pasować, jeśli argument jest otoczony cudzysłowami.

IF [%1]==[-b] (GOTO SPECIFIC) ELSE (GOTO UNKNOWN)

C:\> run.bat "-b"

(does not match, and jumps to UNKNOWN instead of SPECIFIC)

Używanie "%~1"=="-b"jest najbardziej niezawodne. %~1usunie otaczające cudzysłowy, jeśli istnieją. Działa więc z cudzysłowami i bez cudzysłowów, a także bez argumentów.

IF "%~1"=="-b" (GOTO SPECIFIC) ELSE (GOTO UNKNOWN)

C:\> run.bat
C:\> run.bat -b
C:\> run.bat "-b"
C:\> run.bat "a b"

(all of the above tests work correctly)
wisbucky
źródło
1
To nie koniec nawiasów kwadratowych, widzisz: IF [%~1]==[]- to działa, a nawet obsługuje "-b". Musisz tylko umieć myśleć wewnątrz pudełka, erm, kwadratu [nawiasy].
Tomasz Gandor
3

Ostatnio zmagałem się z implementacją złożonych przełączników parametrów w pliku wsadowym, więc oto wynik moich badań. Żadna z udzielonych odpowiedzi nie jest w pełni bezpieczna, przykłady:

"%1"=="-?" nie będzie pasował, jeśli parametr jest ujęty w cudzysłowy (wymagane w przypadku nazw plików itp.) lub ulegnie awarii, jeśli parametr jest w cudzysłowach i ma spacje (często spotykane w nazwach plików)

@ECHO OFF
SETLOCAL
echo.
echo starting parameter test...
echo.
rem echo First parameter is %1
if "%1"=="-?" (echo Condition is true, param=%1) else (echo Condition is false, param=%1)
C:\>test.bat -?

starting parameter test...

Condition is true, param=-?

C:\>test.bat "-?"

starting parameter test...

Condition is false, param="-?"

Dowolna kombinacja z nawiasami kwadratowymi [%1]==[-?]lub [%~1]==[-?]zakończy się niepowodzeniem, jeśli parametr zawiera spacje w cudzysłowach:

@ECHO OFF
SETLOCAL 
echo.
echo starting parameter test...
echo.
echo First parameter is %1
if [%~1]==[-?] (echo Condition is true, param=%1) else (echo Condition is false, param=%1)

C:\>test.bat "long file name"

starting parameter test...

First parameter is "long file name"
file was unexpected at this time.

Proponowane najbezpieczniejsze rozwiązanie "%~1"=="-?"ulegnie awarii ze złożonym parametrem, który zawiera tekst poza cudzysłowami i tekst ze spacjami w cudzysłowie:

@ECHO OFF
SETLOCAL 
echo.
echo starting parameter test...
echo.
echo First parameter is %1
if "%~1"=="-?" (echo Condition is true, param=%1) else (echo Condition is false, param=%1)

C:\>test.bat -source:"long file name"

starting parameter test...

First parameter is -source:"long file name"
file was unexpected at this time.

Jedynym sposobem, aby upewnić się, że wszystkie powyższe scenariusze są uwzględnione, jest użycie EnableDelayedExpansion i przekazanie parametrów przez odwołanie (nie przez wartość) przy użyciu zmiennych. Wtedy nawet najbardziej złożony scenariusz będzie działał dobrze:

@ECHO OFF
SETLOCAL EnableDelayedExpansion
echo.
echo starting parameter test...
echo.
echo First parameter is %1
:: we assign the parameter to a variable to pass by reference with delayed expansion
set "var1=%~1"
echo var1 is !var1!
:: we assign the value to compare with to a second variable to pass by reference with delayed expansion
set "var2=-source:"c:\app images"\image.png"
echo var2 is !var2!
if "!var1!"=="!var2!" (echo Condition is true, param=!var1!) else (echo Condition is false, param=!var1!)
C:\>test.bat -source:"c:\app images"\image.png

starting parameter test...

First parameter is -source:"c:\app images"\image.png
var1 is -source:"c:\app images"\image.png
var2 is -source:"c:\app images"\image.png
Condition is true, param=-source:"c:\app images"\image.png

C:\>test.bat -source:"c:\app images"\image1.png

starting parameter test...

First parameter is -source:"c:\app images"\image1.png
var1 is -source:"c:\app images"\image1.png
var2 is -source:"c:\app images"\image.png
Condition is false, param=-source:"c:\app images"\image1.png

C:\>test.bat -source:"c:\app images\image.png"

starting parameter test...

First parameter is -source:"c:\app images\image.png"
var1 is -source:"c:\app images\image.png"
var2 is -source:"c:\app images"\image.png
Condition is false, param=-source:"c:\app images\image.png"
D.Barev
źródło
0

W tym celu partia systemu Windows ma słowo kluczowe DEFINED.

IF DEFINED %~1 foo
IF NOT DEFINED %~1 bar
Charles
źródło
Dziękuję David. Nie wiem, dlaczego nie było powrotu karetki i nowej linii między IF i IF NOT. Nawet nie zauważyłem.
Charles