Jak uzyskać wynik polecenia w zmiennej w systemie Windows?

137

Chcę uzyskać wynik polecenia jako zmienną w skrypcie wsadowym systemu Windows (zobacz, jak uzyskać wynik polecenia w bash dla odpowiednika skryptu bash). Preferowane jest rozwiązanie, które będzie działać w pliku .bat, ale mile widziane są również inne popularne rozwiązania skryptowe dla systemu Windows.

John Meagher
źródło
10
John, absurdalnie trudno jest znaleźć to bardzo przydatne pytanie. Czy mógłbyś rozważyć dodanie alternatywnego sformułowania jak Jak uchwycić na wyjście aa programu do zmiennej w pliku wsadowym systemu Windows?
Piotr Dobrogost
3
Google wyświetla tę stronę na 3 miejscu.
René Nyffenegger
2
Możliwy duplikat partii Windows przypisuje wyjście programu do zmiennej
Michael Freidgeim
@MichaelFreidgeim to pytanie zostało faktycznie zadane 2 lata przed tym duplikatem - ale istnieje wiele duplikatów tego pytania. Zobacz komentarze na stackoverflow.com/questions/6359820/ ...
icc97
1
@ icc97, „Możliwy duplikat” to sposób na uporządkowanie - zamknięcie podobnych pytań i pozostawienie jednego z najlepszymi odpowiedziami. Data nie jest konieczna. Zobacz meta.stackexchange.com/questions/147643/ ... Jeśli zgadzasz się, że wymaga to wyjaśnienia, zagłosuj na meta.stackexchange.com/questions/281980/… Jeśli widzisz wiele duplikatów, wybierz jeden jako kanoniczny i zagłosuj za zamknięciem innych .
Michael Freidgeim

Odpowiedzi:

36

Jeśli musisz przechwycić wszystkie dane wyjściowe polecenia, możesz użyć takiej partii:

@ECHO OFF
IF NOT "%1"=="" GOTO ADDV
SET VAR=
FOR /F %%I IN ('DIR *.TXT /B /O:D') DO CALL %0 %%I
SET VAR
GOTO END

:ADDV
SET VAR=%VAR%!%1

:END

Wszystkie wiersze wyjściowe są przechowywane w VAR oddzielone znakiem „!”.

@John: czy ma to jakieś praktyczne zastosowanie? Myślę, że powinieneś obejrzeć PowerShell lub inny język programowania zdolny do łatwego wykonywania zadań skryptowych (Python, Perl, PHP, Ruby)

PabloG
źródło
Pojedyncza linia odpowiedzi rozwiąże mój konkretny przypadek. Pomyślałem, że istnieje opcja wieloliniowa (lub łatwiejsza opcja pojedynczej linii). Wydaje mi się, że możliwości plików nietoperzy nie są po prostu świetne. Potrzebują tego skrypty nietoperzy opakowujące aplikacje Java. Budowanie głównie ścieżek klas.
John Meagher,
Bądź z tym bardzo ostrożny. GWT próbował również użyć plików wsadowych do uruchamiania Javy z określonymi ścieżkami klas, co zakończyło się strasznym niepowodzeniem, gdy tylko dotarłeś do katalogów ze znakami spoza ASCII (w tym przypadku był to mój dom :)). Od tego czasu przepisali to za pomocą VBS.
Joey,
27
czy ma to jakieś praktyczne zastosowanie? Żartujesz? Jest to używane przez cały czas w innych powłokach (chyba wszystkie GNU / Linux) i jest bardzo przydatne.
Piotr Dobrogost
2
@PiotrDobrogost Myślę, że próbował powiedzieć, że skrypty bash są okropnym reliktem przeszłości (potrzebujesz pętli for tylko po to, aby przechwycić wyjście programu? Poważnie?), A jeśli kiedykolwiek dojdziesz do sedna tam, gdzie musisz używać zmiennych, powinieneś użyć czegoś bardziej nowoczesnego, takiego jak PowerShell.
Ajedi32,
3
Właściwie jest to praktyczne zastosowanie ... trochę. Chcę użyć skryptu PowerShell, aby znaleźć określoną ścieżkę Visual Studio, ale potrzebuję z tej ścieżki pliku wsadowego, który ustawia kilka zmiennych env, więc mogę wywołać editbin ... Więc muszę znaleźć ścieżkę i przekaż go z powrotem do wywołującej instancji programu cmd, abym mógł go tam uruchomić. ... Tak, to jest tak straszne jak dźwięki. Visual Studio czasami sprawia, że ​​mam ochotę płakać. @ Ajedi32 Myślę, że chciałeś powiedzieć, że skrypty wsadowe są okropnym reliktem przeszłości. ;)
jpmc26
85

Pokora do dowodzenia zgromadziła przez lata kilka interesujących zdolności:

