Windows batch (cmd.exe) command line parameters and string manipulation

10,592

Solution 1

One way to work around the problem would be to specify "CPPFLAGS=/EHsc" and then in the loop use %%~a to get rid of the double quotes.

Another way to work around the problem would be to check the first character of %%a, and if it is a / then prepend a = to it. In order to achieve this you will need to setlocal enabledelayedexpansion, assign %%a to a variable, and then use the %variable:~1,1% notation to extract the first character so you can compare it against /. For more information about this notation, type help set.

Update (after OP's update)

The following fragment appears to work, it is a bit simpler than the corresponding fragment in your solution, and it does not contain any hard-coded names of the arguments, so it is more general-purpose:

SET allargs=
FOR %%a IN (%*) DO (
    SET curarg=%%a
    IF "!curarg:~0,1!" EQU "/" (
        SET allargs=!allargs!=!curarg!
    ) ELSE (
        SET allargs=!allargs! !curarg!
    )
)
ECHO !allargs!

Solution 2

You can use for /f in a loop to force splitting argument list by space and not by equal sign. Try something like this:

@echo off
setlocal enabledelayedexpansion

set params=%*

:loop
for /f "usebackq tokens=1,*" %%A in ('!params!') do (
  echo %%A
  set params=%%B
)
if not "!params!"=="" goto loop

endlocal

Solution 3

Have you tried the following?

string.bat foo.obj bar.obj "CPPFLAGS=/EHsc"

If you're appending the CPPFLAGS argument yourself, try enclosing it in quotes.

Reference: http://www.robvanderwoude.com/parameters.php

Share:
10,592
Lumi
Author by

Lumi

This is not about me, and nothing about computers or programming either, but check out the site of a man from a land down under who, going against the academic establishment, has come to what I believe are amazing insights into the history of the mega surface features of Our Planet Earth, adding significant and genuinely new facets to the old theory of Earth Expansion which, while discredited by consensus, will prevail when the current paradigm of Plate Tectonics collapses under its many conundrums: http://www.earthexpansion.blogspot.com/ - Don Findlay's Expanding Earth Blog

Updated on August 21, 2022

Comments

  • Lumi
    Lumi almost 2 years

    Let's consider this FOR loop in a Windows batch script:

    D:\MiLu\Dev\C++\temp :: type string.bat
    @ECHO OFF
    FOR %%a IN (%*) DO ECHO %%a
    

    It echoess all the arguments, one by one. Really?

    D:\MiLu\Dev\C++\temp :: string.bat foo.obj bar.obj CPPFLAGS=/EHsc
    foo.obj
    bar.obj
    CPPFLAGS
    /EHsc
    

    It splits command-line arguments not only on spaces (good), but also on = (not good). How can I prevent this from happening?

    What I want to achieve is simple: A wrapper around NMAKE.exe that specifies /nologo to nmake and also - and this is the problem - to the compiler via the environment variables CFLAGS and CPPFLAGS while at the same time including any settings for CFLAGS and CPPFLAGS supplied on the command line.

    In other words, I want to have the script add /nologo to the command line input for CFLAGS and CPPFLAGS even when there is none. Always /nologo! Don't annoy me with your logo, comrade compiler!

    Update

    Here's what I've come up with based on Mike's answer:

    @ECHO OFF
    SETLOCAL
    SETLOCAL ENABLEDELAYEDEXPANSION
    
    FOR %%a IN (%*) DO (
        SET var1=%%a
        ECHO %%a - !var1! - !var1:~0,1!
        IF "!var1:~0,1!" EQU "/" (
            ECHO gefunden: %%a !var1!
        )
    )
    

    Going to continue tomorrow ...

    Update 2

    Okay, given that tomorrow is already here I might just as well continue ... so here's a working solution, proudly presented. Feel free to comment on how to improve it.

    @ECHO OFF
    SETLOCAL
    SETLOCAL ENABLEDELAYEDEXPANSION
    
    SET files=
    SET   CFLAGS=/nologo %CFLAGS%
    SET CPPFLAGS=/nologo %CPPFLAGS%
    
    SET state=normal
    FOR %%a IN (%*) DO (
        SET curarg=%%a
        REM ECHO %%a - !curarg! - !curarg:~0,1!
        IF /I "%%a" EQU "CFLAGS" (
            SET state=expecting_cflags
        ) ELSE IF /I "%%a" EQU "CPPFLAGS" (
            SET state=expecting_cppflags
        ) ELSE (
            IF "!curarg:~0,1!" EQU "/" (
                REM ECHO gefunden: %%a !curarg!
                IF "!state!" EQU "expecting_cflags" (
                    REM ECHO expecting_cflags
                    SET CFLAGS=!CFLAGS! !curarg!
                ) ELSE IF "!state!" EQU "expecting_cppflags" (
                    REM ECHO expecting_cppflags
                    SET CPPFLAGS=!CPPFLAGS! !curarg!
                ) ELSE (
                    ECHO Logikfehler >&2
                )
            ) ELSE (
                SET files=!files! !curarg!
            )
            SET state=normal
        )
    )
    ECHO Dateien:  !files! >&2
    ECHO CFLAGS:   !CFLAGS! >&2
    ECHO CPPFLAGS: !CPPFLAGS! >&2
    :: ECHO ON
    nmake /nologo %files% CFLAGS="%CFLAGS%" CPPFLAGS="%CPPFLAGS%"
    ENDLOCAL
    
    • Aacini
      Aacini over 12 years
      The standard delimiters for Batch file parameters and FOR sets are comma, semicolon and equal-sign, besides spaces.
    • Lumi
      Lumi over 12 years
      Thanks, @Aacini. Here's a useful example page at SS64.COM. Doesn't look like there's a way to override the delimiters of the simple FOR loop (without /F), does it?
  • Lumi
    Lumi over 12 years
    Yes, I had tried enclosing CPPFLAGS=/EHsc in double quotes, and it does work. I didn't consider it convenient, though. Useful link, by the way. Here's another one.
  • Lumi
    Lumi over 12 years
    Thanks, halfway there. Going to update my question with what I've achieved so far.
  • Lumi
    Lumi over 12 years
    Works nicely, thanks! Had tried this approach, but on the parameter list directly (%*), not on a variable. And that didn't work nicely. Sort of counter-intuitive: you assign the parameter to a variable, and the behaviour changes completely. Okay, guess that's because %* is a list beast.
  • MBu
    MBu over 12 years
    I used for /f because it allows to declare the character I want to split the string by (by default it is a space, and only space - no equal sign). But for /f is for iterating through lines and splitting them into fields, so I had to add external loop and reformat the input string each time - that's why I used the variable. In this case for is just a string operation: "put the string up to the first space in %%A and the rest in %%B" - no looping.
  • Mike Nakis
    Mike Nakis over 12 years
    I updated my answer with a suggestion for an improvement on what you have come up with.
  • Lumi
    Lumi over 12 years
    Thanks, Mike. Unfortunately, I don't understand your improvement. It appears to simply reproduce the original %*. I'd need a way to merge in my default options, originally /nologo, but /W4 is useful as well. I can supply CFLAGS or CPPFLAGS only once. So how would I merge in my defaults with your solution?
  • Mike Nakis
    Mike Nakis over 12 years
    Okay, I may have misunderstood what you are trying to do. I thought the only problem was that you did not want to lose the '=' sign. Apparently you want to do something more complex than that: in the command-line of the batch-file you are trying to combine parameters to CL as well as parameters to the batch file itself. So, scratch my update. Your solution is fine.
  • Lumi
    Lumi over 12 years
    Thanks again for clarifying. Giving you the final vote as you provided the clue to solving this. Man, that batch scripting is difficult! All those cryptic % ! %% :~. (Same story with Bash, nearly impossible to remember that stuff.)
  • Mike Nakis
    Mike Nakis over 12 years
    Thanks. Yes, the complexity of these things is insane, and it is all in the name of not having to enclose strings in quotes. Sometimes I think it is not worth the hassle, twisting these shell languages to get them to do what you want, and it is better to just write the program in a real language.