cd command does not changed to variable value change directories - batch-file

Here the value is coming correct in the variable lastoccur and also it is echoed
but the cd command to move to that directory does is seemingly not working. Why is this happening?
#echo off
mode con: cols=157 lines=2500
setlocal enabledelayedexpansion
for /f "tokens=2" %%G in ('p4 client -o 2850NewDev ^| findstr /c:"Root:"') do (
set lastoccur=%%G
)
cd /d !lastoccur!
echo !lastoccur!
PAUSE
UPDATE 1
I changed the code to this:
#echo off
mode con: cols=157 lines=2500
set lastoccur=""
for /f "tokens=2" %%G in ('p4 client -o 2850NewDev ^| findstr /c:"Root:"') do (
set lastoccur=%%G
)
cd %lastoccur%
echo %lastoccur%
PAUSE
Now also cd does not change the directory. I am not seeing any path on the window, just the echoed value.

The problem comes from the line setlocal enabledelayedexpansion.
setlocal creates a local session when it runs. You can think of this as a child cmd.exe process being spawned (it's not, but it acts like one). Everything you do after that line but before a corresponding endlocal (if any) is done in its own compartmentalized environment.
You are changing the directory in the newly-created local environment, but the global environment is not being changed.
You were close with your second attempt, but you cannot use variables that you create inside of code blocks without delayed expansion. You can, however, use the for loop variables to do things.
for /f "tokens=2" %%G in ('p4 client -o 2850NewDev ^| findstr /c:"Root:"') do (
cd /d %%G
echo %%G
)

Related

Search file with wildcard path

I want to write a script to prompt user for file path and list all files found. The file path can contain wildcards. Something similar to this. But the batch script version of it. For example:
C:\Somewhere\user*\app\version-*.*\start.exe
The files might be located like this:
C:\Somewhere\user345\app\version-1.0\start.exe
C:\Somewhere\user898\app\version-1.2\start.exe
C:\Somewhere\user898\app\version-1.3\start.exe
I tried to use FOR and it turns out to be so much harder than expected because FOR does not support wildcards in the middle of a path.
Is there a way to list these files? (Maybe without using for?)
I think this recursive solution works pretty well; you may name it WCDIR.bat:
#echo off
setlocal
if "%~1" neq "" set "next=%~1" & goto next
echo Show files selected by several wild-cards
echo/
echo WCDIR wildcardPath
echo/
echo Each folder in the path may contain wild-cards
echo the last part must be a file wild-card
goto :EOF
:next
for /F "tokens=1* delims=\" %%a in ("%next%") do set "this=%%a" & set "next=%%b"
if defined next (
for /D %%a in ("%this::=:\%") do (
setlocal
cd /D "%%~a" 2>NUL
if not errorlevel 1 call :next
endlocal
)
) else (
for /F "delims=" %%a in ('dir /B /A:-D "%this%" 2^>NUL') do echo %%~Fa
)
exit /B
EDIT: I fixed a small bug in the last for /F command.
For example, the output of WCDIR.bat C:\Windows\Sys*\find*.exe command in my Windows 8.1 64-bits computer is:
C:\Windows\System32\find.exe
C:\Windows\System32\findstr.exe
C:\Windows\SysWOW64\find.exe
C:\Windows\SysWOW64\findstr.exe
You can try with the command Where /?
The WHERE command is roughly equivalent to the UNIX 'which' command. By default, the search is done in the current directory and in the PATH.
#echo off
Where /R "%programfiles%" *winrar.exe
pause
#echo off
:: Example d'input
set UserInput=*drive*
:: building the Pattern
set cmd=%Userinput%.exe
:: storage Where.exe command in a macro, the execution will be faster
set whereCmd=where.exe /r c:\windows\ %cmd%
:: execution of macro and output formatting
for /f %%a in ('%whereCmd%') do echo %%~nxa --^> %%a
pause

SETLOCAL ENABLEDELAYEDEXPANSION causes CD and PUSHD to not persist

I am trying to use setlocal enabledelayedexpansion and cd together in a batch script, which seems to not persist changes back to shell.
The reason I need setlocal enabledelayedexpansion is that I need the variables in the script to be expanded dynamically upon runtime of the script.
Consider the below sample batch file :
a.bat
================================
setlocal enabledelayedexpansion
cd ..
The above batch file does not migrate to previous directory as expected !
Check this.
Blorgbeard provided an explanation as to why CD issued after SETLOCAL does not persist after ENDLOCAL (could be explicit or implicit).
Here is an interesting work-around. The PUSHD stack is independent of the environment that is saved/restored by SETLOCAL/ENDLOCAL. So the following simple sequence will preserve the directory change:
#echo off
setlocal
cd somePath
pushd .
endlocal
popd
Not very useful if somePath is constant - you could just as easily issue the CD after the ENDLOCAL. But it can be very useful if somePath is dynamic.
The problem is that setlocal causes any current directory changes to be local to the batch file.
See setlocal /?:
Begins localization of environment changes in a batch file. Environment
changes made after SETLOCAL has been issued are local to the batch file.
ENDLOCAL must be issued to restore the previous settings. When the end
of a batch script is reached, an implied ENDLOCAL is executed for any
outstanding SETLOCAL commands issued by that batch script.
Current directory is included in "environment changes".
Try this, notice that it echoes C:\ for %CD% inside the batch, but the current directory is still reset when the batch exits.
[11:42:00.17] C:\temp
> cat test.bat
#echo off
setlocal enabledelayedexpansion
cd ..
echo %CD%
[11:42:19.38] C:\temp
> test.bat
C:\
[11:42:23.00] C:\temp
>
Saving a string in registry across endlocal. Tested on win7 cmd (skip=2, may differ with different versions of reg.exe)
:: What: stash string in registry from cmd line, across endlocal barrier
:: PS. I added %RANDOM% to Windows-NT and Win95 command.com and still using it.
#echo off
set key="HKCU\Volatile Environment"
set keyname=stash%RANDOM%
set keytype=REG_SZ
setlocal ENABLEDELAYEDEXPANSION
::== Save string
set data_stash=Hello-%DATE%-%TIME%
echo Saving=%data_stash% in key=%keyname%
reg add %key% /v %keyname% /t %keytype% /d "%data_stash%" /f
endlocal
::== Read string
echo reg query %key% /v %keyname%
for /f "tokens=2* skip=2" %%a in ('reg query %key% /v %keyname%') do (
set data_unstashed=%%b
)
echo Read data_unstashed=%data_unstashed%
::== Delete stash
reg delete %key% /v %keyname% /f
Edit Stephan
another way to save variables beyond endlocal:
#echo off
set var1=hello
set var2=world
setlocal
echo previous: %var1%, %var2%
set var1=HELLO
set var2=WORLD
echo inside: %var1%, %var2%
endlocal & set "var1=%var1%" & set "var2=%var2%"
echo after: %var1%, %var2%
The trick is that due to the parsing the variables are expanded before endlocal is effective, but setted after that.
(sorry for hijacking your answer, but that's also too big for a comment)
Here is proof that it works - start the batch file in any folder above the root.
#echo off
setlocal enabledelayedexpansion
echo "%cd%"
cd ..
echo "%cd%"
pause

For Command help - nested command

I have created the following simple batch file.... What I'm trying to accomplish here is to connect using Remote Command (RCMD utility) to a list o servers defined in (clusters.txt) and delete a list of video files difines in (assets.txt)... I want to delete those assets on every server. This Batch file works however it has to connect and disconnect to delete every piece of asset and that's exactly what I'm trying to avoid. I want to be able to connect to 1 server and delete my list of assets %%B using scp del cmd then disconnect from that server and go to the next one on the list %%A and delete all the assets from there with just one connection instead of having to connect and disconnect 200 times.
#echo off
FOR /F "tokens=*" %%A IN (clusters.txt) DO FOR /F "tokens=*" %%B in (assets.txt) DO rcmd \\%%A \vstrmkit\scp del %%B
This is what the list of files looks like:
11E8A51A*
11E8A51D*
11E8A614*
11E88E4E*
11E88E4C*
and the list of server it's just computer hostnames with letters and numbers:
SEA88630-N0
Any help would be greatly appreciate it.
Without knowing the server and client versions of the OS, it is possible to have some kind of command line limitations. But, let's try
#echo off
rem prepare environment
setlocal enableextensions enabledelayedexpansion
rem Define the command that will be executed for each element
set "command=echo \vstrmkit\scp del"
rem Prepare the command for the list of files into a environment variable
set "command=set c=%command% "
for /f "tokens=*" %%f in (assets.txt) do set "command=!command!/#c#%%~f"
rem Adjust the command variable to generate a valid concatenation of commands
set "command=%command:/=&%"
rem Connect to each of the servers sending the generated command
rem In the last step, change the # with ! to generate on remote, when
rem delayed expansion is applied, the correct final command to execute
setlocal disabledelayedexpansion
for /f "tokens=*" %%a in (clusters.txt) do rcmd \\%%a cmd.exe /v:on /c "%command:#=!%"
Note that the line where the main command in defined, there is a echo inserted for testing. If everything seems ok, remove echo to do it work.
EDITED - Option number 2
It seems that there is some problem with quotes. The previous code works in my system but not in the OP. Let's try another one. It also works on my system.
#echo off
rem prepare environment
setlocal enableextensions disabledelayedexpansion
rem Define the command that will be executed for each element
set "command=echo \vstrmkit\scp del"
rem Define a temporary file to contain commands to process
set "tempFile=%temp%\%~nx0.tmp"
rem Generate the content of the temp file with the list of commands
rem to execute for each server
(
for /f "tokens=*" %%f in (assets.txt) do echo %command% %%f
echo exit
) > "%tempFile%"
rem Connect to each of the servers sending the generated command list
for /f "tokens=*" %%a in (clusters.txt) do (
rcmd \\%%a < "%tempFile%"
)
del /f /q "%tempFile%" > nul 2>nul
endlocal
#ECHO OFF
SETLOCAL
SET "filelist="
FOR /f "delims=" %%A IN (assets.txt) DO CALL SET filelist=%%filelist%% %%A
FOR /F "tokens=*" %%A IN (clusters.txt) DO echo rcmd \\%%A \vstrmkit\scp del %filelist%
GOTO :EOF
The required commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO rcmd to rcmd to execute the deletion
Possible solution if the command can't take multiple arguments:
Create a batch file called delasset.bat and copy it either into \vstrmkit on each target machine or some other directory on the path of each machine (eg c:\windows or c:\windows\system32)
delasset.bat should contain
#ECHO OFF
ECHO DEL %*
(Again, I've simply ECHOed the DEL - you'd need to remove the echo after verifying to activate)
Then change ...rcmd \\%%A \vstrmkit\scp del %filelist% to ...rcmd \\%%A \vstrmkit\scp DELASSET %filelist%
(it's case-insensitive; I've UPPERCASED it for emphasis)
Another possible way:
#ECHO OFF
SETLOCAL
SET "filelist="
FOR /f "delims=" %%A IN (assets.txt) DO CALL SET filelist=%%filelist%%:%%A
SET filelist=%filelist:~1%
FOR /F "tokens=*" %%A IN (clusters.txt) DO echo rcmd \\%%A \vstrmkit\scp del %filelist::=^&\vstrmkit\scp del %
GOTO :EOF
which should generate (eg)
rcmd \\SEA88630-N0 \vstrmkit\scp del 11E8A51A*&\vstrmkit\scp del 11E8A51D*&\vstrmkit\scp del 11E8A614*&\vstrmkit\scp del 11E88E4E*&\vstrmkit\scp del 11E88E4C*
Note that filelist is now built with colons separating the items. Colon cannot be part of a filename. The first character (which will be a colon, since filelist has :filenamefromfile appended to it) is then stripped off (see set/? from the prompt for substring documentation)
The hieroglypics in %filelist::=^&\vstrmkit\scp del % mean 'with filelist, substitute for (first :) [the characters between the first : and the = - which happens to be a colon] with the string &\vstrmkit\scp del (ie. all characters between the = and the closing % - with the additional modification that since & is a special character to cmd (it means 'do the thing before the &, then the thing after') the & needs to be "escaped" by a caret (^) which says "this is a special character that I want to use as an ordinary character".
Leaving the echo firmly in place will allow you to see precisely what should be delivered. Sadly, I've no idea whether it's correct...
Something like this should help.
I don't now for what you have an * after the file-name but it have to be removed by
the script. Or is that a .* ???
EDIT : IF you have various file with the same name and various ("Unknown") extensions then you have to test for the extensions with a new FOR loop :
#echo off
setlocal EnableDelayedExpansion
FOR /F "tokens=*" %%A IN ('type clusters.txt') DO (
FOR /F "tokens=*" %%B in ('type assets.txt') DO (
set $asset=%%B
rcmd.exe \\%%A \vstrmkit\scp del !$asset:~0,-1!.*))

For loop in Windows batch file: Error: "The syntax of the command is incorrect"

this is a follow-up to my question asked in this thread:
Extracting string after last instance of delimiter in a Batch file
I am working on writing a batch file that takes an unknown number of strings denoting directories from a java program (which calls the .bat) as arguments. These directories are saved in an "array" called "dirs".
for /F "tokens=2 delims==" %%z in ('set dirs[') do (
set "parm=%~1"
for %%a in (%parm:\= %) do set folder=%%a
echo %folder%
shift
)
The "dirs" array will hold on to the actual directory without changing it in any way, and eventually I will be using it with xcopy as the directory to be copied from. Parm takes the actual parameters put into the batch file and manipulates it to extract the last part of the directory. A variable called "folder" stores this directory name and prints it to the command line. However, when I run the program I constantly get an error saying "The syntax of the command is incorrect". I tried narrowing down where exactly the problem was, and it's definitely within the above loop. In order for the outer loop to work, does the %%z have to be operated on in some way? The loop works when I replace its contents with a simple "echo %%z", so it made me wonder if that's where the issue lies.
EDIT: Here is the updated loop, with the xcopy line included:
for /F "tokens=2 delims==" %%z in ('set dirs[') do (
set "param=%~1"
set param=!param:\= !
for %%a in (!param!) do set lastDir=%%a
echo !lastDir!
XCOPY %%z\*.* !outdrive!:\!dir!\!lastDir! /C /S /D /Y /I
shift
)
In case you need to know, "outdrive" is an external hard drive, "dir" is the backup directory created earlier in the program, and lastDir should be self-explanatory. It's the folder name, i.e. "pictures" or "documents"
Final Edit:
Thank you all who replied. I am kicking myself for not seeing the answer before, but I have figured out some code that works as expected. Here is the loop as it currently sits, operating properly:
for /F "tokens=2 delims==" %%z in ('set dirs[') do (
set param=%%z
set param=!param:\= !
for %%a in (!param!) do set lastDir=%%a
XCOPY %%z\*.* !outdrive!:\!dir!\!lastDir! /C /S /D /Y /I
)
Instead of using the parameters that were passed in to the batch file, I am working directly from the "dirs" array. I am then setting a different variable to the specified directory string, and operating upon that variable, rather than the string stored in the array.
Use Delayed Expansion and the changed syntax for !variables! if you need to use the variables in the loop.
The shift command is doing nothing to the loop variables.
Based upon the updated information, this should display the commands you need, and delayed expansion isn't needed in this code snippet.
#echo off
set outdrive=E
set dir=backup
set dirs[1]=c:\folders1
set dirs[2]=c:\folders2
set dirs[3]=c:\folders3
set dirs[4]=c:\folders4
for /F "tokens=2 delims==" %%z in ('set dirs[') do (
echo XCOPY "%%z\*.*" "%outdrive%:\%dir%\%%~nxz\" /C /S /D /Y /I
)
pause

Batch file to merge file names into a string doesn't work

I want to make a batch file, that would make me happy a long string of all file names in the directory.
So I have a directory:
-docA.pdf
-docB.pdf
-docC.pdf
And I want to have a string like this:
docA.pdf docB.pdf docC.pdf
In a nutshell:
http://bit.ly/u1eAHO
Long way:
I wrote a script
#echo off
pushd "%~dp0"
set "FILE_LIST=pre_"
FOR /F "tokens=*" %%i IN ('DIR /A:-D /B') DO (
IF NOT %%i==%~nx0 (
echo %%i
set "FILE_LIST=%FILE_LIST%%%i"
echo %FILE_LIST%
)
)
echo %FILE_LIST%
pause
And get this output:
docA.pdf
pre_
docB.pdf
pre_
docC.pdf
pre_
pre_docC.pdf
Why? How can I get the good one?
This is a pretty well-known problem with batch files. The short answer is that batch files are executed by expanding the variables in a line before executing them. For multiline blocks (the stuff in parentheses) have all of the non-loop variables expanded once and then the loop is run. Your loop body is essentially:
IF NOT %%i==%~nx0 (
echo %%i
set "FILE_LIST=pre_%%i"
echo pre_
)
They added a feature called delayed expansion somewhere along the way (NT4 IIRC) that you can enable in the registry or via a SETLOCAL statement. Change your script to something like:
#echo off
setlocal EnableDelayedExpansion EnableExtensions
pushd "%~dp0"
FOR /F "tokens=*" %%i IN ('DIR /A:-D /B') DO (
IF NOT %%i==%~nx0 (
set "FILE_LIST=!FILE_LIST!%%i"
)
)
echo %FILE_LIST%
endlocal
pause
and it should work. I'm away from my Windows box until Monday, but that should be close enough. If you run into any problems read the help shown by typing FOR /? and SETLOCAL /? in a command prompt.

Resources