need help parsing output

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
mirrormirror
Posts: 129
Joined: 08 Feb 2016 20:25

need help parsing output

#1 Post by mirrormirror » 23 Jul 2016 05:09

Hello all. I posted this same issue here: viewtopic.php?f=3&t=6044&p=47986#p47986

I'm asking in the wider forum in case someone might have a solution ising findstr or something.

I want to parse the output from this:

Code: Select all

C:\>icacls "c:\Program Files\Common Files"
c:\Program Files\Common Files NT SERVICE\TrustedInstaller:(F)
                              NT SERVICE\TrustedInstaller:(CI)(IO)(F)
                              NT AUTHORITY\SYSTEM:(M)
                              NT AUTHORITY\SYSTEM:(OI)(CI)(IO)(F)
                              BUILTIN\Administrators:(M)
                              BUILTIN\Administrators:(OI)(CI)(IO)(F)
                              BUILTIN\Users:(RX)
                              BUILTIN\Users:(OI)(CI)(IO)(GR,GE)
                              CREATOR OWNER:(OI)(CI)(IO)(F)

The only problem I have is the first line of output:

Code: Select all

c:\Program Files\Common Files NT SERVICE\TrustedInstaller:(F)

The output I want is this (with all preceeding / trailing <TABS>, <SPACES> trimmed):

Code: Select all

NT SERVICE\TrustedInstaller:(F)
NT SERVICE\TrustedInstaller:(CI)(IO)(F)
NT AUTHORITY\SYSTEM:(M)
NT AUTHORITY\SYSTEM:(OI)(CI)(IO)(F)
BUILTIN\Administrators:(M)
BUILTIN\Administrators:(OI)(CI)(IO)(F)
BUILTIN\Users:(RX)
BUILTIN\Users:(OI)(CI)(IO)(GR,GE)
CREATOR OWNER:(OI)(CI)(IO)(F)

The folder path: "c:\Program Files\Common Files" will change - This needs to work for an unknown number of <folder paths> which may include [spaces], [!], etc. - basically all allowable NTFS file naming characters. I do have a work-a-round so this is not critical but would love to have a simpler solution if one is available.

aGerman
Expert
Posts: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: need help parsing output

#2 Post by aGerman » 23 Jul 2016 05:41

Try this

Code: Select all

@echo off &setlocal DisableDelayedExpansion

set "directory=c:\Program Files\Common Files"

for /f "delims=" %%i in ('icacls "%directory%"') do (
  set "line=%%i"
  setlocal EnableDelayedExpansion
  for /f "tokens=*" %%j in ("!line:%directory%=!") do (
    endlocal
    echo %%j
  )
)
pause

Regards
aGerman

mirrormirror
Posts: 129
Joined: 08 Feb 2016 20:25

Re: need help parsing output

#3 Post by mirrormirror » 23 Jul 2016 14:39

Hello and thank you for the solution. It works on the foldername in my original post but fails with some special characters. If you run this script (it will create one new folder) you can see the failure:

Code: Select all

@echo off &setlocal DisableDelayedExpansion

REM set "directory=c:\Program Files\Common Files"
SET "directory=1% . a! b! c! ^ 'name1 = name +2"
IF NOT EXIST "%directory%" MD "%directory%"

for /f "delims=" %%i in ('icacls "%directory%"') do (
  set "line=%%i"
  setlocal EnableDelayedExpansion
  for /f "tokens=*" %%j in ("!line:%directory%=!") do (
    endlocal
    echo "%%j"
  )
)

Output:

Code: Select all

"name +2== name +2 BUILTIN\Administrators:(I)(F)"
"BUILTIN\Administrators:(I)(OI)(CI)(IO)(F)"
"NT AUTHORITY\SYSTEM:(I)(F)"
"NT AUTHORITY\SYSTEM:(I)(OI)(CI)(IO)(F)"
"NT AUTHORITY\Authenticated Users:(I)(M)"
"NT AUTHORITY\Authenticated Users:(I)(OI)(CI)(IO)(M)"
"BUILTIN\Users:(I)(RX)"
"BUILTIN\Users:(I)(OI)(CI)(IO)(GR,GE)"
"Successfully processed 1 files; Failed processing 0 files"

Notice the first line of output: "name +2== name +2 BUILTIN\Administrators:(I)(F)"

aGerman
Expert
Posts: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: need help parsing output

#4 Post by aGerman » 23 Jul 2016 14:58

In that case you have no better chance than determining the string length (as you already did). I used the function found here
http://www.dostips.com/DtTipsStringOperations.php#Function.strLen

Code: Select all

@echo off &setlocal DisableDelayedExpansion

REM set "directory=c:\Program Files\Common Files"
SET "directory=1% . a! b! c! ^ 'name1 = name +2"
IF NOT EXIST "%directory%" MD "%directory%"

call :strLen directory len
set /a "len+=1"
for /f "delims=" %%i in ('icacls "%directory%"') do (
  set "line=%%i"
  setlocal EnableDelayedExpansion
  echo !line:~%len%!
  endlocal
)
pause

exit /b