D:\> FOR /F "delims=" %i IN ('date /t') DO set today=%i
D:\> echo %today%
Sat 20/09/2008

Zauważ, że "delims="nadpisuje domyślną spację i ograniczniki tabulacji, tak że dane wyjściowe polecenia date zostaną pożarte od razu.

Aby uchwycić wyjście wieloliniowe, nadal może to być zasadniczo jednowierszowy (używając zmiennej lf jako separatora w wynikowej zmiennej):

REM NB:in a batch file, need to use %%i not %i
setlocal EnableDelayedExpansion
SET lf=-
FOR /F "delims=" %%i IN ('dir \ /b') DO if ("!out!"=="") (set out=%%i) else (set out=!out!%lf%%%i)
ECHO %out%

Aby uchwycić wyrażenie potokowe, użyj ^|:

FOR /F "delims=" %%i IN ('svn info . ^| findstr "Root:"') DO set "URL=%%i"
spóźniać się
źródło
1
Podobnie jak w przypadku odpowiedzi @ PabloG, zadziała to tylko w celu uzyskania ostatniej linii wyniku polecenia, w tym przypadku „date / t”.
John Meagher,
11
Znalazłem twoją odpowiedź, że działa tylko wtedy, gdy użyłem znaków podwójnego procentu, czyliFOR /F "delims=" %%i IN ('date /t') DO set today=%%i
Rabarberski
18
@Rabarberski: Jeśli wpiszesz forpolecenie bezpośrednio w wierszu poleceń, użyjesz pojedynczego %. Jeśli używasz go w pliku wsadowym, używasz %%.
indyw
@tardate: Czy mógłbyś powiedzieć, jak będzie wyglądał skrypt, jeśli polecenie będzie %for /f "delims=" %%i in ('date +%F_%H-%M-%S') do set now=%%i
wymagało podania
1
@degenerate Polecenie jest poprawne pod warunkiem, że dateużyte polecenie pochodzi z Cygwin. Zgadzam się, że powinienem o tym wspomnieć.
dma_k
24

Aby uzyskać bieżący katalog, możesz użyć tego:

CD > tmpFile
SET /p myvar= < tmpFile
DEL tmpFile
echo test: %myvar%

Używa jednak pliku tymczasowego, więc nie jest najpiękniejszy, ale z pewnością działa! „CD” umieszcza bieżący katalog w „tmpFile”, „SET” ładuje zawartość tmpFile.

Oto rozwiązanie dla wielu wierszy z „tablicami”:

@echo off

rem ---------
rem Obtain line numbers from the file
rem ---------

rem This is the file that is being read: You can replace this with %1 for dynamic behaviour or replace it with some command like the first example i gave with the 'CD' command.
set _readfile=test.txt

for /f "usebackq tokens=2 delims=:" %%a in (`find /c /v "" %_readfile%`) do set _max=%%a
set /a _max+=1
set _i=0
set _filename=temp.dat

rem ---------
rem Make the list
rem ---------

:makeList
find /n /v "" %_readfile% >%_filename%

rem ---------
rem Read the list
rem ---------

:readList
if %_i%==%_max% goto printList

rem ---------
rem Read the lines into the array
rem ---------
for /f "usebackq delims=] tokens=2" %%a in (`findstr /r "\[%_i%]" %_filename%`) do set _data%_i%=%%a
set /a _i+=1
goto readList

:printList
del %_filename%
set _i=1
:printMore
if %_i%==%_max% goto finished
set _data%_i%
set /a _i+=1
goto printMore

:finished

Ale możesz rozważyć przejście na inną, mocniejszą powłokę lub stworzenie aplikacji do tych rzeczy. To znacznie rozszerza możliwości plików wsadowych.

Zła aktywność
źródło
Czy istnieje odmiana, która będzie działać w celu przechwycenia wielu wierszy z polecenia?
John Meagher,
6
Aby uzyskać bieżący katalog, możesz użyć echo% CD%
Joe,
9

musisz użyć SETpolecenia z parametrem /Pi skierować do niego swoje wyjście. Na przykład zobacz http://www.ss64.com/nt/set.html . Będzie działać dla CMD, nie jestem pewien co do plików .BAT

Z komentarza do tego posta:

To łącze zawiera polecenie „ Set /P _MyVar=<MyFilename.txt”, które mówi, że zostanie ustawione _MyVarna pierwszą linię z MyFilename.txt. Może to być użyte jako myCmd > tmp.txt„z set /P myVar=<tmp.txt”. Ale otrzyma tylko pierwszą linię danych wyjściowych, a nie wszystkie dane wyjściowe

