Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delayed expansion and exclamation marks in strings

Tags:

batch-file

Ok, so I'm still pretty new to batch scripting and I have this problem with my code that I'm using setlocal enabledelayedexpansion in my code for the for loop, the For loop goes through folder names in a specific directory, but the problem is that some of the names may include "!"(exclamation marks) and if that is the case they are not counted for in the "!filename!" and when the code creates a new directory it does not include the "!". Also when the xcopy tries to copy the files from the original folder, the folder is not found, because the variable "!filename!" is not the same as the original(does not have the exclamation point).

So I found that for this I need to only add "setlocal enable delayed expansion" to some parts of the code and turn it off at other, but I cant seem to find the right places.

The code:

@ECHO OFF
setlocal enabledelayedexpansion

SET Location_Folder=V:
SET Destination_folder=V:\Nonimportable

SET Check_file_validation=V:\Nonimportable\result.txt

SET Excluded_folder=Nonimportable
set "lineNr=12"

For /f "tokens=*" %%O in ('dir /b /a:d "%Location_Folder%"') do (
    set filename=%%O

    call D:\somefolder\otherfolder\batscriptname.bat !filename!
    set Validation=

    echo !filename!| FINDSTR /i /c:"%Excluded_folder%" >NUL
    IF ERRORLEVEL 1 (

        for /F "skip=12 delims=" %%a in (%Check_file_validation%) do if not defined Validation (
                set Validation=%%a
                call :valid
        )
    ) else (
        echo This folder name is excluded: !filename!
    )
)
goto Finish
:valid 

echo !Validation!| FINDSTR /c:"1" >NUL
    if ERRORLEVEL 1 (

        set Folder_path=%Location_Folder%\!filename!
        set New_Folder_path=%Destination_folder%\!filename!

        mkdir "!New_Folder_path!"

        echo D | xcopy /o /y /q /s /v "!Folder_path!" "!New_Folder_path!"

        rmdir /s /q "!Folder_path!"
    ) else (

        echo Folder is valid !filename!
        goto Finish
    )
:Finish
exit /b 

The Call part calls another small (~5lines) batch file that checks the sqlplus server if the "!filename!" is valid

EDIT: The whole code works fine and does what it should, unless there is a "!" in the name of some folder.

like image 889
Elviox Rance Avatar asked Mar 10 '17 08:03

Elviox Rance


People also ask

What is delayed expansion?

Delayed Expansion will cause variables within a batch file to be expanded at execution time rather than at parse time, this option is turned on with the SETLOCAL EnableDelayedExpansion command. Variable expansion means replacing a variable (e.g. %windir%) with its value C:\WINDOWS.

How do I enable delayed variable expansion?

When cmd.exe is started, delayed variable expansion is enabled or disabled by giving either the /v:on or /v:off option. In a batch file, delayed variable expansion can be enabled with setlocal enableDelayedExpansion .


2 Answers

The problem is the parameter expansion in set filename=%%O. In %%O is still the exclamation mark, but when delayed expansion is enabled, the bangs are dropped.
The conclusion is simple, delayed expansion have to be disabled when you expand a FOR parameter.

But when you also need delayed expansion?
You simply toggle the mode.

setlocal DisableDelayedExpansion
For /f "tokens=*" %%O in ('dir /b /a:d "%Location_Folder%"') do (
    set "filename=%%O" -- Here the DelayedExpansion have to be disabled
    setlocal EnableDelayedExpansion

    call D:\somefolder\otherfolder\batscriptname.bat filename
    set "Validation="
     ...
    endlocal
)

See also my modification of the CALL myBat.bat filename instead of CALL myBat.bat !filename!.
You shouldn't use content with CALL, better use a variable by reference and in your function take the content by

set "_filename=!%1!"

It's because CALL itself has some nasty behaviour with spaces, carets, etc

like image 179
jeb Avatar answered Oct 24 '22 00:10

jeb


If you use a variable within a code block (parenthesised series of commands) then %var% will yield the value of the variable when the block is originally encountered (ie parse-time value) and !var! the value of the variable as it changes during the block (ie "run-time" value).

If you call a procedure - internal or external, then the values of the variables that the procedure sees are the run-time values from the parent. If these values are changed within the called procedure then the same rules apply, and the changed values are returned to the parent procedure.

However if you invoke setlocal then any value-variation made is reverted to its original value if you execute an endlocal instruction or reach end-of-file within the context of the setlocal.


OK - so that's how delayedexpansion works.

In your case, there is no need for delayedexpansion at all. In the loop in the mainline (%%O) you can use %%O in place of !filename!. In the :valid procedure, you can move the two set commands outside of the code block and then there's no need at all to use !vars! since no access is required to variables whose values change within blocks.

@ECHO OFF
setlocal 

SET Location_Folder=V:
SET Destination_folder=V:\Nonimportable

SET Check_file_validation=V:\Nonimportable\result.txt

SET Excluded_folder=Nonimportable
set "lineNr=12"

For /f "tokens=*" %%O in ('dir /b /a:d "%Location_Folder%"') do (
    set filename=%%O

    call D:\somefolder\otherfolder\batscriptname.bat %%O
    set Validation=

    echo %%O| FINDSTR /i /c:"%Excluded_folder%" >NUL
    IF ERRORLEVEL 1 (

        for /F "skip=12 delims=" %%a in (%Check_file_validation%) do if not defined Validation (
                set Validation=%%a
                call :valid
        )
    ) else (
        echo This folder name is excluded: %%O
    )
)
goto Finish
:valid 

set Folder_path=%Location_Folder%\%filename%
set New_Folder_path=%Destination_folder%\%filename%
echo %Validation%| FINDSTR /c:"1" >NUL
    if ERRORLEVEL 1 (
        mkdir "%New_Folder_path%"
        echo D | xcopy /o /y /q /s /v "%Folder_path%" "%New_Folder_path%"
        rmdir /s /q "%Folder_path%"
    ) else (
        echo Folder is valid %filename%
        rem redundant instruction : goto Finish
    )
:Finish
exit /b 
like image 36
Magoo Avatar answered Oct 23 '22 23:10

Magoo