Как получить результат выполнения команды в переменной в Windows?

133

Я хочу получить результат команды в виде переменной в пакетном сценарии Windows (см., Как получить результат команды в bash для эквивалента сценария bash). Решение, которое будет работать с файлом .bat, является предпочтительным, но также приветствуются другие распространенные решения сценариев Windows.

Джон Мигер
источник
10
Джон, найти этот очень полезный вопрос до смешного сложно . Не могли бы вы рассмотреть вопрос о добавлении альтернативного фразировки , как Как захватить на выходе из аа программы в переменную в пакетном файле с Windows?
Петр Доброгост
3
Google показал эту страницу на 3-м месте.
Рене Ниффенеггер, 06
1
Возможный дубликат пакетной
Майкл
@MichaelFreidgeim, этот вопрос был задан за 2 года до этого дубликата, но есть несколько дубликатов этого вопроса. См. Комментарии на stackoverflow.com/questions/6359820/…
icc97
1
@ icc97, «Возможный дубликат» - это способ очистки - закрыть похожие вопросы и оставить один с лучшими ответами. Дата не важна. См. Meta.stackexchange.com/questions/147643/… Если вы согласны с тем, что это требует разъяснения, пожалуйста, проголосуйте на meta.stackexchange.com/questions/281980/… Если вы видите несколько дубликатов, вы должны выбрать один как канонический и проголосовать за закрытие других ,
Майкл

Ответы:

34

Если вам нужно захватить весь вывод команды, вы можете использовать такой пакет:

@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

Все выходные строки сохраняются в VAR, разделенные знаком "!".

@John: есть ли в этом практическая польза? Я думаю, вам стоит посмотреть PowerShell или любой другой язык программирования, способный легко выполнять скриптовые задачи (Python, Perl, PHP, Ruby).

PabloG
источник
Ответ в одну строку решит конкретный случай, который у меня есть. Я просто подумал, что есть вариант с несколькими строками (или более простой вариант с одной строкой). Я думаю, возможности файлов bat не так уж велики. Это необходимо для сценариев летучих мышей, обертывающих приложения Java. В основном создание путей к классам.
Джон Мигер,
Будьте с этим очень осторожны. GWT также пытался использовать командные файлы для запуска Java с определенными путями к классам, что ужасно провалилось, как только вы попали в каталоги с символами, отличными от ASCII (в этом случае это был мой дом :)). С тех пор они переписали это с помощью VBS.
Joey
26
есть ли в этом практическая польза? Ты смеешься? Это постоянно используется в других оболочках (думаю, во всех оболочках GNU / Linux), и это очень полезно.
Петр Доброгост,
2
@PiotrDobrogost Я думаю, что он пытался сказать, что сценарии bash - это ужасный пережиток прошлого (вам нужно использовать цикл for только для захвата вывода программы? Серьезно?), И если вы когда-нибудь дойдете до сути там, где вам нужно использовать переменные, вам следует использовать что-то более современное, например PowerShell.
Ajedi32
3
Практическое применение, собственно, есть ... вроде как. Я хочу использовать сценарий PowerShell, чтобы найти конкретный путь к Visual Studio, но то, что мне нужно из этого пути, - это пакетный файл, который устанавливает кучу переменных env, поэтому я могу вызвать editbin ... Итак, мне нужно найти путь и вернуть его вызывающему экземпляру, cmdчтобы я мог запустить его там. ... Да, это ужасно, как звучит. Visual Studio иногда заставляет меня плакать. @ Ajedi32 Я думаю, вы хотели сказать, что пакетные скрипты - это ужасный пережиток прошлого. ;)
jpmc26
85

Скромный командир за годы накопил несколько интересных способностей:

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

Обратите внимание, что "delims="перезаписываются разделители пробелов и табуляции по умолчанию, так что вывод команды date сразу же проглатывается.

Для захвата многострочного вывода он все еще может быть однострочным (с использованием переменной lf в качестве разделителя в результирующей переменной):

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%

Чтобы записать конвейерное выражение, используйте ^|:

FOR /F "delims=" %%i IN ('svn info . ^| findstr "Root:"') DO set "URL=%%i"
tardate
источник
1
Как и в случае с ответом @PabloG, это будет работать только для получения последней строки вывода команды, в данном случае «date / t».
Джон Мигер,
11
Я обнаружил, что ваш ответ работает только тогда, когда я использовал двойные знаки процента, то естьFOR /F "delims=" %%i IN ('date /t') DO set today=%%i
Rabarberski
18
@Rabarberski: Если вы вводите forкоманду прямо в командной строке, вы используете одиночный %. Если вы используете его в пакетном файле, вы используете %%.
indiv
@tardate: Не могли бы вы рассказать, как будет выглядеть скрипт, если команде нужно передать аргумент %, например:for /f "delims=" %%i in ('date +%F_%H-%M-%S') do set now=%%i
dma_k
1
@degenerate Команда действительна при условии, что dateиспользуемая команда исходит от Cygwin. Я согласен, я должен был упомянуть об этом.
dma_k
24

Чтобы получить текущий каталог, вы можете использовать это:

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

Хотя он использует временный файл, поэтому он не самый красивый, но, безусловно, работает! «CD» помещает текущий каталог в «tmpFile», «SET» загружает содержимое tmpFile.

Вот решение для нескольких строк с "массивом":

@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

Но вы можете подумать о переходе на другую, более мощную оболочку или создать приложение для этого. Это немного расширяет возможности командных файлов.

Злая деятельность
источник
Есть ли вариант, который будет работать для захвата нескольких строк из команды?
Джон Мигер,
6
Чтобы получить текущий каталог, вы можете использовать echo% CD%
Джо,
9