:strLen string len -- returns the length of a string
::                 -- string [in]  - variable name containing the string being measured for length
::                 -- len    [out] - variable to be used to return the string length
:: Many thanks to 'sowgtsoi', but also 'jeb' and 'amel27' dostips forum users helped making this short and efficient
:$created 20081122 :$changed 20101116 :$categories StringOperation
:$source http://www.dostips.com
(   SETLOCAL ENABLEDELAYEDEXPANSION
    set "str=A!%~1!"&rem keep the A up front to ensure we get the length and not the upper bound
                     rem it also avoids trouble in case of empty string
    set "len=0"
    for /L %%A in (12,-1,0) do (
        set /a "len|=1<<%%A"
        for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"
    )
)
( ENDLOCAL & REM RETURN VALUES
    IF "%~2" NEQ "" SET /a %~2=%len%
)
EXIT /b

Regards
aGerman

mirrormirror
Posts: 129
Joined: 08 Feb 2016 20:25

Re: need help parsing output

#5 Post by mirrormirror » 23 Jul 2016 15:58

ok - thank you :)

aGerman
Expert
Posts: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: need help parsing output

#6 Post by aGerman » 23 Jul 2016 16:40

Following your discussion with Dave ...

Code: Select all

@echo off &setlocal DisableDelayedExpansion

SET "d=1%% . a! b! c! ^ 'name1 = name +2"
IF NOT EXIST "%d%" MD "%d%"

for %%d in (
  "%programfiles%"
  .
  "%commonprogramfiles%"
  "someinvalidpath"
  "%d%"
) do (
  echo CurrentFolder: __________ "%%~d"
  set "directory=%%~d"
  set "previous="
  setlocal EnableDelayedExpansion
  set "str=AA!directory!"
  set "len=0"
  for /L %%A in (12,-1,0) do (
      set /a "len|=1<<%%A"
      for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"
  )
  for %%i in (!len!) do (
    endlocal
    for /f "delims=" %%j in ('icacls "%%~d"') do (
      set "line=%%j"
      if not defined previous (
        setlocal EnableDelayedExpansion
        for /f "delims=" %%k in ("!line:~%%i!") do (endlocal &set "previous=%%k")
      ) else (
        setlocal EnableDelayedExpansion
        echo !previous!
        for /f "delims=" %%k in ("!line:~%%i!") do (endlocal &set "previous=%%k")
      )
    )
  )
)
pause

This also removes the last status line.

Regards
aGerman

mirrormirror
Posts: 129
Joined: 08 Feb 2016 20:25

Re: need help parsing output

#7 Post by mirrormirror » 23 Jul 2016 17:21

Thanks for continuing to look at this. I discovered a provlem with the test script. It is stripping out the "%" from both the folder creation as well as the search - for example:

Code: Select all

SET "directory1=1% 2% 3% 4^% 5% ) == =. a! b! c! d^! e! ^ ^ 'n1 = n +2 -( )))"
IF NOT EXIST "%directory1%" MD "%directory1%"

The folder created should be: "1% 2% 3% 4^% 5% ) == =. a! b! c! d^! e! ^ ^ 'n1 = n +2 -( )))"
But it is actually: "1 3 5 ) == =. a! b! c! d^! e! ^ ^ 'n1 = n +2 -( )))"

so this presents another issue because those percents also get stripped out during the FOR loop search:

Code: Select all

CurrentFolder: __________ 1 3 5 ) == =. a! b! c! d^! e! ^ ^ 'n1 = n +2 -( )))


Now for my needs, I don't need to create any folders, I'm actually searching for folders like this:

Code: Select all

