当前位置:  开发笔记 > 编程语言 > 正文

heredoc for Windows批处理?

如何解决《heredocforWindows批处理?》经验,为你挑选了6个好方法。

有没有一种方法可以批量指定多行字符串,类似于unix shell中的heredoc.类似的东西:

cat < out.txt
bla
bla
..
EOF

我们的想法是从模板文件创建自定义文件.



1> ephemient..:

不是我所知道的.

我最了解的是

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

(@防止命令shell本身打印它正在运行的命令,并echo.允许您使用空格开始一行.)


是否有解决方案将内容传递给另一个命令?

2> rojo..:

这是另一种方法.

@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

    
        

!bodyText!

!lipsum!

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


    
        

Hello world!

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Thus endeth the heredoc. :)


+1,两个小的改进.使用`echo(`而不是`echo;`否则它会失败并带有一个包含`/?`的行.使用`set"line =!line:*:=!"`删除行号,与delims =一样:你也删除了所有的冒号

3> esac..:

是的,很有可能.^是文字转义字符,只需将它放在换行符之前.在这个例子中,我还添加了额外的换行符,以便在文件中正确打印:

@echo off
echo foo ^

this is ^

a multiline ^

echo > out.txt

输出:

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

E:\>



4> walid2mi..:
@echo off
 for /f "delims=:" %%a in (
     'findstr -n "^___" %0') do set "Line=%%a"

 (for /f "skip=%Line% tokens=* eol=_" %%a in (
       'type %0') do echo(%%a) > out.html
:: out.html
pause
goto: EOF



___DATA___


  
   title>
  
  
    
        
                  Hello World              
        
    
  



5> dbenham..:

随着在DosTips,西伯利亚人发布的演示错误GOTO语句的惊人表现的形式(goto) 2>nul.然后Aacini和jeb记录了一些关于奇怪行为的其他有趣发现.它基本上表现得像一个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中列出的文件夹中.然后,任何批处理脚本都可以轻松调用该实用程序以获取此处的doc功能.

以下是一般用法语法:

call PrintHere :Label
Here doc text goes here
:Label

怎么可能实现这个?...我的PrintHere实用程序使用了(GOTO) 2>nul两次技巧.

我第一次使用(GOTO) 2>nul返回调用者所以我可以获得调用脚本的完整路径,以便PrintHere知道要读取的文件.然后我再次打印PrintHere!

我第二次使用(GOTO) 2>nul返回调用者和GOTO终止标签,以便不执行此处的doc文本.

注意 - 下面的脚本在标签的定义中包含一个制表符(0x09),位于标签的正下方:start.某些浏览器可能难以显示和复制选项卡.作为替代方案,您可以从我的保管箱下载PrintHere.bat.txt,只需将其重命名为PrintHere.bat即可.

我最初在DosTips上发布了PrintHere.bat,您可以在其中跟踪未来的发展.

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

--OUTPUT--

    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

--OUTPUT--

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



6> Aacini..:

使用带参数的宏允许以更简单的方式编写"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

    
        

!bodyText!

!lipsum!

: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

推荐阅读
wurtjq
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有