0

A net users command has this output:

C:\>net users

Users accounts for \\WIN-PC

-------------------------------------------------------------------------------
User 1                   Administrador            Guest
DefaultAccount           test                     WDAGUtilityAccount
The command completed successfully.

I want to run a net user "user name" for every user, so I need to loop this string for every name.

Because every name are separated with double or more spaces, I can use this regex:

^(.*?) +(.*?) +(.*?)(?: +)?$

Explain:

 ^(.*?) - Match and extract the first name in the start of line
 '  +'  - Match double spaces
 (.*?)  - Match and extract the second name
 '  +'  - Match double spaces
 (.*?)  - Match and extract the third name
 (?: +)?$ - After the third name, it can have more spaces or end the line

The case with two or one name can have another regex, but it works with 99% of the names. Names can have space, but will have only one space.

Until now, this command just print the correct lines:

net user | findstr /R /C:"^[!-z ]* [!-z ]* [!-z ]*"

I used [!-Z ] to get string ASCII 0x21 to 0x7a -> !"#$....vwxyz plus space. It gets the correctly line, but just it.

How can I extract the names and run a command with it?

Rodrigo
  • 11,909
  • 23
  • 68
  • 101
  • Well, this command even work, but fail when there is one space in name: ```cmd /E:ON /V:ON /C "FOR /F "usebackq delims=" %W IN (`net user^| findstr /R /C:"^^[!-Z]* [!-Z]* [!-Z]*"`)DO @(SET T=%W &(for %a in (!T!) do @(echo %a)))"``` – Rodrigo Jun 20 '23 at 13:53
  • Use just the first 3 groups, remove the rest. And, remove the last lazy-quantifier. _^(.*?) +(.*?) +(.*)_ – Reilas Jun 21 '23 at 04:04

1 Answers1

0

There could be used the following batch file using net user as long as no user account name contains two spaces in series.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "skip=4 eol=| delims=" %%G in ('%SystemRoot%\System32\net.exe user') do (
    if not "%%G" == "The command completed successfully." (
        set "EntireInfo=%%G"
        setlocal EnableDelayedExpansion
        set "EntireInfo=!EntireInfo:  =?!"
        for /F "tokens=1-3 eol=| delims=?" %%H in ("!EntireInfo:? =?!") do (
            endlocal
            echo "%%H"
            if not "%%I" == "" echo "%%I"
            if not "%%J" == "" echo "%%J"
        )
    )
)
endlocal

There is first defined completely the required execution environment with the first two command lines for

  • turning off command echo mode,
  • enabling command extensions,
  • disabling delayed variable expansion.

Next is executed by FOR respectively cmd.exe processing the batch file one more Windows Command Processor with %ComSpec% and option /c and the command line specified in first FOR command line between ' appended as additional arguments. There is executed therefore in background with Windows installed into C:\Windows:

C:\Windows\System32\cmd.exe /c C:\Windows\System32\net.exe user

The output of net.exe to handle STDOUT (standard output) of background command process is captured by FOR and processed line by line once cmd.exe started in background terminated itself after finishing the execution of net.exe.

There are skipped the first four lines because of option skip=4.

The option eol=| redefines the end of line character from default ; to a vertical bar because of an account name could begin with a semicolon and the line should not be ignored just because of beginning with a semicolon. A vertical bar cannot be part of an account name as that character is not allowed in file/folder names.

The option delims= defines an empty list of delimiters instead of default normal space and horizontal tab to get the entire line on not being completely empty assigned to the specified loop variable H.

There is made a case-sensitive string comparison to ignore the last line output by net.exe and process further only the lines with account names.

The entire information on current line is first assigned to the environment variable EntireInfo. That must be done before enabling delayed variable expansion required for further processing because otherwise any ! inside the string assigned to the loop variable H would be interpreted as beginning/end of a delayed expanded variable reference instead of a literally interpreted exclamation mark.

Next is enabled delayed expansion. Read this answer for details about the commands SETLOCAL and ENDLOCAL because of there is not just enabled delayed variable expansion.

The line assigned to the environment variable EntireInfo is modified before the second FOR processes it by using two string substitutions. The first one replaces every occurrence of two spaces in series by a question mark.

The result of the first string substitution for the two lines of interest in the example is:

User 1????????? Administrador??????Guest??????????
DefaultAccount????? test?????????? WDAGUtilityAccount???

The space character left to next account name on an odd number of spaces between two account names requires the usage of one more string substitution to replace a question mark + space by just a question mark producing for the example lines:

User 1?????????Administrador??????Guest??????????
DefaultAccount?????test??????????WDAGUtilityAccount???

There is one more FOR loop used to split up the current line into up to three substrings assigned to the loop variables G, I and J according to the ASCII table with using the question mark as delimiter. A question mark cannot be in an account name as that character is not allowed in file/folder names.

Delayed variable expansion is next disabled first by restoring the previous local environment before there is output the first account name which always exists.

The other two account names are optional depending on number of accounts setup at all. The two IF conditions are used to find out if there is a second and a third account name on a line and output them using ECHO if that is the case.

The command ECHO can be replaced by any other command using the account name assigned to loop variables H, I and J. Surrounding the account names with " is very important on further processing them, especially on an account name contains an ampersand.

It would be of course also possible to use a character index dependent string substitution to get the account names and use extra code to remove all trailing spaces. But a character index independent solution is better in my opinion.

The third command line could be also with using FINDSTR as filter.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "eol=| delims=" %%G in ('%SystemRoot%\System32\net.exe user ^| %SystemRoot%\System32\findstr.exe /R /C:"^[!-Z]* [!-Z]*  [!-Z]*"') do (
    set "EntireInfo=%%G"
    setlocal EnableDelayedExpansion
    set "EntireInfo=!EntireInfo:  =?!"
    for /F "tokens=1-3 eol=| delims=?" %%H in ("!EntireInfo:? =?!") do (
        endlocal
        echo "%%H"
        if not "%%I" == "" echo "%%I"
        if not "%%J" == "" echo "%%J"
    )
)
endlocal

The option skip=4 is removed from the FOR command line and the first IF condition on next line is no longer necessary as FINDSTR filtered already the output of NET in background command process.

Read the Microsoft documentation about Using command redirection operators for an explanation of |. The redirection operator | must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded command line with using a separate command process started in background. ^ must not be escaped inside a double quoted argument string as it is not interpreted as escape character inside a string with surrounding ".

Mofi
  • 46,139
  • 17
  • 80
  • 143