Acid
Acid

Reputation: 33

How to pipe the final result of a windows batch for loop into another command

have my script splitted into two batch files as i dont know how to combine it:

short so you get an idea:

1.bat : calls 2.bat with args, sorts its output and makes lines unique

FOR /F ... (2.bat %1 ^| sort) DO (...)

the do part compares lines to each other and excludes identical lines using a linebuffer

2.bat : prints strings as a result of an FOR loop

FOR ... DO ECHO

in bash this might look like this: (piping is easy)

(command) | while read line ; do echo $line ; done | sort | uniq

i really dont know how to process the final output of a FOR loop in windows batch to use its result with another loop (without using temp files for result buffering)

This is the whole code with jeb's solution applied:

@echo off
setlocal EnableDelayedExpansion

REM *** This "goto" to a label, if there is one embedded in %~0 -
FOR /F "delims=: tokens=3" %%L in ("%~0") do goto :%%L

REM *** The second part calls this batch file, but embedd a label to be called
FOR /F "usebackq" %%A IN (`echo dummy ^| call %~d0\:main:\..\%~pnx0 %1 ^| SORT`) DO (
    IF NOT "%%A"=="!buff!" SET str=!str!%%A
    SET buff=%%A
)
ECHO %str:.dll=;%
endlocal
GOTO :eof

REM *** This function will be called in the pipe, but runs in batch context
:main
CD %1
:sub
FOR %%A IN (*.dll *.exe) DO dumpbin /DEPENDENTS %%A | FINDSTR /I "DLL" | FINDSTR /V "Dump :"
FOR /D %%B IN (*) DO (
    CD %%B
    CALL :sub
    CD..
)
GOTO :eof

it returns a single line with runtime dependencies of all files inside a folder [arg1] and all its subfolders : the .dll part of the output string is stripped to suit my needs

ADVAPI32;COMCTL32;COMDLG32;GDI32;KERNEL32;ole32;SHELL32;USER32;WININET;

here is the counterpart on osx using pev tools:

#!/bin/sh
find "${1}" -type f \( -iname "*.dll" -o -iname "*.exe" \) | while read pe ; do
    readpe -i "${pe}" | grep -i '.dll' | awk '{print $2}'
done | sort | uniq | sed 's/.[dD][lL][lL]//g;' | tr '\n' ';' ## have the BSD sed release and can't use I with sed

Upvotes: 2

Views: 2022

Answers (1)

jeb
jeb

Reputation: 82307

The first step is simple

command | (
    for /f "delims=" %%L in ('more') DO @(
       echo # %%L
    )
) | sort

But it's get nasty when you really want to make more than a simple echo.
The next code will fail at the IF 1 NEQ 2

echo dummy | (
    for /f "delims=" %%L in ('more') DO @(
        echo # %%L
        if 1 NEQ 2 echo Not Equal
    ) 
)

This has different (complex) reasons, you can read at Why does delayed expansion fail when inside a piped block of code? and What happens with IF command when I pipe to it?

To be able to use the loop without problems you can use a call-function-wrapper.

@echo off
REM *** This "goto" to a label, if there is one embedded in %~0 -
FOR /F "delims=: tokens=3" %%L in ("%~0") do goto :%%L

REM *** The second part calls this batch file, but embedd a label to be called
echo dummy | call %~d0\:myFunc:\..\%~pnx0 | sort
exit /b 

REM *** This function will be called in the pipe, but runs in batch context
:myFunc
for /f "delims=" %%L in ('more') DO @(
    echo # %%L
    if 1 NEQ 2 echo Not Equal
) 
exit /b

How the self-calling works:

call %~d0\:myFunc:\..\%~pnx0

This expands to the full path of the batch, assuming the batch is C:\temp\myFile.bat then the result will be:
call C:\:myFunc:\..\temp\myFile.bat
This is strange but still a valid path, because :myFunc: will not be evaluated as the \..\ part will remove the previous part.
So the batch file calls itself.

The trick is, that :myFunc: is still part of the %0 variable.

FOR /F "delims=: tokens=3" %%L in ("%~0") do goto :%%L
This part splits the %0 by : into
"C", "\", "myFunc" and "\..\temp\myFile.bat"
And the use the third token (myFunc) for the goto %%L.

The advantage of this technic is, it doesn't break existing batch files and their parameter handling.
You could put the function also into %1 but then it's hard to guess if the batch shall use %1 as a goto destination or use it as a normal parameter.

Upvotes: 5

Related Questions