Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Batch script, re-formatting output of a cli application

I have a very small batch script which is extracting a quite amount of files. The script is meant to be delivered with the compressed data to other users.

Now my problem is that this compression tool is outputting a ton of data into the cmd window. I think this will confuse a lot of useser because the output is really "running". It basically shows a percentage with each line and how it decompressed at which speed (CPU and HDD). A lot of confusing data that no one needs to see. Now I don't really like suppressing all the output of the program, giving the user feedback on how far the decompression already got would be important in my opinion. So is it possible to redirect the output and read just the first three digits of that output and deliver that to the users in a single line? So the users only sees an advancing percantage (in one line) and not 20 new lines every second with all this data?

Here an example of how it looks at the moment: https://i.sstatic.net/guTYP.png The compression tool is SREP, my OS Win 7 x64.

like image 569
timonsku Avatar asked Aug 31 '26 17:08

timonsku


2 Answers

If you use windows batch, it can be done, but it's not simple, as you would normally do this with a FOR/F-Loop.

Like for /f "delims=" %%a in (7z packit ...) do ...
The problem is here, that the for-loop will first collect all data and wait for the end of 7z before it process any line of the output.

The only way is to redirect the output, and to scan it simultaneously.
But to do that you need a second thread (at best in the same cmd-window).

Something like this would do the job

@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
like image 180
jeb Avatar answered Sep 03 '26 23:09

jeb


Here is a simple hybrid batch/JScript script that I think will do what you want.

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();

Usage:

yourCommand | show3

The script could be simplified to pure JScript, but then it won't be as convenient to use:

show3.js

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

Usage:

yourCommand | cscript //nologo show3.js

EDIT As jeb commented, you should not need any redist to use this solution.

I've taken some of the concepts in jeb's answer and combined the entire process into one hybrid script. No need for a standalone "show3" file.

@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 would be whatever compression command you are using. Based on your comments, it sounds like you might have to use yourCommand 2>&1 if the output you want is printing to stderr instead of stdout.

I've created a "yourCommand.bat" file for testing purposes. It crudely emulates the output behavior you describe for your compression program.

@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
)

Finally, if you really want a pure batch solution, I greatly simplified jeb's solution. I eliminated the temp file and used a pipe instead.

@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

Edit - I modified the EOF test to ignore any leading or trailing spaces. This should make the code more robust.

like image 37
dbenham Avatar answered Sep 04 '26 00:09

dbenham



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!