I'm trying to figure how to return multiple variables, in a rather dynamic fashion where I just maintain a list of variable names and then do the ENDLOCAL "export" with a loop, but it seems the !%%V! expansion in the inner FOR loop isn't expanding soon enough or so.
Is there something I'm missing or is there another way to accomplish this?
Example code:
SETLOCAL EnableExtensions EnableDelayedExpansion
[...]
SET "RETURN=FOO BAR"
[...]
SET "RETURN=%RETURN% BAZ"
[...]
:END
:: "Export" %FOO%, %BAR%, and %BAZ% to calling context.
ENDLOCAL & (
FOR %%N IN (%RETURN%) DO (
FOR /f %%V in ("%%N") DO SET %%N=!%%V!
)
)
EXIT /B
Thank you for any help.
Here is a return routine for preserving variables across scope. Just call the return routine with the variables names as parameters and then call endlocal %return%.
Routine:
:return [Variables...]
setlocal enabledelayedexpansion
set "return="
:_return
if "%~1"=="" endlocal & exit /b 1
if not defined %~1 goto __return
set "%~1=!%~1:"=""!"
set "%~1=!%~1:^=^^!"
set "%~1=!%~1:<=^<!"
set "%~1=!%~1:>=^>!"
set "%~1=!%~1:&=^&!"
set "%~1=!%~1:|=^|!"
:__return
set "return=!return!^&set ""%~1=!%~1!"""
if not "%~2"=="" shift & goto _return
setlocal disabledelayedexpansion
set "return=%return:!=^^^^^^^!%"
endlocal & endlocal & set "return=%return:""="%"
exit /b 0
Usage:
call :return foo bar baz
endlocal %return%
Example:
Only variable a will be preserved outside the scope.
#echo off
setlocal
echo Inside
set "a=1"
set "b=2"
echo(%a%
echo(%b%
call :return a
endlocal %return%
echo Outside
echo(%a%
echo(%b%
exit /b 0
:return [Variables...]
setlocal enabledelayedexpansion
set "return="
:_return
if "%~1"=="" endlocal & exit /b 1
if not defined %~1 goto __return
set "%~1=!%~1:"=""!"
set "%~1=!%~1:^=^^!"
set "%~1=!%~1:<=^<!"
set "%~1=!%~1:>=^>!"
set "%~1=!%~1:&=^&!"
set "%~1=!%~1:|=^|!"
:__return
set "return=!return!^&set ""%~1=!%~1!"""
if not "%~2"=="" shift & goto _return
setlocal disabledelayedexpansion
set "return=%return:!=^^^^^^^!%"
endlocal & endlocal & set "return=%return:""="%"
exit /b 0
Problem in your code
As for why your code is not working as you expect, is because the variable must be expanded before the endlocal command is run. Read this excellent SO post about host the command line parses batch scripts: Answer: How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Update
Added support for the following special characters | & < > " ^ !
All special characters supported
Return empty variables as well
https://raw2.github.com/DavidRuhmann/BAT/master/SO21736349891976.bat
:: "Export" %FOO%, %BAR%, and %BAZ% to calling context.
for %%a in ("endlocal" "FOO=%FOO%" "BAR=%BAR%" "BAZ=%BAZ%") do (
if %%a equ "endlocal" (endlocal) else set %%a
)
exit /B
How about
#ECHO OFF
SETLOCAL
FOR %%i IN (foo bar baz) DO SET $%%i=value:%%i
ECHO == initial values ===
SET $
SET "RETURN=$FOO $BAR"
CALL :demo
ECHO == after CALL modifying %return% ===
SET $
FOR %%i IN (foo bar baz) DO SET $%%i=value:%%i
ECHO == initial values ===
SET $
SET "RETURN=%RETURN% $BAZ"
CALL :demo
ECHO == after CALL modifying %return% ===
SET $
GOTO :eof
:demo
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%i IN (foo bar baz) DO SET $%%i=modified:%%i
SET "$retvals="
FOR %%N IN (%RETURN%) DO SET "$retvals=!$retvals!^&set %%N=%%%%N%%"
CALL SET "$retvals=%$retvals%"
:: "Export" %FOO%, %BAR%, and %BAZ% to calling context.
endlocal%$retvals%
EXIT /B
GOTO :EOF
Yielding
== initial values ===
$bar=value:bar
$baz=value:baz
$foo=value:foo
== after CALL modifying $FOO $BAR ===
$bar=modified:bar
$baz=value:baz
$foo=modified:foo
== initial values ===
$bar=value:bar
$baz=value:baz
$foo=value:foo
== after CALL modifying $FOO $BAR $BAZ ===
$bar=modified:bar
$baz=modified:baz
$foo=modified:foo
(I just changed the varnames for convenience of display using set)
Related
Here is a syntax error. How to properly run the ShellWait function? I care about double quotes being used. The boolean parameter should be able to be changed to false. I do not use VBA every day so I am asking for an example if you can.
Sub ShellWait(fName As String, Optional showWindow As Boolean = True)
Dim wsh As Object
Set wsh = VBA.CreateObject("WScript.Shell")
wsh.Run fName, -showWindow, True
End Sub
Private Sub Document_close()
ShellWait "cmd /c echo ^<meta http-equiv="x-ua-compatible" content="IE=10"^> && echo '"hello"' "
' ^the above code does not work
ShellWait "cmd /c echo '"hello"' "
' ^the above code does not work, syntax error
ShellWait (cmd /c echo '"hello"')
' ^the above code does not work, syntax error
End Sub
You could use the following
ShellWait "cmd /K echo ^<meta http-equiv=""x-ua-compatible"" content=""IE=10""^> && echo '""hello""' "
I'm trying to create a batch file that checks if
the directory C:\Pswrd.Zask,
the user name file C:\Pswrd.Zask\Username.Zask,
and the password file C:\Pswrd.Zask\Password.Zask
all exist.
The batch processing should continue on PasswordScreen if the two files in the directory exist.
But batch processing should continue on CreatePasswordScreen if they don't exist all.
The create CreatePasswordScreen block asks for user input. That input is then stored into a file named Password.Zask & Username.Zask inside the folder C:\Pswrd.Zask.
On second run of the program, the program checks existence of that folder, the user name file and the password file, and then sets the user name & password from the two files in C:\Pswrd.Zask to variables right before the PasswordScreen block which will be used to determine if the password is correct or not.
The problem is that I cannot get the password and user name to store correctly in the batch file as a variable without the batch process ultimately coming to a end for an unknown reason.
Did I miss use a command?
The problem should be right under then line that says :UsernameExist.
#echo off
color 0a
if not exist "C:\Pswrd.Zask\" (
mkdir "C:\Pswrd.Zask\"
if "!errorlevel!" EQU "0" (
goto DirectoryExist
) else (
echo Created the folder "C:\Pswrd.Zask"
)
) else (
goto DirectoryExist
)
:DirectoryExist
if exist C:\Pswrd.Zask\Password.Zask (
goto PasswordExist
) else (
goto CreatePasswordScreen
)
:PasswordExist
if exist C:\Pswrd.Zask\Username.Zask (
goto UsernameExist
) else (
goto CreatePasswordScreen
)
:UsernameExist
for /f "Delims=" %%A in (C:\Pswrd.Zask\Password.Zask) do (
set CHECKPASSWORD=%%A
for /f "Delims=" %%B in (C:\Pswrd.Zask\Username.Zask) do (
set CHECKUSERNAME=%%B
goto PasswordScreen
:CreatePasswordScreen
cls
echo Create a password.
echo.
set /p "CREATEPASSWORD= Enter password : "
set /p "CREATEUSERNAME= Enter username : "
echo %CREATEPASSWORD% >> C:\Pswrd.Zask\Password.Zask
attrib C:\Pswrd.Zask\Password.Zask -s -h & echo Password Created!
echo %CREATEUSERNAME% >> C:\Pswrd.Zask\Username.Zask
attrib C:\Pswrd.Zask\Username.Zask -s -h & echo Username Created!
echo.
echo Loading...
timeout /5
:PasswordScreen
color 0a
cls
echo Existing User Account.
echo.
set /p "PASSWORD= Enter Password : "
set /p "USERNAME= Enter Username : "
if %PASSWORD%==%CHECKPASSWORD% goto Operation1True
if not %PASSWORD%==%CHECKPASSWORD% goto OperationFalse
:Operation1True
if %PASSWORD%==%CHECKPASSWORD% goto Operation2True
if not %PASSWORD%==%CHECKPASSWORD% goto OperationFalse
:OperationFalse
color 0c
echo Password Incorrect!
timeout /10
goto PasswordScreen
:Operation2True
cls
echo Password Correct!
pause
I figured out the solution, i chanced it to the following.
#echo off
color 0a
if not exist "C:\Pswrd.Zask\" (
mkdir "C:\Pswrd.Zask\"
if "!errorlevel!" EQU "0" (
goto DirectoryExist
) else (
echo Created the folder "C:\Pswrd.Zask" & timeout /t 5
)
) else (
goto DirectoryExist
)
:DirectoryExist
if exist C:\Pswrd.Zask\Password.Zask (
goto PasswordExist
) else (
goto CreatePasswordScreen
)
:PasswordExist
if exist C:\Pswrd.Zask\Username.Zask (
goto UsernameExist
) else (
goto CreatePasswordScreen
)
:UsernameExist
for /f "Delims=" %%A in (C:\Pswrd.Zask\Password.Zask) do (
set CHECKPASSWORD=%%A
)
for /f "Delims=" %%B in (C:\Pswrd.Zask\Username.Zask) do (
set CHECKUSERNAME=%%B
)
goto PasswordScreen
:CreatePasswordScreen
cls
echo Create a password.
echo.
set /p "CREATEPASSWORD= Enter password : "
set /p "CREATEUSERNAME= Enter username : "
echo %CREATEPASSWORD% >> C:\Pswrd.Zask\Password.Zask
attrib C:\Pswrd.Zask\Password.Zask +s +h & echo Password Created!
echo %CREATEUSERNAME% >> C:\Pswrd.Zask\Username.Zask
attrib C:\Pswrd.Zask\Username.Zask +s +h & echo Username Created!
start %~n0%~x0
exit
:PasswordScreen
color 0a
cls
echo Existing User Account.
echo.
set /p "PASSWORD= Enter Password : "
set /p "USERNAME= Enter Username : "
if %PASSWORD%==%CHECKPASSWORD% (
goto Operation1True
) else (
goto OperationFalse
)
:Operation1True
if %USERNAME%==%CHECKUSERNAME% (
goto Operation2True
) else (
goto OperationFalse
)
:OperationFalse
color 0c
echo Password Incorrect!
timeout /t 10
goto PasswordScreen
:Operation2True
cls
echo Password Correct!
echo.
pause
I am working on a database using a bat file an my variables are not expanding the right way.
Edit:
I have tried the above and have had no such luck.
This is all of the code necessary to show:
set n=0
:1
echo.
set /p fname=Enter First Name:
set /p lname=Enter Last Name:
set /p val%n%=Enter E-mail:
set /p num%n%=Enter Phone Number:
set /p var%n%=Enter Service Number:
set /p dvice%n%=Enter Device:
set /p dserv%n%=Enter Date Serviced:
set /p summ%n%=Enter Summary of Service:
#echo set var%n%=!var%n%! > "C:\Documents and Settings\Administrator\Desktop\My Personal Files\Computer Refresh\Database\%lname%.bat"
#echo set val%n%=!val%n%! >> "C:\Documents and Settings\Administrator\Desktop\My Personal Files\Computer Refresh\Database\%lname%.bat"
#echo set fname=%fname% >> "C:\Documents and Settings\Administrator\Desktop\My Personal Files\Computer Refresh\Database\%lname%.bat"
#echo set lname=%lname% >> "C:\Documents and Settings\Administrator\Desktop\My Personal Files\Computer Refresh\Database\%lname%.bat"
#echo set dvice%n%=!dvice%n%! >> "C:\Documents and Settings\Administrator\Desktop\My Personal Files\Computer Refresh\Database\%lname%.bat"
#echo set dserv%n%=!dserv%n%! >> "C:\Documents and Settings\Administrator\Desktop\My Personal Files\Computer Refresh\Database\%lname%.bat"
#echo set summ%n%=!summ%n%! >> "C:\Documents and Settings\Administrator\Desktop\My Personal Files\Computer Refresh\Database\%lname%.bat"
#echo set num%n%=!num%n%! >> "C:\Documents and Settings\Administrator\Desktop\My Personal Files\Computer Refresh\Database\%lname%.bat"
#echo set n=%n% >> "C:\Documents and Settings\Administrator\Desktop\My Personal Files\Computer Refresh\Database\%lname%.bat"
echo.
echo The data has been stored!
echo.
pause > nul
goto start
:2
echo.
echo LAST NAME IS CASE SENSITIVE!
set /p lname=Please enter last name:
echo.
if /i not exist "C:\Documents and Settings\Administrator\Desktop\My Personal Files\Computer Refresh\Database\%lname%.bat" (goto Error)
call "C:\Documents and Settings\Administrator\Desktop\My Personal Files\Computer Refresh\Database\%lname%.bat"
echo First Name: %fname%
echo Last Name: %lname%
echo Device: !%dvice%%n%!
echo Date of Service: !dserv%n%!
echo.
echo E-Mail: !val%n%!
echo Phone: !num%n%!
echo.
echo Summary:
echo.
echo !summ%n%!
echo.
pause > nul
goto start
This is all I have to work with. The %lname%.bat stores variables as var0="value of variable" The problem is that it is not reading var0. It reads !var 0!.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET /a n=0
:: call "C:\Documents and Settings\Administrator\Desktop\My Personal Files\Computer Refresh\Database\%lname%.bat"
:start
call :plname
echo First Name: %fname%
echo Last Name: %lname%
echo Device: !%dvice%%n%!
echo Date of Service: !dserv%n%!
echo.
echo E-Mail: !val%n%!
echo Phone: !num%n%!
echo.
echo Summary:
echo.
echo !summ%n%!
echo.
SET /a n+=1
IF %n% lss 2 goto START
GOTO :EOF
:plname
SET "fname=First name read"
SET "lname=Last name read"
SET "dvice0=Device zero"
SET "dvice1=Device one"
SET "dserv0=Date zero"
SET "dserv1=Date one"
SET "val0=Email zero"
SET "val1=Email one"
SET "num0=Phone zero"
SET "num1=Phone one"
SET "summ0=Summary zero"
SET "summ1=Summary one"
GOTO :EOF
This batch proves that your code does work. The problem is with your %lname%.bat which you haven't posted. Surely you don't set up a different %lname%.bat for each customer?
No harm in posting some real sample data - just change the names to protect the guilty. Modify the real names to "Brown" or "Green" or "Robinson." No-one appears to use those names any more - they're obviously fake.
When calling the script, a new instance of cmd is running. The variables set are only available to that instance.
set filepath=C:\Documents and Settings\Administrator\Desktop\My Personal Files\Computer Refresh\Database\%lname%.bat
for /f "delims=" %%a in (%filepath%) do %%a
This will loop through each line of %lname%.bat and run that line as a command - if all of the lines in %lname%.bat are those set commands, this should work.
I have problem about IF ELSE in Command Batch script...
In Notepad:
Code:
:CHECKACCOUNT
if /I "%user%"=="insertusername" ( GOTO :ACCOUNT ) ELSE ( GOTO :CHECKPASSACCT )
:CHECKPASSACCT
if /I "%pass%"=="insertpassword" ( GOTO :ACCOUNT ) ELSE ( GOTO :COUNTER )
In COMMAND:
Code:
( was unexpected at this time.
FULL Script Code:
#echo off
::SETTINGS:::::::::::::::::::::::
set filetxt =userpass.txt
set log=logfile.log
set timer=900
::set default = true
::set user = 0
::set pass = 0
:::::::::::::::::::::::::::::::::
:STARTER
ECHO.>>%log%
ECHO ========START========>>%log%
SetLocal EnableDelayedExpansion
Set n=
Set _InputFile=%filetxt%
For /F "tokens=*" %%I IN (%_InputFile%) DO (
Set /a n+=1
Set acct!n!=%%I
)
set router_ip=%acct1%
set user=%acct2%
set pass=%acct3%
GOTO :CHECKFILE1
:CHECKFILE1
CLS
IF EXIST curl.exe ( GOTO :CHECKFILE2 ) else (
ECHO ERROR: curl.exe was not found.>>%log%
ECHO ERROR: curl.exe was not found.
ECHO.
ECHO.
GOTO :PAUSEEXIT
)
:CHECKFILE2
CLS
IF EXIST sleep.exe ( GOTO :CHECKACCOUNT ) else (
ECHO ERROR: sleep.exe was not found.>>%log%
ECHO ERROR: sleep.exe was not found.
ECHO.
ECHO.
GOTO :PAUSEEXIT
)
:CHECKACCOUNT
if /I "%user%"=="insertusername" GOTO ACCOUNT
GOTO CHECKPASSACCT
:CHECKPASSACCT
if /I "%pass%"=="insertpassword" GOTO ACCOUNT
GOTO COUNTER
:ACCOUNT
CLS
::if %default% = true ( GOTO :COUNTER ) ELSE (
ECHO To edit/change USERNAME and PASSWORD... Please type: OPTION
ECHO.
SET /P user="Please enter the username of your Router:"
IF /I %user%==OPTION ( Goto :EDITBAT )
CLS
ECHO To edit/change USERNAME and PASSWORD... Please type: OPTION
ECHO.
SET /P pass="Please enter the password of your Router:"
IF /I %pass%==OPTION ( Goto :EDITBAT )
CLS
set /a i = 1
GOTO :CHECKACCOUNT
::)
:EDITBAT
start /WAIT notepad %filetxt%
set router_ip=%acct1%
set user=%acct2%
set pass=%acct3%
GOTO :CHECKACCOUNT
:COUNTER
IF %i%==0 ( GOTO :RESETROUTER ) ELSE (
ECHO WAIT %i% seconds...
sleep 1
set /a i = i - 1
CLS
GOTO :COUNTER
)
:RESETROUTER
CLS
ECHO READY to RESET....
ECHO Preparing....
sleep 2
sleep 2
CLS
ECHO Processing....
sleep 5
sleep 2
CLS
ECHO Success....
sleep 5
set /a i = %timer%
CLS
GOTO :COUNTER
:PAUSEEXIT
PAUSE
:EXIT
ECHO.>>%log%
ECHO ========END OF LOG FILE========>>%log%
Your error as seen comes from wrong formatting. Don't put it all on 1 line. Instead use this:
if /I "%user%"=="insertusername" (
GOTO :ACCOUNT
) ELSE (
GOTO :CHECKPASSACCT
)
The deeper, underlying reason is: goto :somewhere command needs to be terminated by a newline.
You can simplify this down to:
:CHECKACCOUNT
if /I "%user%"=="insertusername" GOTO ACCOUNT
GOTO CHECKPASSACCT
:CHECKPASSACCT
if /I "%pass%"=="insertpassword" GOTO ACCOUNT
GOTO COUNTER
The ELSE statements aren't needed. Since the IF block will jump somewhere else, placing the second GOTO on the next line or in an ELSE block should be equivalent.
Also, you need the leading colon when defining a GOTO target but not when referring to the target name inside the GOTO statement itself.
The problem has nothing to do with the shown code!
The problem is at
:COUNTER
IF %i%==0 ( GOTO :RESETROUTER ) ELSE (
As the variable i isn't defined, the line will be expanded to
IF ==0 ( GOTO :RESETROUTER ) ELSE (
That is an invalid expression.
To find such simple syntax fails, you should enable debugging with ECHO ON.
You don't need to use the parentheses. You're basically telling the system
"if %user% is insertusername (" and it's asking "what is the '(' supposed to do?"
I would like to write a script that can recursively scan the DLLs in a directory and generate a report of all of their version numbers.
How can I detect the version number of a DLL using a script? VBScript solutions are preferred, unless there is a better way.
You can use the FileSystemObject object to access the file system and its GetFileVersion method to obtain the file version information.
You asked for a VBScript example, so here you are:
Dim oFSO : Set oFSO = CreateObject("Scripting.FileSystemObject")
PrintDLLVersions oFSO.GetFolder(WScript.Arguments.Item(0))
Sub PrintDLLVersions(Folder)
Dim oFile, oSubFolder
' Scan the DLLs in the Folder
For Each oFile In Folder.Files
If UCase(oFSO.GetExtensionName(oFile)) = "DLL" Then
WScript.Echo oFile.Path & vbTab & oFSO.GetFileVersion(oFile)
End If
Next
' Scan the Folder's subfolders
For Each oSubFolder In Folder.SubFolders
PrintDLLVersions oSubFolder
Next
End Sub
Usage:
> cscript //nologo script-file.vbs folder > out-file
e.g.:
> cscript //nologo dll-list.vbs C:\Dir > dll-list.txt
Sample output:
C:\Dir\foo.dll 1.0.0.1
C:\Dir\bar.dll 1.1.0.0
C:\Dir\SubDir\foobar.dll 4.2.0.0
...
EDIT I think this is the source I referenced
This is the script that I use, I apologize, but I don't recall from where. (So,reader, if this started as your script please step forward) It uses the FileSystemObject which can get version directly.
#echo off
setlocal
set vbs="%temp%\filever.vbs"
set file=%1
echo Set oFSO = CreateObject("Scripting.FileSystemObject") >%vbs%
echo WScript.Echo oFSO.GetFileVersion(WScript.Arguments.Item(0)) >>%vbs%
for /f "tokens=*" %%a in (
'cscript.exe //Nologo %vbs% %file%') do set filever=%%a
del %vbs%
echo Full file version of %file% is: %filever%
for /f "tokens=2 delims=. " %%a in ("%filever%") do set secondparam=%%a
set splevel=%secondparam:~0,1%
echo SP level is: %splevel%
endlocal
pause