FOR /F "tokens=1,* delims=:" %%a IN ('icacls "%NetResRoot%" /findsid "%tagFldr01%" /t /c /l ^|FIND /I "SID Found:"') DO (

Any ideas on how to handle this?

aGerman
Expert
Posts: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: need help parsing output

#8 Post by aGerman » 23 Jul 2016 17:35

Percent signes in a (hardcoded) string literal have to be doubled in order to save a single percent sign in the variable. Have a look at my latest code. I corrected your example accordingly.

If you read the folder names from a file (for /f) or from the file system (for /d or dir) you don't need to double them afterwards.

mirrormirror
Posts: 129
Joined: 08 Feb 2016 20:25

Re: need help parsing output

#9 Post by mirrormirror » 23 Jul 2016 17:46

Ok. I am working up a short test script to see how this works...

mirrormirror
Posts: 129
Joined: 08 Feb 2016 20:25

Re: need help parsing output

#10 Post by mirrormirror » 23 Jul 2016 18:12

aGerman - thank you for your help. I tested your latest code with an even more complex foldername and everything works fine.

Also, if you have any input on the :strlen question I asked in the other post feel free to state it:
viewtopic.php?f=3&t=6044&p=48009#p48009

mirrormirror
Posts: 129
Joined: 08 Feb 2016 20:25

Re: need help parsing output

#11 Post by mirrormirror » 23 Jul 2016 18:20

Sorry - one more question concerning your code...

Are you sure that the proper number of SETLOCAL / ENDLocal commands will be performed? Even if a large number of folders are processed in this loop? I just don't normally see the "SETLOCAL" performed in one loop and the "ENDLOCAL" performed in another. I've never used it this way.

aGerman
Expert
Posts: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: need help parsing output

#12 Post by aGerman » 24 Jul 2016 03:59

I just don't normally see the "SETLOCAL" performed in one loop and the "ENDLOCAL" performed in another.

That might be confusing but is a good way to safely bring a value over the endlocal barrier. Of cause this will only work if you are sure the for /f loop iterates only once. It's safe if you process a single line because for /f iterates linewise :wink: (A simple for loop behaves the same with only one token.)

As to the strLen question - I agree with Dave. It doesn't make a big difference. In your example the powers of 2 are already hardcoded (which could be slightly faster). But it's not the algorithm that slows the code down. Calling labels reduces the performance (that's why I removed it) and setlocal/endlocal in a loop too. But you can't eliminate the latter because you would lose the possibility to safely handle special characters.

mirrormirror
Posts: 129
Joined: 08 Feb 2016 20:25

Re: need help parsing output

#13 Post by mirrormirror » 25 Jul 2016 02:10

Thank you for the input on :strlen

So after everything, I was poking around with the code you sent and changed it a bit. I would love to have an extra pair of eyes check it out to see if there are any issues with it. It works, but I have a few questions.

Note: I decided to try the strlen.exe provided by Aacini here: http://www.dostips.com/forum/viewtopic.php?t=3428

Code: Select all

FOR /F "tokens=1,* delims=:" %%a IN ('icacls "%NetResRoot%" /findsid "%tagFldr01%" /t /c /l ^|FIND /I "SID Found:"') DO (
   SET "myDir=%%~b"
   SET "previous="
   SETLocal EnableDelayedExpansion
   @ECHO CurrentFolder: __________ "!myDir:~1,-1!"
   SET "str=x!myDir:~1,-1!"
   REM SET "len=0"
   StrLen str
   SET /A len=!ERRORLEVEL!
   FOR /F "tokens=1,* delims=/" %%c IN ("!len!/!myDir:~1,-1!") DO (
      ENDLocal
      FOR /F "delims=" %%e IN ('icacls "%%d"') DO (
         SET "line=%%e"
         SETLocal EnableDelayedExpansion
            IF DEFINED previous @ECHO !previous!
            FOR /F "delims=" %%k in ("!line:~%%c!") do (ENDLocal &SET "previous=%%k")
      )
   )
   @ECHO(
)

Note: the "!myDir:~1,-1" is needed to trim the output from the outer FOR loop in case it isn't obvious - it trims a <SPACE> and <Period> from the output string.

Question 1: Will this line ever result in len=<NUL>? Assuming that I don't manually set SET "ERRORLEVEL=" - In other words, would I ever need to check if "!len!"=="" before proceeding to the next loop?

Code: Select all

SET /A len=!ERRORLEVEL!

Question 2: Are these two SETLOCAL / ENDLOCAL necessary within the loop - or can I simply just place them before and after the entire code I posted:
- - - - - - - 2b: Would DELAYED expansion change the character handling within this loop in any way?

Code: Select all

   SETLocal EnableDelayedExpansion
...
   FOR /F "tokens=1,* delims=/" %%c IN ("!len!/!myDir:~1,-1!") DO (
      ENDLocal
      FOR /F "delims=" %%e IN ('icacls "%%d"') DO (
...
Question 3: Do you see any way I could end up with an uneven number of SETLOCAL / ENDLOCAL commands? I know I asked this last time, but I've changed the code a bit since then.

Again, thanks aGerman for your assistance - and this is open for anyone to reply / give advice.

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: need help parsing output

#14 Post by penpen » 25 Jul 2016 05:04

You could first change to your specified directory (cd or pushd), execute "icacls ." and
display the output after removing the first two characters:

Code: Select all

@echo off
setlocal enableExtensions disableDelayedExpansion
set "directory=C:\Program Files\Common Files"

setlocal enableExtensions enableDelayedExpansion
pushd "!directory!"
for /F "tokens=* delims=" %%a in ('icacls .') do (
   set line=%%a
   echo(!line:~2!
)
popd
endlocal
endlocal
goto :eof


penpen

Edit: Corrected a flaw "popd" was in the wrong line.
Last edited by penpen on 25 Jul 2016 17:52, edited 1 time in total.

aGerman
Expert
Posts: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: need help parsing output

#15 Post by aGerman » 25 Jul 2016 10:53

Good idea penpen!
___________________________

Question 1:
Never set errorlevel manually. Elsewise you overwrite the dynamic errorlevel variable with your user-defined environment variable. Of course you can set variable len to 0.

Question 2:
viewtopic.php?t=6866
Just execute my 3 examples ...

Question 3:
No. Still only one line ("!len!/!myDir:~1,-1!" doesn't contain any line break) will be processed. Thus your loop doesn't iterate and the body of the loop (including the endlocal command) will be executed only once.

Regards
aGerman

Post Reply