script-being-invoked.cmd:
REM This is my script file, it is being invoked from anywhere
REM Get absolute path to root directory
set astr=%~dp0
set substr1=\mq_1.7.6\interface\
set substr2=\
call set rootPath=%%astr:%substr1%=%substr2%%%
I'm trying to get the absolute directory path a few paths below from the file being invoked in the interface directory. The issue however is that the path in the code above changes. The version in mq_1.7.6 could change. Is there anyway to get this value via cmd scripting? I would have done \..\.. but this doesn't seem to work in Windows.
I usually do that using the ~fi expansion pattern. Unfortunately this can only be used (at least to my knowledge) in a for loop.
set "parentdir=%~dp0.."
for %%i in ("%parentdir%") do set "realparent=%%~fi"
echo "%parentdir%"
echo "%realparent%"
If the above is in a batch file located in c:\foo\bar\mq_1.7.6\interface the variable realparent will contain c:\foo\bar\mq_1.7.6 and parentdir would contain c:\foo\bar\mq_1.7.6\interface\...
So the for loop essentially turns a relative path into an absolute path.
It appears from your question, that you're trying to retrieve the name of the scripts grandparent, in this case mq_1.7.6.
It is still a little unclear however, whether you want that in isolation, i.e. mq_1.7.6, or as an absolute path, e.g. P:\ath\to\mq_1.7.6
If the former is your intention then the following should suffice:
#For %%A In ("%~f0\..\..")Do #Echo(%%~nxA
If you need that as a variable then:
#For %%A In ("%~f0\..\..")Do #Set "Gp=%%~nxA"
If you're just trying to capture the version string from mq_1.7.6, in this case 1.7.6, then a simple additional line may be all that is required:
#For %%A In ("%~f0\..\..")Do #Set "Gp=%%~nxA"
#Set "Gp=%Gp:*_=%"
If on the other hand you wanted the latter, i.e. the absolute path of the scripts grandparent, the the only real difference is in the metavariable expansion:
#For %%A In ("%~f0\..\..")Do #Echo(%%~fA
If you need that as a variable then:
#For %%A In ("%~f0\..\..")Do #Set "Gp=%%~fA"
If neither of my interpretations of your intent are correct, can you please provide an example of what you wanting to be returned, and whether you want that simply printing to the console, or saving as a variable etc.
Here's one method:
#pushd "%~dp0..\"
#echo %cd%
#popd
shorter one line:
#pushd "%~dp0..\" && echo %cd% && popd
Related
everything in my code is working fine except the last part.
I am wanting to output each text file to the folder with the same name. It is outputing the three text files into the one folder PentahoOutputs. However I am wanting to output it as the following:
folder system2.object2.assets contains file system2.object2.assets
folder system3.object3.assets contains file system3.object3.assets
folder system4.object4.assets contains file system4.object4.assets
#echo off SetLocal EnableDelayedExpansion
SET DELIMS=,
SET COMMAND=AddChange
SET EN=EN
SET ASSETS=Assets
SET DIREC = C:\Users\user.username\Documents\Training\BatchFiles\PentahoOutputs\
SET DELIMS2=.
FOR /D %%a IN (C:\Users\user.username\Documents\Training\BatchFiles\PentahoOutputs\*) DO ( SET subdirs=%%a
result=!subdirs:~71,7!
result2=!subdirs:~79,7!
set "concats=!result!!delims!!result2!!DELIMS!!COMMAND!!DELIMS!!EN!"
echo !concats!
echo !CONCATS! >>C:\Users\user.username\Documents\Training\BatchFiles\PentahoOutputs\!result!!delims2!!result2!!delims2!!assets!.CSV
)
PAUSE>NUL
edit ********** below
changing the problem code to the following puts each of the three files in each of the three folders... however i want one file in each folder
for /d %%b in (C:\Users\usre.username\Documents\Training\BatchFiles\PentahoOutputs\*) DO ( echo !CONCATS! >>%%b\!result!!delims2!!result2!!delims2!!assets!.csv )
From your posted code - given aschipfl's change as noted (although you don't attempt to use direc)
Your posted code has been mangled in an attempt, I assume, to disguise usernames. It also appears that you've cut down the actual code to show only the relevant section. This is good and understandable (but your edit has a glaring typo in the code - which is why you should cut-and-paste as far as possible.)
So - the setlocal following the #echo off must be separated by a & command-concatenator or be (my preference) on a separate line.
Within your for ... %%a ... block, you've removed the required set keyword for result*.
The fixed values you've used for substringing don't suit the changes you've made to the pathname, so the result in result is (eg) "tem3.ob"
If a value does not change within a block (like delims) then it's probably best to use %delims% - result changes, so you'd use !result! not %result%. !delims! also works, of course - but using the delayed-expansion form primes the reader to believe it's going to vary. (opinion)
'tis best with a string assignment to use set "var=value" as the quotes ensure that stray trailing spaces are not included in the value assigned. You only ever need to have that happen once...
OK - here's a revision
#echo OFF
SetLocal
SET DELIMS=,
SET COMMAND=AddChange
SET EN=EN
SET ASSETS=Assets
SET DIREC=U:\Users\user.username\Documents\Training\BatchFiles\PentahoOutputs\
SET DELIMS2=.
FOR /D %%a IN (%direc%*) DO (
FOR /f "tokens=1,2,3 delims=." %%p IN ("%%~nxa") DO IF /i "%%r"=="%assets%" (
echo %%p%delims%%%q%DELIMS%%COMMAND%%DELIMS%%EN%
echo %%p%delims%%%q%DELIMS%%COMMAND%%DELIMS%%EN% >> %%a\%%~na.CSV
)
)
GOTO :EOF
Note that I've used U: for the test directory (it's a ramdrive on my machine)
Given the outer loop, %%a is assigned the full pathname to the directory.
Since you imply that your target directorynames are system2.object2.assets then %%~nxa (the Name and eXtension of %%a) conveniently holds this string. Parsing that using delims of . and selecting the first 3 tokens would assign system2 to %%p, object2 to %%q and assets to %%r This avoids the substringing problem and permits system and object to be any length - not just 7.
The if statement ensures that the main block for for...%%p is only executed for directories found which fit ..asset (/i makes the if case-insensitive)
The required line can then be constructed from the metavariables and constants, as can the destination filename, so the enabledelayedexpansion is not required.
In a Batch file I need to add some paths to the PATH env variable. Since its a larger numer of long paths I tried to spread them on multiple line and tried to make the bat-file as clean as I can by indenting the lines.
But it seems that the spaces at the beginning of the newline (and so in the %PATH%) are interpreted as part of the actual path.
So
SET PATH=^
\\somewhere\Tools\strawberryperl\perl\site\bin;^
\\somewhere\Tools\strawberryperl\perl\bin;^
\\somewhere\Tools\strawberryperl\c\bin;^
\\somewhere\Tools\KDiff3;^
%PATH%
does not work (programs are not found). Is there some trick I can use?
Because it is a medium complex batch file some indentation would be nice.
for %%x in (
"\\somewhere\Tools\strawberryperl\perl\site\bin;"
"\\somewhere\Tools\strawberryperl\perl\bin;"
"\\somewhere\Tools\strawberryperl\c\bin;"
"\\somewhere\Tools\KDiff3;"
) do call set "path=%%path%%%%~x"
this will append the extra items to the path. You'd need to initialise path to nothing first if all you want is to build the directory sequence specified.
There is no way to have PATH ignore the leading spaces. I see two possible options if you want indented lines:
Option 1 - Use undefined variable to indent, so spaces never get in the value
#echo off
SET PATH=^
% =%\\somewhere\Tools\strawberryperl\perl\site\bin;^
% =%\\somewhere\Tools\strawberryperl\perl\bin;^
% =%\\somewhere\Tools\strawberryperl\c\bin;^
% =%\\somewhere\Tools\KDiff3;^
% =%%PATH%
Option 2 - Remove the spaces afterwards
#echo off
SET PATH=^
\\somewhere\Tools\strawberryperl\perl\site\bin;^
\\somewhere\Tools\strawberryperl\perl\bin;^
\\somewhere\Tools\strawberryperl\c\bin;^
\\somewhere\Tools\KDiff3;^
%PATH%
set "PATH=%PATH:; =%"
First let me start by informing you that adding to the PATH variable in this way is ONLY for the running session. Once the cmd session is closed that variable returns to its previous value.
Here are a suggestion, append each addition one by one:
SET "ToAdd=\\somewhere\Tools\strawberryperl\perl\site\bin;"
SET "ToAdd=%ToAdd%;\\somewhere\Tools\strawberryperl\perl\bin;"
SET "ToAdd=%ToAdd%;\\somewhere\Tools\strawberryperl\c\bin;"
SET "ToAdd=%ToAdd%;\\somewhere\Tools\KDiff3"
SET "PATH=%PATH%;%ToAdd%"
BTW, if you were hoping to add to the environment variable beyond the running session then it is important that you ignore anyone suggesting you use SETX instead of SET. (the variable will be truncated at 1024 bytes therefore corrupting it). Your best solutions would involve editing the registry and possibly using a built in tool such as powershell.
Edit
This shows the method mentioned in my comment and uses the same structure as Magoo's answer:
C:\MyDir\Paths.txt
\\somewhere\Tools\strawberryperl\perl\site\bin
\\somewhere\Tools\strawberryperl\perl\bin
\\somewhere\Tools\strawberryperl\c\bin
\\somewhere\Tools\KDiff3
batch file
#Echo Off
SetLocal EnableDelayedExpansion
For /F "UseBackQDelims=" %%A In ("C:\MyDir\paths.txt") Do Set "Path=!Path!;%%~A"
Echo(%Path%
EndLocal
Timeout -1
This means that you only really need to include the for loop each time instead of adding each of the paths to it.
Not even remotely bulletproof, but the Magoo's answer reminded me this. Just because someone, somewhere, could find a better usage for this construct
#echo off
setlocal enableextensions disabledelayedexpansion
for /f "delims=" %%a in ('echo "%path:;=" "%"
"\\somewhere\Tools\strawberryperl\perl\site\bin"
"\\somewhere\Tools\strawberryperl\perl\bin"
"\\somewhere\Tools\strawberryperl\c\bin"
"\\somewhere\Tools\KDiff3"
""') do (set "path=%%~a") & call set "path=%%path:" "=;%%"
path
I am moving files based on their names to preset folders. I don't want to make new folders. So files should only be moved if the corresponding folder is existing already.
The file names all follow the same pattern: 1234_123456_AA_***********.(doc/pdf)
I have the following script below which works:
#echo on
for /r %%f in (*.*) do (
echo processing "%%f"
for /f "tokens=1-3 delims=_" %%a in ("%%~nxf") do (
move "%%f" C:\Users\xxxxxxxxx\Desktop\MOVEFILES\%%a_%%b_%%c\
)
)
pause
But the issue I am running into is that some of the files names have a '0' place holder in loop variable %%b, for example 1234_0123456_AA. But this file name should be interpreted like 1234_123456_AA and I want this file moved into the appropriate folder.
I have written this:
#echo on
SETLOCAL ENABLEDELAYEDEXPANSION
for /r %%f in (*.*) do (
for /f "tokens=1-3 delims=_" %%a in ("%%~nxf") do (
set z=%%b%
echo !z:~-6!
move "%%f" C:\Users\xxxxxxxxx\Desktop\MOVEFILES\%%a_%%z_%%c\
)
)
pause
I get the echo to remove the '0' place holder, but need to get that back into %%b in the file path of where the file should be moved to.
What to modify in code to get the desired behavior?
Use the following code with the corrections of JosefZ applied and some additional improvements:
#echo off
setlocal EnableDelayedExpansion
for /R %%F in (*_*_*.*) do (
for /F "tokens=1-3 delims=_" %%A in ("%%~nxF") do (
set "MiddlePart=%%B"
set "MiddlePart=!MiddlePart:~-6!"
if exist "%USERPROFILE%\Desktop\MOVEFILES\%%A_!MiddlePart!_%%C\*" (
move /Y "%%F" "%USERPROFILE%\Desktop\MOVEFILES\%%A_!MiddlePart!_%%C"
)
)
)
endlocal
pause
The improvements on code are explained below in details.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
if /?
move /?
set /?
setlocal /?
1. Search pattern
The first improvement is the pattern used in first FOR as only files should be moved with at least 2 underscores in file name. This pattern is still not the best one, but good enough for this task.
2. Loop variable
It is better to use loop variables in upper case to avoid problems with the modifiers.
For example using as loop variable %%f and using inside the loop %%~f to use the string (must not be a file or folder name) of loop variable f without surrounding quotes, command processor exits batch processing with an error message because it expects one more letter, the loop variable as %%~f is interpreted as full name of file/folder of loop variable ?.
The loop variables and the modifiers are case sensitive. Therefore %%~F is interpreted by command processor as string of loop variable F without surrounding quotes and %%~fF as file/folder name with full path and extension of the file/folder of loop variable F.
Some other characters like # can be also used as loop variable.
3. Assigning value to environment variable with quotes
On assigning a string to an environment variable, it is always good to use
set "variable=text or other variable"
Using the quotes as shown here has the advantage that not visible spaces/tabs after last double quote are ignored by command processor.
But with using just
set variable=text or other variable
everything after first equal sign up to line termination is assigned to the variable including trailing spaces and tabs added perhaps by mistake on this line in the batch file. This is nearly never good and a common source of a batch execution problem which can be easily avoided by using quotes right.
Using the quotes as show below is also not good as in this case both double quotes are part of the text assigned to the variable (plus trailing spaces/tabs). This is sometimes useful, but is most often a coding mistake.
set variable="text or other variable"
4. Delayed expansion
Referencing a variable set or modified within a block defined with (...) requires delayed expansion if the current variable value should be used and not the value assigned to the variable above the block. Therefore using %%z was wrong in original code as variable z was not defined above first FOR loop and therefore was replaced always with nothing on execution of the loops.
5. Environment variable USERPROFILE
Running in a command prompt window set results in getting displayed all predefined environment variables for the current user account. There is the variable USERNAME, but also USERPROFILE containing path to the userĀ“s profile directory with the Desktop folder and other user account related folders. Using environment variable USERPROFILE makes the batch file less dependent on Windows version and language of Windows.
ATTENTION:
The first FOR runs because of /R recursive on current directory and all its subdirectories. As the inner FOR loop moves all found files in current directory tree to subdirectories of %USERPROFILE%\Desktop\MOVEFILES, the current directory should be never any directory of this path.
I am trying to write a batch file that calls another which replaces two files in a directory. Here is my code:
set mmcIpath="C:"*"\mmc-stable-win32\MultiMC\instances"
call C:\%mmcIpath%\spc_we_replace_CED+CEDU.bat
Whenever I set the temporary environment variable, it says the directory cannot be found.
--ADDITIONAL INFO--
I run a Minecraft launcher called MultiMC5; it has a feature which runs commands - but only one command, for some reason. So I wanted it to call a batch file to run multiple commands.
My main batch file is in "C:...\MultiMC\instances", but I want the program to be able to call it. It cannot, as it works within a subdirectory called "CED (210 mods-)". So I placed another batch in the subdirectory to call the main one (I wanted to do the same for a second subdirectory called "CEDU (300+ mods-)"). I got this error: "The system cannot find the path specified.". It happened when I set the path.
I'm using Windows 8.1 and have searched for tips on how to use wildcards and on how to use FOR loops, but none of the wildcard methods have worked for me and I cannot understand FOR loops at all. I have also tried to remove and add things like quotation marks in an attempt to fix it, but that didn't work either.
My question:
Is the set command compatible with wildcards and if so, how do I get this to work?
It looks like your problem is that the main batch file can be on any drive and you want to call other batch files with a path relative to the location of main batch file. Is that correct?
You can get the drive with %~d0, path with %~p0 and drive + path with %~dp0. See the example below and execute this little batch file stored in several different directories:
#echo off
echo Batch is stored on drive %~d0
echo in the directory %~p0
echo resulting in path %~dp0
So you can use argument 0 referenced by %0 containing always name of batch file with full path using the syntax above explained in help of command FOR displayed with entering for /?in a command prompt window to call the other batch files with a path depending on path of the main batch file.
Now what we have here is a classic XY problem - being asked about a solution, not the underlying issue.
Here's a possible resolution:
#ECHO OFF
SETLOCAL
set mmcIpath="C:"*"\mmc-stable-win32\MultiMC\instances"
set "mmcIpath=U:\\.*\\mmc-stable-win32\\MultiMC\\instances"
SET "targetbat=spc_we_replace.bat"
FOR /f "delims=" %%a IN (
'dir /b/s /ad "%mmcIpath:~0,3%"^|findstr /e /i /r /c:"%mmcIpath%" '
) DO (
IF EXIST "%%a\%targetbat%" (
ECHO CALL "%%a\%targetbat%"
ECHO GOTO nextstep
)
)
:nextstep
GOTO :EOF
I've left the original setting of mmcIpath in place, and replaced it with a form to suit my system.
The approach is to execute a dir/s/b/ad command (directory, basic format, with subdirectories, directory names only) and filter it using findstr. I chose switches /i (case-insensitive) /e (must end with string) /r regular-expression /c: (following is one string to be matched).
The regex is constructed according to findstr's rules - \ needs to be doubled if it is to be used as a literal; .* means 'any number of any character'
This should provide a list of literal directory names which get through the filter. Look in the directory found for the filename, call the target file if found. The required CALL commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO CALL to CALL to actually execute the files.
The following ECHO GOTO is there to show that the loop can be broken after finding the first target file, if desired. You havent't indicated whether you want to run only the first or run all of the targets found.
Here's my test source. I use U: for testing data. Your system would likely be different.
#ECHO OFF
SETLOCAL
SETLOCAL ENABLEDELAYEDEXPANSION
set "mmcIpath=\mmc-stable-win32\MultiMC\instances"
FOR /l %%a IN (1,1,4) DO MD u:\%%a%mmcIpath% 2>nul
FOR /l %%a IN (2,2,4) DO COPY NUL u:\%%a%mmcIpath%\spc_we_replace.bat 2>NUL >nul
ENDLOCAL
ENDLOCAL
#ECHO OFF
SETLOCAL
set mmcIpath="C:"*"\mmc-stable-win32\MultiMC\instances"
set "mmcIpath=U:\\.*\\mmc-stable-win32\\MultiMC\\instances"
SET "targetbat=spc_we_replace.bat"
FOR /f "delims=" %%a IN (
'dir /b/s /ad "%mmcIpath:~0,3%"^|findstr /e /i /r /c:"%mmcIpath%" '
) DO (
IF EXIST "%%a\%targetbat%" (
ECHO CALL "%%a\%targetbat%"
ECHO GOTO nextstep
)
)
:nextstep
GOTO :EOF
Note that the first section is merely establishing u:\1\mmc-stable-win32\MultiMC\instances to u:\4\mmc-stable-win32\MultiMC\instances then creating a dummy file spc_we_replace.bat in u:\2\mmc-stable-win32\MultiMC\instances and u:\4\mmc-stable-win32\MultiMC\instances.
The string assigned to mmcIath in the second section is according to findstr's syntax rules - each \ is doubled, .* means "any number of any character". You would have to modify that string to suit your system. Note that "%mmcIpath:~0,3%" gratuitously takes the first 3 characters from the string. In my case, that would be U:\. YMMV
I'm creating a simple production environment for work and in doing so need to set specific environment variables for specific projects in batch file.
Here's what i want to achieve:
1) Define a single environment variable which would define a list of directories
2) Recurse down each directory and add all leaf folders to a final environment variable.
[EDIT] After looking back at what i originally posted i was able to remove some redundancy. But the "The input line is too long." error occurs when %LINE% gets too long. Using the short path expansion does help but it can still error out. I'll look at how to break the echo to a temp file next as suggested.
Here's what i currently have:
#echo off
set RECURSE_THESE_DIRS=C:\Users\eric\Autodesk
set TMP_FILE=%CD%TMP_FILE.%RANDOM%.txt
setLocal EnableDelayedExpansion
for %%i in (%RECURSE_THESE_DIRS%) do (
if exist %%~si\NUL (
for /f "tokens=*" %%G in ('dir /b /s /a:d %%i') do set LIST=!LIST!;%%G
)
)
set LIST=%LIST:~1%
rem !!! AT THE ECHO LINE BELOW IF %LIST% IS TOO LONG, THIS SCRIPT FAILS
rem !!! WITH The input line is too long. ERROR :(
echo %LIST%>%TMP_FILE%
endlocal
for /F "delims=" %%G in (%TMP_FILE%) do set FINAL_VAR=%%G
del /F %TMP_FILE%
So by setting RECURSE_THESE_DIRS to directories i wish to parse, i end up with a %FINAL_VAR% which i can use to specify paths for proprietary software i use. Or i could use this script to append to %PATH%, etc...
This works for me but i would love suggestions to improve/streamline my script?
The root of your problem is that batch is limited to fit the variable name, contents and = into 8192 bytes, hence your directory-list simply isn't going to fit into one variable.
Personally, I'd just spit out a dir/s/b/a-d list to a tempfile and process that file with a for/f "delims=" - after all, you'd be likely to need to process your ;-separated envvar similarly in whatever process you are proposing to execute.
For instance, here's a test producing the same error - not using filenames at all
#ECHO OFF
SETLOCAL
SET "var=hello!1234"
SET var=%var%%var%%var%%var%%var%
SET var=%var%%var%%var%%var%%var%%var%%var%%var%
SET var=%var%%var%%var%%var%%var%
SET var=%var%%var%%var%%var%
SET count=8000
:loop
SET /a count +=1
ECHO %count%
SET var=%var%x
ECHO %var%
GOTO loop
GOTO :EOF
This should fail where count=8184.
Suggestions:
Use for /d /r to handle the recursion
Maybe i'm wrong, but in your script, you traverse the directory hierarchy, adding each directory to temp file which is then readed to concatenate its lines into a variable which is then writed to temp file, to be read again in another variable. If concatenation of directories fit into a variable, why not concatenate them without any temporary file?
If concatenation is in the limit of line length, as suggested by npocmaka, and if soported by end application, user short paths. Also, instead of adding the new values in the same instruction (not line) that contains the for loop, use a call to a subrutine with the new value passed to it, and then append the value. It's slower, but command lines executed will be shorter.