批处理脚本,重新格式化cli应用程序的输出

时间:2012-05-08 18:05:08

标签: batch-file formatting

我有一个非常小的批处理脚本,它提取了大量的文件。 该脚本旨在与压缩数据一起提供给其他用户。

现在我的问题是这个压缩工具正在向cmd窗口输出大量数据。 我认为这会混淆很多使用者因为输出真的“正在运行”。它基本上显示每行的百分比以及它以何种速度(CPU和HDD)解压缩。 许多令人困惑的数据,没有人需要看到。现在我真的不喜欢压缩程序的所有输出,在我看来,给用户反馈解压已经到达的程度是多么重要。 那么是否可以重定向输出并只读取该输出的前三位数并将其传递给用户一行?因此,用户只能看到一个前进的优势(在一行中),而不是每秒看到20个新行所有这些数据?

这是一个关于当下情况的例子: http://i.imgur.com/5w5LH.png 压缩工具是SREP,我的操作系统Win 7 x64。

2 个答案:

答案 0 :(得分:5)

如果您使用Windows批处理,可以完成,但这并不简单,因为您通常使用FOR / F-Loop执行此操作。

for /f "delims=" %%a in ( 7z packit ... ) do ...
问题在于,for循环将首先收集所有数据并在处理输出的任何行之前等待7z的结束。

唯一的方法是重定向输出,并同时扫描它 但要做到这一点,你需要第二个线程(最好在同一个cmd窗口中)。

这样的事情可以胜任

@echo off
echo start
setlocal EnableDelayedExpansion
if "%1"=="internal" goto :readThread
start /b cmd /c ""%~f0" internal"

rem *** myPacker pack here and redirect the output
del archive.7z
del output.tmp
start "title" /b /wait cmd /c  "%ProgramFiles%\7-Zip\7z" u  archive \tempx\*.rtf \tempx\*.pdf ^> output.tmp
echo EOF>>output.tmp
echo ENDE
:waitForEnd
(
> lock (
  rem
) || goto :waitForEnd
) 2> nul
exit /b

:readThread

for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
echo ####!cr!x
echo(
<output.tmp ( 
    echo ## before
    call :readData 2> lock
    echo after
)
exit /b

:readData
set "var="
set /p var=
if "!var!"=="EOF" exit /b
if defined var (
  <nul set /p ".=Processing files, currently at !var:~0,4!!CR!"
)
goto :readData

答案 1 :(得分:4)

这是一个简单的混合批处理/ JScript脚本,我认为它会做你想要的。

<强> show3.bat

@if (@X)==(@Y) @end /* harmless hybrid line that begins a JScrpt comment

::: Batch part ::::
@cscript //nologo //e:JScript "%~f0"
@exit /b

*** JScript part ***/
while( !WScript.StdIn.AtEndOfStream ) {
  WScript.StdOut.Write( '\x08\x08\x08' + WScript.StdIn.ReadLine().substr(0,3) );
}
WScript.StdOut.WriteLine();

用法:

yourCommand | show3

脚本可以简化为纯JScript,但是使用起来不方便:

<强> show3.js

while( !WScript.StdIn.AtEndOfStream ) {
  WScript.StdOut.Write( '\x08\x08\x08' + WScript.StdIn.ReadLine().substr(0,3) );
}
WScript.StdOut.WriteLine();

用法:

yourCommand | cscript //nologo show3.js

编辑 正如jeb评论的那样,您不需要任何redist来使用此解决方案。

我在jeb的答案中采用了一些概念,并将整个过程合并为一个混合脚本。不需要独立的“show3”文件。

@if (@X)==(@Y) @end /* harmless hybrid line that begins a JScrpt comment

:: ***** Batch part ******
@echo off

REM whatever batch code you need goes here

yourCommand | cscript //nologo //e:JScript "%~f0"

REM whatever batch code you need goes here

exit /b

****** JScript part ******/
while( !WScript.StdIn.AtEndOfStream ) {
  WScript.StdOut.Write( '\x08\x08\x08' + WScript.StdIn.ReadLine().substr(0,3) );
}
WScript.StdOut.WriteLine();

yourCommand将是您正在使用的压缩命令。根据您的评论,如果您想要的输出打印到stderr而不是stdout,则可能需要使用yourCommand 2>&1

我已经创建了一个“yourCommand.bat”文件用于测试目的。它粗略地模拟了您为压缩程序描述的输出行为。

@echo off
for /l %%A in (1 1 100) do (
  echo %%A   "I don't want to see this quoted text"
  for /l %%B in (1 1 50000) do rem
)

最后,如果你真的想要一个纯粹的批量解决方案,我大大简化了jeb的解决方案。我删除了临时文件并使用了管道。

@echo off
if "%~1"==":show3" goto :show3
REM whatever batch code you need goes here

(yourCommand & echo EOF) | "%~f0" :show3

REM whatever batch code you need goes here
exit /b

:show3
setlocal enableDelayedExpansion
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
:read
set "ln="
set /p "ln="
if not defined ln goto :read
for /f "tokens=1* delims= " %%A in ("!ln!") do if "%%A%%B" equ "EOF" (
  echo(
  exit /b
)
<nul set /p "=!ln:~0,3!   !cr!"
goto :read

编辑 - 我修改了EOF测试以忽略任何前导或尾随空格。这应该使代码更健壮。