heredoc для пакета Windows?

Есть ли способ указать многострочные строки в пакетном режиме аналогично heredoc в оболочках unix. Что-то похожее:

cat <<EOF > out.txt
bla
bla
..
EOF

Идея состоит в том, чтобы создать собственный файл из файла шаблона ..


person Amro    schedule 18.06.2009    source источник


Ответы (17)


Насколько я знаю, нет.

Самое близкое, что я знаю, это

> out.txt (
    @echo.bla
    @echo.bla
    ...
)

(@ не позволяет самой командной оболочке печатать команды, которые она выполняет, а echo. позволяет начинать строку с пробела.)

person ephemient    schedule 18.06.2009
comment
Вы можете перейти к другой команде, см. Мой пост ниже. Однако за лучшую технику все же заслуга ephemient. - person Michael Erickson; 01.11.2017

Вот еще один подход.

@echo off

:: ######################################################
:: ## Heredoc syntax:                                  ##
:: ## call :heredoc uniqueIDX [>outfile] && goto label ##
:: ## contents                                         ##
:: ## contents                                         ##
:: ## contents                                         ##
:: ## etc.                                             ##
:: ## :label                                           ##
:: ##                                                  ##
:: ## Notes:                                           ##
:: ## Variables to be evaluated within the heredoc     ##
:: ## should be called in the delayed expansion style  ##
:: ## (!var! rather than %var%, for instance).         ##
:: ##                                                  ##
:: ## Literal exclamation marks (!) and carats (^)     ##
:: ## must be escaped with a carat (^).                ##
:: ######################################################



:--------------------------------------------
: calling heredoc with results sent to stdout
:--------------------------------------------

call :heredoc stickman && goto next1

\o/
 | This is the "stickman" heredoc, echoed to stdout.
/ \
:next1



:-----------------------------------------------------------------
: calling heredoc containing vars with results sent to a text file
:-----------------------------------------------------------------

set bodyText=Hello world!
set lipsum=Lorem ipsum dolor sit amet, consectetur adipiscing elit.

call :heredoc html >out.txt && goto next2
<html lang="en">
    <body>
        <h3>!bodyText!</h3>
        <p>!lipsum!</p>
    </body>
</html>

Thus endeth the heredoc.  :)
:next2



echo;
echo Does the redirect to a file work?  Press any key to type out.txt and find out.
echo;

pause>NUL
type out.txt
del out.txt

:: End of main script
goto :EOF

:: ########################################
:: ## Here's the heredoc processing code ##
:: ########################################
:heredoc <uniqueIDX>
setlocal enabledelayedexpansion
set go=
for /f "delims=" %%A in ('findstr /n "^" "%~f0"') do (
    set "line=%%A" && set "line=!line:*:=!"
    if defined go (if #!line:~1!==#!go::=! (goto :EOF) else echo(!line!)
    if "!line:~0,13!"=="call :heredoc" (
        for /f "tokens=3 delims=>^ " %%i in ("!line!") do (
            if #%%i==#%1 (
                for /f "tokens=2 delims=&" %%I in ("!line!") do (
                    for /f "tokens=2" %%x in ("%%I") do set "go=%%x"
                )
            )
        )
    )
)
goto :EOF

Пример вывода:

C:\Users\oithelp\Desktop>heredoc

\o/
 | This is the "stickman" heredoc, echoed to stdout.
/ \

Does the redirect to a file work?  Press any key to type out.txt and find out.

<html lang="en">
    <body>
        <h3>Hello world!</h3>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
    </body>
</html>

