You are not logged in.
Pages: 1
I tried writing a little script to sort arguments into switches and targets to be processed, but I realised I couldn't use a for loop after endlocal, since the iteration through variables would expand after the variables have been cleared. Does anyone know if there a better way of doing this? I don't think it is possible to use enabledelayedexpansion without setting local, and I don't want to set permanently defined environmental variables. The only alternative I can think of is to echo the array as output, but then the array would need to be re-indexed, and the script wouldn't be able to provide normal output (since output would always have to be redirected).
Incidentally, I'm sure there's a much more efficient way of doing this, like `if "%1"=="/*" `, but I wasn't able to find it.
Here is the code I originally wrote (with echo for testing). Criticism is welcome.
@echo off
setlocal enabledelayedexpansion
set /a ArgumentCount=0
set /a SwitchCount=0
set /a TargetCount=0
echo.
for %%a in (%*) do (
set /a ArgumentCount+=1
set "Argument[!ArgumentCount!]=%%~a"
rem I have not found a way to to reference !A[!B!]! ; only works if one variable uses %.
echo %%~a|findstr /r "^/" 2>&1>NUL
if !ERRORLEVEL! leq 0 (
set /a SwitchCount+=1
set "Switch[!SwitchCount!]=%%~a"
) else (
set /a TargetCount+=1
set "Target[!TargetCount!]=%%~a"
)
)
endlocal & (
rem set /a ArgumentCount=%ArgumentCount%
set /a SwitchCount=%SwitchCount%
set /a TargetCount=%TargetCount%
for /L %%a in (1,1,%SwitchCount%) do (
echo Switch %%a: "!Switch[%%a]!"
set "Switch[%%a]=!Switch[%%a]!"
)
for /L %%a in (1,1,%TargetCount%) do (
echo Target %%a: "!Target[%%a]!"
set "Target[%%a]=!Target[%%a]!"
)
)
exit /b 0
Offline
Update on my stupidity:
I just realised I can dump the arrays into strings and pass those normally. I don't know what the limitations of this method might be, but it will probably meet my needs. I am still curious if there is a way to pass the array itself though.
I also realised that, while `if` does not seem to support the * wildcard, the string could probably be concatenated like `if "%%a:~0,1" equ "/" `. I'm not yet sure if this works, but I will give it a try.
Offline
Interesting that FOR doesn't seem to accept variable values for tokens.
:Testing
for /L %%a in (1,1,%SwitchCount%) do (
for /f "tokens=%%a" %%b in ("%SwitchString%") do (
echo Switch %%a: "%%~b"
)
)
This method works, but not if %SwitchString% is designated as a string like "%SwitchString%". I guess batch for loops are still a mystery to me.
:Testing
set /a Index=0
for %%a in (%SwitchString%) do (
set /a Index+=1
echo !Index!: "%%~a"
)
Last edited by Dheath (28 Jan 2018 19:16)
Offline
Just don’t `endlocal`—don’t return anything—keep it global, then you can do away with all that `endlocal & (…)` nonsense. I don’t think there’s a way to handle those variables in a dynamic way after doing `endlocal`.
echo %%~a|findstr /r "^/" 2>&1>NUL
Hmm. I don’t think `2>&1>NUL` does what you think it does.
Incidentally, I'm sure there's a much more efficient way of doing this, like `if "%1"=="/*" `, but I wasn't able to find it.
If you can’t `find` it then `findstr` it:
echo(%1| findstr "^/" >nul && echo Switch, it’s a switch! || echo Just an ordinary argument passing through…
I also realised that, while `if` does not seem to support the * wildcard, the string could probably be concatenated like `if "%%a:~0,1" equ "/" `. I'm not yet sure if this works, but I will give it a try.
This won’t work, clearly; dynamic variables cannot be indexed, but hopefully you found the previous example useful.
On a different note, you might want to check on your definition of “concatenation”…
Interesting that FOR doesn't seem to accept variable values for tokens.
It’s not the tokens’ fault. The `for /f` loop options are parsed before delayed expansion variables and `for`-loop dynamic variables get a change to evaluate, so they won’t work here. Using a subroutine and parameter variables can help evade this limitation though.
This method works, but not if %SwitchString% is designated as a string like "%SwitchString%". I guess batch for loops are still a mystery to me.
Well not for me, and not for thee, as examples are key, as you will see:
@echo off
setlocal EnableDelayedExpansion
set /a Index=0
for %%a in (a b c) do (
set /a Index+=1
echo !Index!: "%%~a"
)
echo(
set /a Index=0
for %%a in ("a b" c) do (
set /a Index+=1
echo !Index!: "%%~a"
)
echo(
set /a Index=0
for %%a in ("a b c") do (
set /a Index+=1
echo !Index!: "%%~a"
)
echo(
Output:
1: "a"
2: "b"
3: "c"
1: "a b"
2: "c"
1: "a b c"
Simply, the double quotes negate delimiters.
I’m long past that stage of trying to put together an argument parser in batch. Not trying to prematurely end your road of discovery, but any path is going to lead to disappointment: a good batch parser cannot be written in batch itself.
Offline
Thanks for the advice! I realise this might not be the most productive script but it's been a good learning experience since I'm new to programming. Your review is greatly appreciated.
Just don’t `endlocal`—don’t return anything—keep it global, then you can do away with all that `endlocal & (…)` nonsense. I don’t think there’s a way to handle those variables in a dynamic way after doing `endlocal`.
Even if I don't explicitly `endlocal`, the variables still seem to be cleared when the script ends. They are passed to scripts called from the script in which they are defined, but not to the script that calls them. Unless I'm missing something, maybe `endlocal` is implicit since I use `setlocal enabledelayedexpansion`? I am not clear on all the differences between `exit /b` and `goto :eof`, but I read that `goto :eof` does invoke `endlocal` automatically if `setlocal` was set. I didn't think `exit /b` would have the same effect, but perhaps it does.
Hmm. I don’t think `2>&1>NUL` does what you think it does.
Could you tell me what it actually does? I had some mixed results while I was tinkering, but it seems to work fine now. The only thing I'm not sure of is whether one expression can be used to redirect twice, or whether it would be more reliable to use `2>1 ` and `1>NUL` as two expressions separated by a space. My goal was to not ever print any output from the command.
If you can’t `find` it then `findstr` it:
echo(%1| findstr "^/" >nul && echo Switch, it’s a switch! || echo Just an ordinary argument passing through…
Curiously, I encountered some trouble using the echo|findstr method when calling from another script's recursive dir listing. Only the recursive cases choked on the pipe. I ended up matching against a substring to get around this but I'm not sure what caused it. I don't think it's necessary to debug at this point, but perhaps this is a common issue?
This won’t work, clearly; dynamic variables cannot be indexed, but hopefully you found the previous example useful.
Thanks for clarifying about dynamic variables, I didn't even know that was a term. To get around this, I just set `set "Argument=%%~a"` and referenced the substring of !Argument!.
On a different note, you might want to check on your definition of “concatenation”…
You're absolutely right. I guess this would be truncation?
It’s not the tokens’ fault. The `for /f` loop options are parsed before delayed expansion variables and `for`-loop dynamic variables get a change to evaluate, so they won’t work here. Using a subroutine and parameter variables can help evade this limitation though.
Great tip! I can imagine this would get out of hand quickly if the script isn't well structured, but that's probably a good incentive.
Well not for me, and not for thee, as examples are key, as you will see:
@echo off setlocal EnableDelayedExpansion set /a Index=0 for %%a in (a b c) do ( set /a Index+=1 echo !Index!: "%%~a" ) echo( set /a Index=0 for %%a in ("a b" c) do ( set /a Index+=1 echo !Index!: "%%~a" ) echo( set /a Index=0 for %%a in ("a b c") do ( set /a Index+=1 echo !Index!: "%%~a" ) echo(
Output:
1: "a" 2: "b" 3: "c" 1: "a b" 2: "c" 1: "a b c"
Simply, the double quotes negate delimiters.
That's a nice example. I'm still not sure about some of the FOR modes, but I think I understand how double quotes affect the unspecified mode now. It seems like ' and ` are just interpreted as normal characters, but they do have a technical effect in the FOR /F mode as described on the ss64 page about that.
I’m long past that stage of trying to put together an argument parser in batch. Not trying to prematurely end your road of discovery, but any path is going to lead to disappointment: a good batch parser cannot be written in batch itself.
Don't worry about disappointing me, this is mostly just an exercise to learn batch, and programming in general. I know there's a lot more comoplexity in sanitisation and such, but the script I have now meets my basic needs for now. Please don't feel obligated to look it over again, but here's what I have now in case you're curious:
@echo off
setlocal enabledelayedexpansion
set /a SwitchCount=0
set /a TargetCount=0
set "SwitchString="
set "TargetString="
for %%a in (%*) do (
set "Argument=%%~a"
if ["!Argument:~0,1!"] equ ["/"] (
if ["!Argument:~1,1!"] neq [""] (
set /a SwitchCount+=1
set "SwitchString=!SwitchString! "%%~a""
)
) else (
set /a TargetCount+=1
set "TargetString=!TargetString! "%%~a""
)
)
endlocal & (
set /a SwitchCount=%SwitchCount%
set "SwitchString=%SwitchString%"
set /a TargetCount=%TargetCount%
set "TargetString=%TargetString%"
)
exit /b 0
Thanks again for taking the time to point me in the right directions.
Offline
I’ve misunderstood your setup. If the argument parser part is actually in a separate script then that “keep it global” advice suddenly became irrelevant. In this case you will need to return the results.
One way would be to output the results to stdout and have the parent script process this in a FOR /F loop.
The second way, as you’ve demonstrated in your revised script, would be to lose the array as to avoid the need to use delayed expansion in the return block.
Even if I don't explicitly `endlocal`, the variables still seem to be cleared when the script ends. They are passed to scripts called from the script in which they are defined, but not to the script that calls them. Unless I'm missing something, maybe `endlocal` is implicit since I use `setlocal enabledelayedexpansion`? I am not clear on all the differences between `exit /b` and `goto :eof`, but I read that `goto :eof` does invoke `endlocal` automatically if `setlocal` was set. I didn't think `exit /b` would have the same effect, but perhaps it does.
You’re correct about the implied `endlocal`. An implicit `endlocal` happens whenever you run `goto :eof`, `exit /b`, or when the script ends (as mentioned in `endlocal /?`), during any `setlocal`.
I’m just waiting for that day when someone finds a difference, but as far as I’m aware, `goto :eof` behaves exactly like `exit /b` in all cases.
Pyprohly wrote:Hmm. I don’t think `2>&1>NUL` does what you think it does.
Could you tell me what it actually does? I had some mixed results while I was tinkering, but it seems to work fine now. The only thing I'm not sure of is whether one expression can be used to redirect twice, or whether it would be more reliable to use `2>1 ` and `1>NUL` as two expressions separated by a space. My goal was to not ever print any output from the command.
It appears that `2>&1>NUL` is the same as `2>&1 >NUL`, which, if you’re looking to hide both the stdout and stderr streams then this doesn’t achieve that. It needs to be the other way around: `>NUL 2>&1`.
What `2>&1 >NUL` does is discard stdout, and redirect stderr to stdout. So stderr output is converted to stdout output and stdout output is discarded.
Curiously, I encountered some trouble using the echo|findstr method when calling from another script's recursive dir listing. Only the recursive cases choked on the pipe. I ended up matching against a substring to get around this but I'm not sure what caused it. I don't think it's necessary to debug at this point, but perhaps this is a common issue?
I can’t quite visualise the situation. You’ll need to clarify this before I can make a comment.
It seems like ' and ` are just interpreted as normal characters, but they do have a technical effect in the FOR /F mode as described on the ss64 page about that.
Precisely. Single quotes and back ticks are ordinary characters in a FOR loop, it’s only when you use a FOR /F loop that you have to choose your quotes wisely.
Last edited by Pyprohly (01 Feb 2018 12:31)
Offline
Offline
Pages: 1