Ilya Kochetov
źródło
2
To łącze zawiera polecenie „Set / P _MyVar = <MyFilename.txt”, które mówi, że ustawi _MyVar na pierwszą linię z MyFilename.txt. Można go użyć jako „myCmd> tmp.txt” z „set / P myVar = <tmp.txt”. Ale otrzyma tylko pierwszą linię danych wyjściowych, a nie wszystkie dane wyjściowe.
John Meagher,
Ta odpowiedź jest dla początkujących, takich jak ja
5

Przykład ustawienia w zmiennej środowiskowej „V” najnowszego pliku

FOR /F %I IN ('DIR *.* /O:D /B') DO SET V=%I

w pliku wsadowym należy użyć podwójnego przedrostka w zmiennej pętli:

FOR /F %%I IN ('DIR *.* /O:D /B') DO SET V=%%I
PabloG
źródło
2
Widziałem już takie podejście, ale wydaje się to naprawdę hakerskie. Ma również problem z tym, że przechwytuje tylko ostatnią linię danych wyjściowych z polecenia w zmiennej.
John Meagher,
3

Po prostu użyj wyniku z FORpolecenia. Na przykład (w pliku wsadowym):

for /F "delims=" %%I in ('dir /b /a-d /od FILESA*') do (echo %%I)

Możesz użyć %%Ijako żądanej wartości. Podobnie jak ten: %%I.

Z góry %%Inie ma spacji ani znaków CR i może być używany do porównań !!

Możliwość wyścigów
źródło
3

Chciałbym dodać uwagę do powyższych rozwiązań:

Wszystkie te składnie działają doskonale, JEŻELI POLECENIE ZNAJDUJE SIĘ W ŚCIEŻCE lub JEŻELI POLECENIE JEST ŚCIEŻKĄ CMD BEZ SPACJI I ZNAKÓW SPECJALNYCH.

