1

As practice for learning Windows Batch programming, I'm trying to create a batch file which will process all the SCSS files in a folder and compile them into CSS. I am running into an issue while trying to filter out all files that begin with an underscore as these files are snippets only to be used in @import statements.

The batch file I have so far is:

@echo off
SETLOCAL EnableDelayedExpansion
FOR /R "D:\Jaye\Programming\Web\Project\scss" %%G IN (*.scss) DO (
    SET name=%%~nG
    echo !name!
    SET firstChar=!name:~0,1!
    echo !firstChar!
    if !firstChar! == "_" (
        echo "Dont process this file"
    )
)
ENDLOCAL

The ...\Project\scss folder contains the following four files:

main.scss
menus.scss
content.scss
_uiColors.scss

The output I'm getting is:

content
c
main
m
menus
m
_uiColors
_

The important issue here is that the _uiColors.scss file is not triggering the if statement on line 9 of the bat file. Based on the output I can see that it is setting the firstChar variable to an underscore as expected but when that gets compared in the if statement something isn't evaluating as I expect. I'd like to know why "_"=="_" is seeming evaluating to false (is it a type mismatch maybe?) and then what I might try to fix it.

Thanks

EDIT (with answer): I've got it working now thanks to the answers by Mofi and Gerhard. Thanks guys. The issue I ran into is due to my assumptions based on previous experience with other more modern languages I was using quotation marks incorrectly when working with strings.

Dirkinz
  • 41
  • 1
  • 7
  • `if` compares literally everything, even the quotes, so they have to be present on both sides of the comparison operator (`==`)… – aschipfl Aug 12 '20 at 07:37
  • "why `"_"=="_"` is seeming evaluating to false" - it doesn't, but in your code, you used `_=="_"`, which is different. "a type mismatch maybe?" No. The only variable type that exists in `cmd` is STRING (exception: `if` and `set /a` try an internal conversion 'STRING to INTEGER' for each argument), so no mismatch possible. – Stephan Aug 12 '20 at 07:42

2 Answers2

2

My answer on Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files explains in detail how a string comparison is done by command IF which includes the surrounding " on comparing the strings. Therefore the condition if !firstChar! == "_" is never true as the string on left side is always just one character never starting with " while the string on right side has three characters.

I suggest for this task:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "delims=" %%I in ('dir "D:\Jaye\Programming\Web\Project\scss\*.scss" /A-D /B /S 2^>nul ^| %SystemRoot%\System32\findstr.exe /R /V "\\_[^\\]*$"') do (
    rem Do something with file name assigned to loop variable I.
    echo File to process is: "%%I"
)
endlocal

The command FOR starts in background one more command process with %ComSpec% /c and the command line within ' appended as additional arguments. So executed in background is with Windows installed to C:\Windows:

C:\Windows\System32\cmd.exe /c dir "D:\Jaye\Programming\Web\Project\scss\*.scss" /A-D /B /S 2>nul | %SystemRoot%\System32\findstr.exe /R /V "\\_[^\\]*$"

The command DIR searches

  • in specified directory D:\Jaye\Programming\Web\Project\scss
  • and all its subdirectories because of option /S
  • for files because of option /A-D (attribute not directory)
  • matching the wildcard pattern *.scss and
  • outputs the found file names in bare format because of option /B which means with just file name and file extension and with full path because of option /S.

The file names output by DIR are redirected to FINDSTR which searches with a regular expression for lines having an underscore after last backslash character which means at beginning of the file name and outputs the inverted result, i.e. all lines of which file name does not start with an underscore.

The error message output by DIR to handle STDERR on finding no file matching the search criteria is suppressed by redirecting it with 2>nul to device NUL.

Read the Microsoft documentation about Using command redirection operators for an explanation of 2>nul and |. The redirection operators > and | must be escaped with caret character ^ on FOR command line to be interpreted as literal characters 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.

The command FOR with option /F captures output written to handle STDOUT of started background command process and processes the captured output line by line after started cmd.exe terminated itself.

Empty lines are ignored always by FOR which do not occur here. All other lines are split up by default into substrings using normal space and horizontal tab as delimiter. This line splitting behavior is not wanted here because there could be *.scss files with a space in file name. For that reason the option delims= is used to define an empty list of delimiters which disables line splitting behavior.

FOR with option /F would also ignore lines on which first substring after line splitting is starting with a semicolon because of ; is the default end of line character. A file name can start with a semicolon. But DIR outputs in this case the file names always with full path and so it is not possible that any full qualified file name starts with a semicolon. So the default eol=; can be kept in this case.

The advantage of this solution is that *.scss files containing usually one or more ! are processed also correct because of delayed environment variable expansion is not needed here. FINDSTR makes the file name filtering.

To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.

  • dir /?
  • echo /?
  • endlocal /?
  • findstr /?
  • for /?
  • rem /?
  • setlocal /?
Mofi
  • 46,139
  • 17
  • 80
  • 143
1

By double quoting only one side of statement, you are not comparing the same thing:

Examples:

if apples == "apples"

put them underneath each other:

apples
"apples"

These two cannot match, ever. You can almost consider it that the one side of the statement has 2 additional characters.

So by quoting both values either side of the == you will get a proper match, you just need:

if "!firstChar!" == "_"

Additionally, you did not need to create the firstchar variable, you could have just used the following, though it would really only save you one line of code:

if "!name:~0,1!" == "_"

So far you also do not have an else statement, so the if parenthesis block is not needed either. So your code could be:

@echo off & setlocal EnableDelayedExpansion
FOR /R "D:\Jaye\Programming\Web\Project\scss" %%G IN (*.scss) DO (
    SET name=%%~nG
    echo !name!
    echo !name:~0,1!
    if "!name:~0,1!" == "_" echo "Dont process this file: %%~nG"
)

Lastly, you could do this exact same thing, by simply excluding the files starting with _ by including findstr and therefore do not require the if statements, nor require delayedexpansion:

@echo off
pushd "D:\Jaye\Programming\Web\Project\scss"
for /f "delims=" %%i in ('dir /b /s /a-d *.scss ^| findstr /V /IRC:"^_"') do echo Non of these starts with "_": "%%~i"
popd
Gerhard
  • 22,678
  • 7
  • 27
  • 43