Thus endeth the heredoc.  :)
person Community    schedule 22.02.2013
comment
+1, два небольших улучшения. используйте echo( вместо echo;, иначе произойдет сбой со строкой, содержащей /?. Удалите номер строки с помощью set "line=!line:*:=!", как с delims =: вы также удалите все начальные двоеточия - person jeb; 01.03.2013
comment
Однозначно, это (на мой взгляд) лучшее решение для heredoc в пакетных файлах. Я хотел бы добавить несколько комментариев и предложить некоторые улучшения кода, но режим комментариев не позволяет этого. Если вас интересуют мои предложения - см. Мой пост stackoverflow.com/a/29329912/3627676 - person jsxt; 29.03.2015
comment
Посмотрите еще более элегантную реализацию, в которой используется PrintHere.bat, по адресу stackoverflow.com/a/31216893/1012053 - person dbenham; 04.07.2015

Да, вполне возможно. ^ - это буквальный escape-символ, просто поместите его перед новой строкой. В этом примере я также добавляю дополнительную строку новой строки, чтобы она правильно печаталась в файле:

@echo off
echo foo ^

this is ^

a multiline ^

echo > out.txt

Вывод:

E:\>type out.txt
foo
 this is
 a multiline
 echo

E:\>
person esac    schedule 19.06.2009

На DosTips siberia-man разместил демонстрацию удивительного поведения ошибочного GOTO выписка в виде (goto) 2>nul. Затем Аачини и Джеб задокументировали некоторые дополнительные интересные открытия о странном поведении. Он в основном ведет себя как EXIT /B, за исключением того, что он позволяет выполнять объединенные команды в рамках процедуры CALLed в контексте родительского вызывающего объекта.

Вот краткий сценарий, демонстрирующий большинство основных моментов:

@echo off
setlocal enableDelayedExpansion
set "var=Parent Value"
(
  call :test
  echo This and the following line are not executed
  exit /b
)
:break
echo How did I get here^^!^^!^^!^^!
exit /b

:test
setlocal disableDelayedExpansion
set "var=Child Value"
(goto) 2>nul & echo var=!var! & goto :break
echo This line is not executed

:break
echo This line is not executed

-- ВЫВОД --

var=Parent Value
How did I get here!!!!

Это удивительное поведение позволило мне написать элегантную пакетную эмуляцию здесь документа со многими опциями, доступными для unix. Я реализовал PrintHere.bat как отдельную утилиту, которую нужно поместить в папку, указанную в вашем PATH. Тогда любой пакетный сценарий может легко ВЫЗВАТЬ эту утилиту, чтобы получить здесь функциональные возможности документа.

Вот общий синтаксис использования:

call PrintHere :Label
Here doc text goes here
:Label

Как этого можно достичь? ... Моя утилита PrintHere дважды использует уловку (GOTO) 2>nul.

  • В первый раз я использую (GOTO) 2>nul, чтобы вернуться к вызывающему, чтобы я мог получить полный путь к вызывающему сценарию, чтобы PrintHere знал, из какого файла читать. Затем я ЗВОНИТЕ Print Here во второй раз!

  • Во второй раз я использую (GOTO) 2>nul для возврата к вызывающему и GOTO завершающую метку, чтобы текст документа здесь не выполнялся.

Примечание. Приведенный ниже сценарий содержит символ табуляции (0x09) в определении табуляции непосредственно под меткой :start. В некоторых браузерах могут возникнуть проблемы с отображением и копированием вкладки. В качестве альтернативы вы можете загрузить PrintHere.bat.txt из моего Dropbox и просто переименуйте его в PrintHere.bat.

Первоначально я разместил PrintHere.bat на DosTips, где вы можете отслеживать дальнейшее развитие.

PrintHere.bat

@echo off & setlocal disableDelayedExpansion & goto :start
::PrintHere.bat version 1.1 by Dave Benham
:::
:::call PrintHere [/E] [/- "TrimList"] :Label ["%~f0"]
:::call PrintHere [/E] [/- "TrimList"] :Label "%~f0" | someCommand & goto :Label
:::PrintHere /?
:::PrintHere /V
:::
:::  PrintHere.bat provides functionality similar to the unix here doc feature.
:::  It prints all content between the CALL PrintHere :Label line and the
:::  terminating :Label. The :Label must be a valid label supported by GOTO, with
:::  the additional constraint that it not contain *. Lines are printed verbatim,
:::  with the following exceptions and limitations:
:::
:::    - Lines are lmited to 1021 bytes long
:::    - Trailing control characters are stripped from each line
:::
:::  The code should look something like the following:
:::
:::     call PrintHere :Label
:::         Spacing    and blank lines are preserved
:::
:::     Special characters like & < > | ^ ! % are printed normally
:::     :Label
:::
:::  If the /E option is used, then variables between exclamation points are
:::  expanded, and ! and ^ literals must be escaped as ^! and ^^. The limitations
:::  are different when /E is used:
:::
:::    - Lines are limited to ~8191 bytes long
:::    - All characters are preserved, except !variables! are expanded and ^! and
:::      ^^ are transformed into ! and ^
:::
:::  Here is an example using /E:
:::
:::     call PrintHere /E :SubstituteExample
:::       Hello !username!^!
:::     :SubstituteExample
:::
:::  If the /- "TrimList" option is used, then leading "TrimList" characters
:::  are trimmed from the output. The trim characters are case sensitive, and
:::  cannot include a quote. If "TrimList" includes a space, then it must
:::  be the last character in the list.
:::
:::  Multiple PrintHere blocks may be defined within one script, but each
:::  :Label must be unique within the file.
:::
:::  PrintHere must not be used within a parenthesized code block.
:::
:::  Scripts that use PrintHere must use \r\n for line termination, and all lines
:::  output by PrintHere will be terminated by \r\n.
:::
:::  All redirection associated with a PrintHere must appear at the end of the
:::  command. Also, the CALL can include path information:
:::
:::     call "c:\utilities\PrintHere.bat" :MyBlock>test.txt
:::       This line is written to test.txt
:::     :MyBlock
:::
:::  PrintHere may be used with a pipe, but only on the left side, and only
:::  if the source script is included as a 2nd argument, and the right side must
:::  explicitly and unconditionally GOTO the terminating :Label.
:::
:::     call PrintHere :PipedBlock "%~f0" | more & goto :PipedBlock
:::       text goes here
:::     :PipedBlock
:::
:::  Commands concatenated after PrintHere are ignored. For example:
:::
:::     call PrintHere :ignoreConcatenatedCommands & echo This ECHO is ignored
:::       text goes here
:::     :ignoreConcatenatedCommands
:::
:::  PrintHere uses FINDSTR to locate the text block by looking for the
:::  CALL PRINTHERE :LABEL line. The search string length is severely limited
:::  on XP. To minimize the risk of PrintHere failure when running on XP, it is
:::  recommended that PrintHere.bat be placed in a folder included within PATH
:::  so that the utility can be called without path information.
:::
:::  PrintHere /? prints out this documentation.
:::
:::  PrintHere /V prints out the version information
:::
:::  PrintHere.bat was written by Dave Benham. Devlopment history may be traced at:
:::    http://www.dostips.com/forum/viewtopic.php?f=3&t=6537
:::

:start
set "tab=   "   NOTE: This value must be a single tab (0x09), not one or more spaces
set "sp=[ %tab%=,;]"
set "sp+=%sp%%sp%*"
set "opt="
set "/E="
set "/-="

:getOptions
if "%~1" equ "" call :exitErr Invalid call to PrintHere - Missing :Label argument
if "%~1" equ "/?" (
  for /f "tokens=* delims=:" %%L in ('findstr "^:::" "%~f0"') do echo(%%L
  exit /b 0
)
if /i "%~1" equ "/V" (
  for /f "tokens=* delims=:" %%L in ('findstr /rc:"^::PrintHere\.bat version" "%~f0"') do echo(%%L
  exit /b 0
)
if /i %1 equ /E (
  set "/E=1"
  set "opt=%sp+%.*"
  shift /1
  goto :getOptions
)
if /i %1 equ /- (
  set "/-=%~2"
  set "opt=%sp+%.*"
  shift /1
  shift /1
  goto :getOptions
)
echo %1|findstr "^:[^:]" >nul || call :exitErr Invalid PrintHere :Label
if "%~2" equ "" (
  (goto) 2>nul
  setlocal enableDelayedExpansion
  if "!!" equ "" (
    endlocal
    call %0 %* "%%~f0"
  ) else (
    >&2 echo ERROR: PrintHere must be used within a batch script.
    (call)
  )
)
set ^"call=%0^"
set ^"label=%1^"
set "src=%~2"
setlocal enableDelayedExpansion
set "call=!call:\=[\\]!"
set "label=!label:\=[\\]!"
for %%C in (. [ $ ^^ ^") do (
  set "call=!call:%%C=\%%C!"
  set "label=!label:%%C=\%%C!"
)
set "search=!sp!*call!sp+!!call!!opt!!sp+!!label!"
set "cnt="
for /f "delims=:" %%N in ('findstr /brinc:"!search!$" /c:"!search![<>|&!sp:~1!" "!src!"') do if not defined skip set "skip=%%N"
if not defined skip call :exitErr Unable to locate CALL PrintHere %1
for /f "delims=:" %%N in ('findstr /brinc:"!sp!*!label!$" /c:"!sp!*!label!!sp!" "!src!"') do if %%N gtr %skip% if not defined cnt set /a cnt=%%N-skip-1
if not defined cnt call :exitErr PrintHere end label %1 not found
if defined /E (
  for /f "skip=%skip% delims=" %%L in ('findstr /n "^^" "!src!"') do (
    if !cnt! leq 0 goto :break
    set "ln=%%L"
    if not defined /- (echo(!ln:*:=!) else for /f "tokens=1* delims=%/-%" %%A in (^""%/-%!ln:*:=!") do (
      setlocal disableDelayedExpansion
      echo(%%B
      endlocal
    )
    set /a cnt-=1
  )
) else (
  for /l %%N in (1 1 %skip%) do set /p "ln="
  for /l %%N in (1 1 %cnt%) do (
    set "ln="
    set /p "ln="
    if not defined /- (echo(!ln!) else for /f "tokens=1* delims=%/-%" %%A in (^""%/-%!ln!") do (
      setlocal disableDelayedExpansion
      echo(%%B
      endlocal
    )
  )
) <"!src!"
:break
(goto) 2>nul & goto %~1


:exitErr
>&2 echo ERROR: %*
(goto) 2>nul & exit /b 1

Полная документация встроена в сценарий. Ниже приведены некоторые демонстрации использования:

Дословный вывод

@echo off
call PrintHere :verbatim
    Hello !username!^!
    It is !time! on !date!.
:verbatim

-- ВЫВОД --

    Hello !username!^!
    It is !time! on !date!.


Развернуть переменные (отключение отложенного раскрытия не требуется)

@echo off
call PrintHere /E :Expand
    Hello !username!^!
    It is !time! on !date!.
:Expand

--ВЫВОД--

    Hello Dave!
    It is 20:08:15.35 on Fri 07/03/2015.


Расширить переменные и обрезать ведущие пробелы

@echo off
call PrintHere /E /- " " :Expand
    Hello !username!^!
    It is !time! on !date!.
:Expand

--ВЫВОД--

Hello Dave!
It is 20:10:46.09 on Fri 07/03/2015.


Вывод можно перенаправить в файл

@echo off
call PrintHere :label >helloWorld.bat
  @echo Hello world!
:label


Вывод не может быть перенаправлен как ввод, но его можно передать по конвейеру! К сожалению, синтаксис не такой элегантный, потому что обе стороны канала выполняются в новом процессе CMD.EXE, поэтому (GOTO) 2>nul возвращается к дочернему процессу cmd, а не к основному сценарию.

@echo off
call PrintHere :label "%~f0" | findstr "^" & goto :label
  Text content goes here
:label
person dbenham    schedule 04.07.2015
comment
очень интересно, спасибо. Я не эксперт по пакетам, так что признаю, что большая часть этого нереальна;) - person Amro; 04.07.2015
comment
@Amro - Возможно, слишком сложно понять, как это работает, но вам не нужно понимать это, чтобы использовать его ;-) Я разработал эту утилиту для простоты использования. - person dbenham; 04.07.2015
comment
Если вы хотите доставить одну летучую мышь, содержащую PrintHere, вместе с основным скриптом, будет ли она работать с использованием помеченных вызовов, то есть call :PrintHere :endLabel? - person Sam Hasler; 01.02.2016
comment
@SamHasler - Да, он отлично работает, как написано. Вы можете удалить документацию, и тогда GOTO :START не понадобится. Вы также можете избавиться от @ECHO OFF. - person dbenham; 06.04.2016

Использование макроса с параметрами позволяет напишите «heredoc» более простым способом:

@echo off

rem Definition of heredoc macro
setlocal DisableDelayedExpansion
set LF=^


::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
set heredoc=for %%n in (1 2) do if %%n==2 (%\n%
       for /F "tokens=1,2" %%a in ("!argv!") do (%\n%
          if "%%b" equ "" (call :heredoc %%a) else call :heredoc %%a^>%%b%\n%
          endlocal ^& goto %%a%\n%
       )%\n%
    ) else setlocal EnableDelayedExpansion ^& set argv=


rem Heredoc syntax:
rem
rem %%heredoc%% :uniqueLabel [outfile]
rem contents
rem contents
rem ...
rem :uniqueLabel
rem
rem Same notes of rojo's answer apply

rem Example borrowed from rojo's answer:

set bodyText=Hello world!
set lipsum=Lorem ipsum dolor sit amet, consectetur adipiscing elit.

%heredoc% :endHtml out.txt
<html lang="en">
    <body>
        <h3>!bodyText!</h3>
        <p>!lipsum!</p>
    </body>
</html>
:endHtml

echo File created:
type out.txt
del out.txt
goto :EOF


rem Definition of heredoc subroutine

:heredoc label
set "skip="
for /F "delims=:" %%a in ('findstr /N "%1" "%~F0"') do (
   if not defined skip (set skip=%%a) else set /A lines=%%a-skip-1
)
for /F "skip=%skip% delims=" %%a in ('findstr /N "^" "%~F0"') do (
   set "line=%%a"
   echo(!line:*:=!
   set /A lines-=1
   if !lines! == 0 exit /B
)
exit /B
person Aacini    schedule 14.04.2014

@jeb

setlocal EnableDelayedExpansion
set LF=^


REM Two empty lines are required

другой вариант:

@echo off

:)
setlocal enabledelayedexpansion
>nul,(pause&set /p LF=&pause&set /p LF=)<%0
set LF=!LF:~0,1!

echo 1!LF!2!LF!3

pause
person walid2mi    schedule 18.08.2011
comment
+1, очень приятно увидеть альтернативный способ создания LF и эффект команды паузы - person jeb; 18.08.2011

Ссылаясь на сообщение Рохо по адресу https://stackoverflow.com/a/15032476/3627676

Определенно, его решение - это то, что я ищу в течение некоторого времени (конечно, я мог бы попытаться реализовать что-то подобное, но лень движет прогрессом :)). Одна вещь, которую я хотел бы добавить, - это небольшое улучшение исходного кода. Я подумал, что будет лучше, если перенаправление в файл будет написано в конце строки. В этом случае стартовая линия heredoc может быть строже, а ее анализ проще.

@echo off

set "hello=Hello world!"
set "lorem=Lorem ipsum dolor sit amet, consectetur adipiscing elit."

call :heredoc HTML & goto :HTML
<html>
<title>!hello!</title>
<body>
<p>Variables in heredoc should be surrounded by the exclamation mark (^!).</p>
<p>!lorem!</p>
<p>Exclamation mark (^!) and caret (^^) MUST be escaped with a caret (^^).</p>
</body>
</html>
:HTML

goto :EOF

:: https://stackoverflow.com/a/15032476/3627676
:heredoc LABEL
setlocal enabledelayedexpansion
set go=
for /f "delims=" %%A in ( '
    findstr /n "^" "%~f0"
' ) do (
    set "line=%%A"
    set "line=!line:*:=!"

    if defined go (
        if /i "!line!" == "!go!" goto :EOF
        echo:!line!
    ) else (
        rem delims are ( ) > & | TAB , ; = SPACE
        for /f "tokens=1-3 delims=()>&| ,;= " %%i in ( "!line!" ) do (
            if /i "%%i %%j %%k" == "call :heredoc %1" (
                set "go=%%k"
                if not "!go:~0,1!" == ":" set "go=:!go!"
            )
        )
    )
)
goto :EOF

Что я предлагаю этим кодом? Давайте рассмотрим.

Кодекс Рохо очень строгий:

  • не допускает более одного символа пробела в строке между call и :heredoc
  • call :heredoc прикрепляется к краю строки (в начале строки не допускаются пробелы)
  • перенаправление в файл разрешено где-то внутри строки (не очень полезно) -

Я предлагаю:

  • менее строгий (более одного пробела в качестве разделителя)
  • перенаправление в файл разрешено только в конце строки (круглые скобки разрешены и необходимы)
  • нет липкого кода на краю строки

Обновление 1: улучшена проверка и выполнение начала heredoc:

  • важная команда - это только call :heredoc LABEL или call :heredoc :LABEL. Таким образом, после печати содержимого heredoc можно перейти к другой метке, концу скрипта или запустить exit /b.
  • удалена неиспользуемая и ненужная команда goto: next2

Обновление 2:

  • Разделители для внутреннего for: ( ) > & | TAB , ; = SPACE
  • Переключатель /I добавлен к if

Обновление 3:

По следующей ссылке вы можете найти полную версию автономного скрипта (доступно встраивание в ваши скрипты) https://github.com/ildar-shaimordanov/cmd.scripts/blob/master/heredoc.bat

person jsxt    schedule 29.03.2015

Вы можете создать цитируемый блок текста с помощью цикла FOR / F, поэтому вам не нужно экранировать специальные символы, такие как <>|&, только % нужно экранировать.
Иногда это полезно, например, при создании вывода html.

@echo off
setlocal EnableDelayedExpansion
set LF=^


REM Two empty lines are required
set ^"NL=^^^%LF%%LF%^%LF%%LF%^^"

for /F "tokens=* delims=_" %%a in (^"%NL%
___"One<>&|"%NL%
___"two 100%%"%NL%
___%NL%
___"three "quoted" "%NL%
___"four"%NL%
") DO (
   @echo(%%~a
)

Вывод

One<>&|
two 100%

three "quoted"
four

Я пытаюсь объяснить код. Переменная LF содержит один символ новой строки, переменная NL содержит ^<LF><LF>^.
Это можно использовать с процентным расширением, чтобы разместить ОДИН символ новой строки и одну вставку в конце строки.

Обычно FOR / F разбивает цитируемый текст на несколько токенов, но только один раз.
Когда я вставляю символы новой строки, цикл FOR также разбивается на несколько строк.
Цитата в первой и последней строке - это только для создания правильного синтаксиса цикла FOR.

В начале любой строки стоит _, так как первый символ будет экранирован из многострочной вставки предыдущей строки, и если кавычка является первым символом, она теряет возможность выхода.
В качестве пробелов используются разделители _. или запятые вызывают проблемы с XP (иначе XP-Bug ложно пытается получить доступ к именам файлов мусора).

Каретка в конце строки также только против XP-Bug.

Ошибка XP возникает, когда цитируемый текст содержит ,;=<space> символов без кавычек.

for /f "tokens=*" %%a in ("a","b","c") do echo %%a
person jeb    schedule 18.08.2011
comment
+1 спасибо. Мне сложно следить за этим, я буду очень признателен за дальнейшие объяснения .. - person Amro; 18.08.2011

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

(
    @echo.line1
    @echo.line2 %time% %os%
    @echo.
    @echo.line4
) | more

В посте ephemient он перенаправляет в начале, что является приятным стилем, но вы также можете перенаправить в конце как таковой:

(
    @echo.line1
    @echo.line2 %time% %os%
    @echo.
    @echo.line4
) >C:\Temp\test.txt

Обратите внимание, что "@echo." никогда не включается в вывод и "@echo." сам по себе дает пустую строку.

person Michael Erickson    schedule 01.11.2017

В makefile Microsoft NMake можно использовать настоящие UNIX heredocs, как их просил владелец потока. Например, это явное правило для создания файла Deploy.sed:

Deploy.sed:
    type << >$@
; -*-ini-generic-*-
;
; Deploy.sed -- Self-Extracting Directives
;

[Version]
Class=IEXPRESS
SEDVersion=3
    .
    .
[Strings]
InstallPrompt=Install $(NAME)-$(VERSION).xll to your personal XLSTART directory?
DisplayLicense=H:\prj\prog\XLL\$(NAME)\README.txt
    .
    .
<<

clean:
    -erase /Q Deploy.sed

где ‹< заменяется на временное имя файла, которое NMake создает на лету при выполнении правила. То есть, когда Deploy.sed не существует. Приятно то, что переменные NMake тоже расширяются (здесь переменные NAME и VERSION). Сохраните это как makefile. Откройте окно DOS в каталоге makefile и используйте:

> nmake Deploy.sed

для создания файла и:

> nmake clean

чтобы удалить это. NMake входит в состав всех версий Visual Studio C ++, включая Express-редакции.

person Andreas Spindler    schedule 16.08.2011
comment
+1 Всегда хотел выучить NMake. Он сильно отличается от обычного GNU Make (у меня установлены Cygwin и MinGW)? - person Amro; 17.08.2011
comment
Ему 20 с лишним лет, и мне кажется, что Microsoft не продвинулась дальше, так как отказалась от экспорта файлов сборки из Visual Studio. GNU make, с другой стороны, стал довольно совершенным и мощным. Make-файлы - отличные инструменты при работе из командной строки. Часто они могут заменять сценарии оболочки / пакетные команды. Я использую make-файлы на любой платформе; в Windows: GNU make и NMake от Cygwin. Я благодарен, что они все еще поставляют его с графическим замком Visual Studio 2010. - person Andreas Spindler; 17.08.2011
comment
Я думаю, он был заменен основанным на XML MSBuild - person Amro; 17.08.2011
comment
Да, было. Чтобы ответить на исходный вопрос: на самом деле он не сильно отличается от GNU make ... но GNU make два десятилетия назад. Тем не менее мощный. - person Andreas Spindler; 17.08.2011
comment
(1) Возможно, я что-то упускаю, но мне кажется, что если текст заканчивается на <<, то это не «настоящий UNIX heredoc». Отличительной чертой Unix heredocs является то, что вы можете поместить в документ любой текст, потому что вы можете выбрать любую строку терминатора. Что делать, если вы хотите поместить << в файл? (2) Что такое «DOS-бокс»? Вы имеете в виду окно подсказки команды? Если так, то почему? Вопрос касается пакетного, а не интерактивного использования. (3) AUnix heredoc может быть передан любой команде; например, grep "$pat" << EOF. CanNMake heredocs dothat? Пожалуйста, дайте ссылку на документацию. - person Scott; 07.03.2020

Вот вариант отличного решения ephemient. Это позволяет вам передавать несколько строк в другую программу без фактического создания текстового файла и перенаправления ввода в вашу программу:

(@echo.bla
@echo.bla
) | yourprog.exe

Для быстрого рабочего примера вы можете заменить yourprog.exe на more:

(@echo.bla
@echo.bla
) | more

Вывод:

bla
bla
person B. Reynolds    schedule 17.08.2017

OP хотел чего-то очень конкретного (создание текстового файла с выводом), и принятый ответ делает это отлично, но представленное решение не работает хорошо вне этого конкретного контекста. Например, если я хочу передать многострочный ввод в команду, я не могу использовать синтаксис ( echo ). Вот что мне понравилось.

Дан сценарий perl с именем "echolines.pl", состоящий из следующего (для имитации "реальной" программы):

use strict;
use warnings;

while (<>) {
        chomp;
        print qq(<$_>\n);
}

и командный файл с именем "testme.bat", содержащий:

@echo off

set FOO=foo
set BAR=bar
set BAZ=baz

echo %FOO%^
&echo %BAR%^
&echo %BAZ%|perl echolines.pl

его запуск дает ожидаемый результат:

C:\>testme
<foo>
<bar>
<baz>

Следует проявлять осторожность с пробелами, чтобы все они работали правильно и нигде не было лишних пробелов. В частности: каждый конец строки должен быть символом вставки (^), за которым следует новая строка, последующие строки должны начинаться сразу с амперсанда (&), а в последней строке канал должен начинаться сразу после последнего отправляемого элемента. Несоблюдение этого правила приведет к отсутствию параметров или лишним пробелам до и после параметров.

person Joe Casadonte    schedule 22.07.2013

Попробуйте этот код. (Код JScript внизу записывает "out.html" на диск)

@if(0)==(0) echo on
cscript.exe //nologo //E:JScript "%~f0" source1 out.html
start out.html
goto :EOF

[source1]
<!DOCTYPE html>
<html>
  <head>
   title></title>
  </head>
  <body>
    <svg width="900" height="600">
        <text x="230" 
              y="150"
              font-size="100"
              fill="blue"
              stroke="gray"
              stroke-width="1">
                  Hello World              
        </text>
    </svg>
  </body>
</html>
[/source1]

@end

if (WScript.Arguments.length != 2) WScript.Quit();
var tagName = WScript.Arguments(0);
var path    = WScript.Arguments(1);
var startTag = "[" + tagName + "]"
var endTag   = "[/" + tagName + "]"
var fso = new ActiveXObject("Scripting.FileSystemObject");
var file1 = fso.OpenTextFile(WScript.ScriptFullName);
var txt = "";
var found = false;
while (!file1.AtEndOfStream) {
  var line = file1.ReadLine();
  if (!found) {
    if (line.lastIndexOf(startTag, 0) === 0) found = true;
  } else {
    if (line.lastIndexOf(endTag, 0) === 0) break;
    txt += line + "\n";
  }
}
file1.Close();
var file2 = fso.CreateTextFile(path, true, false);
file2.Write(txt);
file2.Close();
person javacommons    schedule 15.04.2017

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

Я использую цикл for для набора строк:

for %%l in (
    "This is my"
    "multi-line here document"
    "that this batch file"
    "will print!"
    ) do echo.%%~l >> here.txt
  • Не требует подпрограммы, легко запоминается и выполняется.
  • Не требует временного файла.
  • Работает внутри пакетного скрипта.
  • Работает со встроенными переменными.
  • Однако: если в ваших строках есть двойные кавычки, метод больше не работает ...

Вот еще один практический пример из сценария, над которым я сейчас работаю:

:intc_version:
for %%l in (
    "File         : %_SCRIPT_NAME%"
    "Version      : %_VERSION%"
    "Company      : %_COMPANY%"
    "License      : %_LICENSE%"
    "Description  : %_DESCRIPTION%"
    ""
) do echo.%%~l
exit /B 0
person Flandraco    schedule 16.10.2019

Это еще проще, и это очень похоже на cat ‹< EOF> out.txt:

C: \> copy con out.txt
Это моя первая строка текста.
Это моя последняя строка текста.
^ Z
1 файл (ы) скопирован.

Результат выглядит так:

C: \> type out.txt
Это моя первая строка текста.
Это моя последняя строка текста.

(скопируйте con + out.txt, введите свой ввод, затем Ctrl-Z, и файл будет скопирован)

COPY CON означает «копировать с консоли» (принимать пользовательский ввод)

person Community    schedule 20.08.2009
comment
это будет работать с консоли, но как насчет командного файла? - person Amro; 22.08.2009

C:\>more >file.txt
This is line 1 of file
This is line 2 of file
^C

C:\>type file.txt
This is line 1 of file
This is line 2 of file

** В конце будет добавлена ​​пустая строка, но вы можете легко решить эту проблему, просто используя метод copy con:

C:\>copy con file.txt >nul
This is line 1 of file
This is line 2 of file^Z

C:\>type file.txt
This is line 1 of file
This is line 2 of file

Будьте осторожны, когда вы набираете ^ C и ^ Z в каждом случае.

person jgodoy    schedule 17.11.2015
comment
Возможно, я что-то упускаю, но я не понимаю, как это отвечает на вопрос. Если да, пожалуйста, поясните. - person blm; 17.11.2015

person    schedule
comment
Это отличное начало. Дополнительные сведения о том, как это сделать, см. На странице stackoverflow.com/q/7885437/745412. - person Jared; 29.10.2011
comment
Удивительно, у меня это сработало! Единственная настройка, которую мне пришлось сделать, - это добавить SETLOCAL DISABLEDELAYEDEXPANSION перед циклом for (в моем выходном файле отсутствовали восклицательные знаки из-за того, что ранее было включено отложенное расширение). Он по-прежнему удаляет пробелы в начале строк в выходном файле, но я возьму его. - person Shoelaced; 17.02.2018
comment
(1) Соотношение кода и объяснения в этом ответе бесконечно (это плохо). Это слишком загадочно, чтобы считаться очевидным, самодокументированным или не требующим пояснений. (2) Я не пытался запустить весь этот командный файл (я не запускал ненадежный код из ненадежных источников), но несбалансированный ( в строке 5 заставляет меня задуматься, будет ли он работать вообще. (3) Я действительно попробовал goto: EOF, и, как я и ожидал, ничего не вышло. Он должен быть goto :EOF или goto:EOF. - person Scott; 07.03.2020