вам нужно использовать SETкоманду с параметром /Pи направить на него свой вывод. Например, см. Http://www.ss64.com/nt/set.html . Будет работать для CMD, не уверен в файлах .BAT

Из комментария к этому сообщению:

У этой ссылки есть команда " Set /P _MyVar=<MyFilename.txt", которая говорит, что она будет установлена _MyVarна первую строку из MyFilename.txt. Это может быть использовано как myCmd > tmp.txt«с set /P myVar=<tmp.txt». Но он получит только первую строку вывода, а не весь вывод

Илья Кочетов
источник
2
В этой ссылке есть команда «Set / P _MyVar = <MyFilename.txt», которая говорит, что она установит _MyVar на первую строку из MyFilename.txt. Его можно использовать как «myCmd> tmp.txt» с «set / P myVar = <tmp.txt». Но он получит только первую строку вывода, а не весь вывод.
Джон Мигер,
Этот ответ предназначен для таких начинающих, как я
5

Пример установки в переменной среды "V" самого последнего файла

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

в пакетном файле вы должны использовать двойной префикс в переменной цикла:

FOR /F %%I IN ('DIR *.* /O:D /B') DO SET V=%%I
PabloG
источник
2
Я видел этот подход раньше, но он кажется действительно хакерским. У него также есть проблема, заключающаяся в том, что он захватывает только последнюю строку вывода команды в переменной.
Джон Мигер,
3

Просто используйте результат FORкоманды. Например (внутри командного файла):

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

Вы можете использовать в %%Iкачестве желаемого значения. Так же , как это: %%I.

И заранее %%Iне имеет пробелов или символов CR и может использоваться для сравнения !!

Raceableability
источник
2

Если вы ищете решение, представленное в разделе Использование результата команды в качестве аргумента в bash?

тогда вот код:

@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
  • Это вызовет себя с результатом команды CD, так же, как pwd.
  • Извлечение строки по параметрам вернет имя файла / папку.
  • Получите содержимое этой папки и добавьте в filename.txt

[Кредиты] : Спасибо всем остальным ответам и некоторым копаниям на странице команд Windows XP .

Густаво Каррено
источник
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%"
Поход
источник
Спасибо за ответ! Хотя фрагмент кода может ответить на этот вопрос, все же здорово добавить дополнительную информацию, например, объяснить и т. Д.
j0k
2

К приведенным выше решениям я хотел бы добавить замечание:

Все эти синтаксисы работают отлично, ЕСЛИ ВАША КОМАНДА НАЙДЕНА В ПУТИ или ЕСЛИ КОМАНДА ЯВЛЯЕТСЯ cmdpath БЕЗ ПРОБЕЛОВ ИЛИ СПЕЦИАЛЬНЫХ СИМВОЛОВ.

Но если вы попытаетесь использовать исполняемую команду, расположенную в папке, путь которой содержит специальные символы, вам нужно будет заключить путь к вашей команде в двойные кавычки ("), и тогда синтаксис FOR / F не будет работать.

Примеры:

$ 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.

или

$ 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.

или

`$ 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.

В этом случае единственное решение, которое я нашел для использования команды и сохранения ее результата в переменной, - это установить (временно) каталог по умолчанию для самой команды:

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%

Тогда результат правильный:

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

Конечно, в приведенном выше примере я предполагаю, что мой пакетный сценарий находится в той же папке, что и одна из моих исполняемых команд, так что я могу использовать синтаксис «% ~ d0% ~ p0». Если это не ваш случай, вам нужно найти способ найти путь к вашей команде и изменить каталог по умолчанию на его путь.

NB: Для тех, кому интересно, здесь используется пример команды (для выбора папки) FOLDERBROWSE.EXE. Я нашел его на сайте f2ko.de ( http://f2ko.de/en/cmd.php ).

Если у кого-то есть лучшее решение для таких команд, доступных через сложный путь, я буду очень рад услышать об этом.

жилль

Gilles Maisonneuve
источник
Вы можете префикс своей команды с помощью CALL. FOR / F не работает, если в пути есть пробелы
jeb
@jeb: БОЛЬШОЕ СПАСИБО! Теперь мой код < CALL "%SOURCEDIR%\FOLDERBROWSE" ... "quoted parameters"> отлично работает.
Gilles Maisonneuve
1

Вы можете записать весь вывод в одну переменную, но строки будут разделены выбранным вами символом (# в примере ниже) вместо фактического 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%

Вторая версия, если вам нужно распечатать содержимое построчно. При этом учитывается тот факт, что не будет повторяющихся строк вывода из "dir / b", поэтому в общем случае это может не работать.

@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=!
    )
)
Адам Миц
источник
0
@echo off
setlocal EnableDelayedExpansion
FOR /F "tokens=1 delims= " %%i IN ('echo hola') DO (
    set TXT=%%i
)
echo 'TXT: %TXT%'

результат - "TXT: hola"

ivan rc
источник
0

Вам следует использовать forкоманду, вот пример:

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

и вы можете использовать, call :output "Command goes here"тогда вывод будет в %output%переменной.

Примечание. Если у вас есть многострочный вывод команды, этот инструмент будет setвыводить его в последнюю строку многострочной команды.

Hayz
источник
-1

См. Http://technet.microsoft.com/en-us/library/bb490982.aspx, где объясняется, что вы можете делать с выводом команд.

Джек Б. Шустрый
источник
1
Эта статья касается только перенаправления и связанных тем. Он не отвечает на вопрос, как получить результат одной команды и преобразовать его в переменную.
Джон Мигер,
3
Используя его ответ, я придумал следующее: git pull>0 set /P RESULTVAR=<0 edit: Думаю, я не знаю, как использовать здесь разрывы строк ... каждая команда находится в отдельной строке.
RibeiroBreno