Ale jeśli spróbujesz użyć wykonywalnego polecenia znajdującego się w folderze, którego ścieżka zawiera znaki specjalne, musisz ująć ścieżkę polecenia w podwójne cudzysłowy ("), a składnia FOR / F nie będzie działać.

Przykłady:

$ for /f "tokens=* USEBACKQ" %f in (
    `""F:\GLW7\Distrib\System\Shells and scripting\f2ko.de\folderbrowse.exe"" Hello '"F:\GLW7\Distrib\System\Shells and scripting"'`
) do echo %f
The filename, directory name, or volume label syntax is incorrect.

lub

$ for /f "tokens=* USEBACKQ" %f in (
      `"F:\GLW7\Distrib\System\Shells and scripting\f2ko.de\folderbrowse.exe" "Hello World" "F:\GLW7\Distrib\System\Shells and scripting"`
) do echo %f
'F:\GLW7\Distrib\System\Shells' is not recognized as an internal or external command, operable program or batch file.

lub

`$ for /f "tokens=* USEBACKQ" %f in (
     `""F:\GLW7\Distrib\System\Shells and scripting\f2ko.de\folderbrowse.exe"" "Hello World" "F:\GLW7\Distrib\System\Shells and scripting"`
) do echo %f
'"F:\GLW7\Distrib\System\Shells and scripting\f2ko.de\folderbrowse.exe"" "Hello' is not recognized as an internal or external command, operable program or batch file.

W takim przypadku jedynym rozwiązaniem, które znalazłem, aby użyć polecenia i zapisać jego wynik w zmiennej, jest ustawienie (tymczasowo) domyślnego katalogu na katalog samego polecenia:

pushd "%~d0%~p0"
FOR /F "tokens=* USEBACKQ" %%F IN (
    `FOLDERBROWSE "Hello world!" "F:\GLW7\Distrib\System\Layouts (print,display...)"`
) DO (SET MyFolder=%%F)
popd
echo My selected folder: %MyFolder%

Wynik jest wtedy poprawny:

My selected folder: F:\GLW7\Distrib\System\OS install, recovery, VM\
Press any key to continue . . .

Oczywiście w powyższym przykładzie zakładam, że mój skrypt wsadowy znajduje się w tym samym folderze co jedno z moich poleceń wykonywalnych, więc mogę użyć składni „% ~ d0% ~ p0”. Jeśli tak nie jest, musisz znaleźć sposób na zlokalizowanie ścieżki poleceń i zmienić domyślny katalog na jego ścieżkę.

NB: Dla tych, którzy się zastanawiają, przykładowe polecenie użyte tutaj (do wybrania folderu) to FOLDERBROWSE.EXE. Znalazłem go na stronie internetowej f2ko.de ( http://f2ko.de/en/cmd.php ).

Jeśli ktoś ma lepsze rozwiązanie dla tego rodzaju poleceń dostępnych poprzez złożoną ścieżkę, z przyjemnością o tym usłyszę.

Gilles

Gilles Maisonneuve
źródło
Możesz poprzedzić swoje polecenie znakiem CALL. FOR / F nie działa, jeśli ścieżka zawiera spacje
jeb
@jeb: BARDZO DZIĘKUJĘ! Teraz mój kod < CALL "%SOURCEDIR%\FOLDERBROWSE" ... "quoted parameters"> działa doskonale.
Gilles Maisonneuve
2

Jeśli szukasz rozwiązania podanego w artykule Używanie wyniku polecenia jako argumentu w bash?

to tutaj jest kod:

@echo off
if not "%1"=="" goto get_basename_pwd
for /f "delims=X" %%i in ('cd') do call %0 %%i
for /f "delims=X" %%i in ('dir /o:d /b') do echo %%i>>%filename%.txt
goto end

:get_basename_pwd
set filename=%~n1

:end
  • Spowoduje to wywołanie siebie z wynikiem polecenia CD, tak samo jak pwd.
  • Wyodrębnienie ciągu parametrów zwróci nazwę pliku / folderu.
  • Pobierz zawartość tego folderu i dołącz do pliku nazwa_pliku.txt

[Podziękowania] : Dzięki wszystkim pozostałym odpowiedziom i odrobinie informacji na stronie poleceń systemu Windows XP .

Gustavo Carreno
źródło
2
@echo off

ver | find "6.1." > nul
if %ERRORLEVEL% == 0 (
echo Win7
for /f "delims=" %%a in ('DIR "C:\Program Files\Microsoft Office\*Outlook.EXE" /B /P /S') do call set findoutlook=%%a
%findoutlook%
)

ver | find "5.1." > nul
if %ERRORLEVEL% == 0 (
echo WinXP
for /f "delims=" %%a in ('DIR "C:\Program Files\Microsoft Office\*Outlook.EXE" /B /P /S') do call set findoutlook=%%a
%findoutlook%
)
echo Outlook dir:  %findoutlook%
"%findoutlook%"
Wycieczka
źródło
Dziękujemy za przesłanie odpowiedzi! Chociaż fragment kodu może odpowiedzieć na pytanie, nadal świetnie jest dodać kilka dodatkowych informacji, takich jak wyjaśnienie itp.
j0k
1

Możesz przechwycić wszystkie dane wyjściowe w jednej zmiennej, ale wiersze będą oddzielone wybranym przez Ciebie znakiem (# w poniższym przykładzie) zamiast rzeczywistego CR-LF.

@echo off
setlocal EnableDelayedExpansion
for /f "delims=" %%i in ('dir /b') do (
    if "!DIR!"=="" (set DIR=%%i) else (set DIR=!DIR!#%%i)
)
echo directory contains:
echo %DIR%

Druga wersja, jeśli chcesz wydrukować zawartość wiersz po wierszu. Wykorzystuje to fakt, że nie będzie zduplikowanych wierszy wyjścia z "dir / b", więc może nie działać w ogólnym przypadku.

@echo off
setlocal EnableDelayedExpansion
set count=0
for /f "delims=" %%i in ('dir /b') do (
    if "!DIR!"=="" (set DIR=%%i) else (set DIR=!DIR!#%%i)
    set /a count = !count! + 1
)

echo directory contains:
echo %DIR%

for /l %%c in (1,1,%count%) do (
    for /f "delims=#" %%i in ("!DIR!") do (
        echo %%i
        set DIR=!DIR:%%i=!
    )
)
Adam Mitz
źródło
1
@echo off
setlocal EnableDelayedExpansion
FOR /F "tokens=1 delims= " %%i IN ('echo hola') DO (
    set TXT=%%i
)
echo 'TXT: %TXT%'

wynik to „TXT: hola”

ivan rc
źródło
0

Powinieneś użyć forpolecenia, oto przykład:

@echo off
rem Commands go here
exit /b
:output
for /f "tokens=* useback" %%a in (`%~1`) do set "output=%%a"

i możesz użyć call :output "Command goes here"wtedy wyjście będzie w %output%zmiennej.

Uwaga: Jeśli masz wyjście polecenia, które jest wielowierszowe, to narzędzie setwyświetli wynik do ostatniego wiersza polecenia wielowierszowego.

Hayz
źródło
-1

Zapoznaj się z tym http://technet.microsoft.com/en-us/library/bb490982.aspx, który wyjaśnia, co można zrobić z danymi wyjściowymi polecenia.

Jack B Zwinny
źródło
1
Ten artykuł dotyczy tylko przekierowań i powiązanych tematów. Nie odpowiada na pytanie, jak pobrać wynik jednego polecenia i przenieść go do zmiennej.
John Meagher,
3
Korzystając z jego odpowiedzi, wymyśliłem to: git pull>0 set /P RESULTVAR=<0 edytuj: wydaje mi się, że nie wiem, jak tutaj używać znaków końca linii ... każde polecenie znajduje się w osobnym wierszu.
RibeiroBreno