DOS batch: get last folder from relative path - parsing

I have the following values in a DOS batch file (for example...):
..\Apple\Jones
..\Banana\Smith
..\Pear\Wilson
I need to extract the last name values ("Jones", "Smith", "Wilson") from each value. What one technique can I use that will always give me these substring values?

According to this topic : What is the best way to do a substring in a batch file?
I suggest you to use
%~n0

I already wrote a function for that. You give it any path and it returns you only it's filename or pathname. Works for any path: Url, Windows path, Unix path, etc...
Copy this function at the end of your batch script: (Instructions below)
rem ===========================================================================
:Name_From_Path
SetLocal
set _TMP_FOLDERNAME=%1
for %%g in ("%_TMP_FOLDERNAME%") do set _TMP_FOLDERNAME=%%~nxg
EndLocal & set _Name_From_Path=%_TMP_FOLDERNAME%
goto :EOF
rem ===========================================================================
Usage:
CALL :Name_Of_Path ..\Apple\Jones
ECHO %_Name_From_Path%
Result: Jones

Related

Is there a way to compose a batch of images with imagemagick?

I have a series of PNG images (ABC_a.png, ABC_b.png, XYZ_a.png, XYZ_b.png, BCA_a.png, BCA_b.png etc.) and would like to compose every image of the same code (i.e. the name of an image without _a or _b) within a folder.
Manually, the code would look like this:
magick composite ABC_b.png ABC_a.png ABC.png
magick composite XYZ_b.png XYZ_a.png XYZ.png
magick composite BCA_b.png BCA_a.BCA BCA.png
...
... where a partly transparent image _b would be placed "on top of" _a and the name of the output file losing its _a/_b suffix.
I looked around and tried several apporaches via mogrify or for loops (FOR %i IN (*.png) DO magick composite ...) but couldn't get it automated. Perhaps it would help to use two separate folders and working with the same image names (without the suffix), but I'm not sure...
I appreciate any tips. Please be aware that I'd need to work within the Windows CMD or PowerShell to make it happen.
There can be used the following batch file for this task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ImageMagick=magick.exe"
if exist "*!*_a.png" goto ExtendedVersion
setlocal EnableDelayedExpansion
for %%I in (*_a.png) do (
set "FileNameA=%%~nI"
set "FileName=!FileNameA:~0,-2!%%~xI"
if not exist "!FileName!" "!ImageMagick!" composite "!FileNameA:~0,-1!b%%~xI" "!FileNameA!%%~xI" "!FileName!"
)
endlocal
goto EndBatch
:ExtendedVersion
echo INFO: Extended version required because of a PNG file with exclamation marks.
for %%I in (*_a.png) do (
set "FileNameA=%%~nI"
setlocal EnableDelayedExpansion
set "FileName=!FileNameA:~0,-2!%%~xI"
if not exist "!FileName!" "!ImageMagick!" composite "!FileNameA:~0,-1!b%%~xI" "!FileNameA!%%~xI" "!FileName!"
endlocal
)
:EndBatch
endlocal
There is defined first the required execution environment with the first two command lines.
Next the environment variable ImageMagick is defined with the file name of this program. It would be best to add the full path because in this case cmd.exe would not need to search for magick.exe in current directory and next in all directories in string value of environment variable PATH using the file extensions in string value of environment variable PATHEXT before each execution of ImageMagick. The usage of the fully qualified file name of ImageMagick would avoid hundreds or even thousands of file system accesses on more than ten PNG files to process.
The IF condition in the fourth command line quickly checks if there is any PNG file with case-insensitive _a in the file name before the file extension .png containing one or more exclamation marks in the file name. The extended version of the processing loop is required if this condition is true.
The standard version enables first required delayed expansion. Then a FOR loop is used to process one PNG file after the other with case-insensitive _a in the file name before the file extension .png.
The current file name without file extension .png is assigned first to the environment variable FileNameA.
Next a string substitution is used to get from the string value of the environment variable FileNameA the file name without the last two characters _a concatenated with the file extension .png assigned to the environment variable FileName.
If there is not already a PNG file not ending with _a in the file name before the file extension, there is next executed ImageMagick with first argument being the corresponding _b.png file determined by using again a string substitution with using the file name string assigned to the environment variable FileName without last character a concatenated with b and the file extension .png and the_a.png file with the file extension as second argument and the file name without _a as third argument.
The command ENDLOCAL after the loop restores the previous environment before enabling delayed expansion and the command GOTO instructs the Windows Command Processor to continue processing the batch file with the command line below the label EndBatch which contains one more ENDLOCAL to restore the environment on starting the batch file processing.
The extended version is nearly the same as the standard version. The difference is that delayed variable expansion is not enabled on assigning the file name of the current _a.png file without the file extension to the environment variable FileNameA. That avoids interpreting the exclamation mark(s) in the file name as beginning/end of a delayed expanded variable reference resulting in a manipulation of the file name string before assigning it to the environment variable as it would happen with delayed expansion already enabled.
The extended version enables next delayed variable expansion inside the loop, does the same as the standard version and restores finally the previous environment before processing the next _a.png file.
The extended version is slower because of the environment variables list copy and the other operations made in background by every execution of SETLOCAL as explained in full details in this answer. The command ENDLOCAL in the loop is required to avoid a stack overflow on processing lots of PNG files.
To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.
echo /?
endlocal /?
for /?
goto /?
if /?
set /?
setlocal /?

DIR but select specific parts of file names

I have the habit for years to use this very easy and convenient .BAT file to create a simple TXT file of all my files in a directory:
dir *.* > LIST.TXT
but the result includes many pieces of information that I don't need
Now I have many files in a directory in this way:
file 01 [shYddYJQIGfffY].xyz
second file 00008 [shYddYJQIfGf11dfzrffY].exe
filex [shGffY].sys
file that i don't need.txt
many other files that i don't need.bat
1/ which command line can I use with DIR (or anything else) to have in the final LIST.txt files only those information? only the value between [ and ]
Expected result:
shYddYJQIGfffY
shYddYJQIfGf11dfzrffY
shGffY
2/ how to edit the .BAT file to add the string MYRESULT before each of those results, like:
MYRESULT shYddYJQIGfffY
MYRESULT shYddYJQIfGf11dfzrffY
MYRESULT shGffY
Use a for loop (see for /?):
for /f "tokens=2 delims=[]" %%a in ('dir /b *[*].*') do echo MYRESULT %%a
This for /f loop takes the output of dir /b *[*].* (filtering for relevant files, bare format) line per line and splits it into three parts, first token the part before the first [, token2 the part between [ and ]. and a third part after that. You need the second token only. Then just echo the string together with the extracted substring, done.

How to manipulate components of pathnames in Tcl?

I'm writing a reporting script that's part of a portable testing package. The user can unzip the package anywhere on their system, which is fine, but it means I can't hardcode a path.
Let's say that on my system, this script lives at C:/projects/testpackage/foo/bar/script.tcl. I need to set a variable, packageLocation, the path to /testpackage. In this example, it would be C:/Projects/testpackage. But when the user gets the package, he or she could put it anywhere, like so:
C:/Users/whatever/testpackage.
So, how can I call two levels up from the location of my currently running script? In Batch, I could do
:: example.bat
cd %~dp0
cd ../..
set packageLocation=%cd%
In Tcl, I'm lost. I know that the current location of the running script can be called as $::argv0. I've tried using cd ../.., but to no avail. It tries to set packageLocation as "../..C:/Projects/testpackage/foo/bar/script.tcl."
Any help is appreciated. Thanks!
set where [file normalize [file dirname [info script]]]
set parts [file split $where]
set pkgloc [file join {*}[lrange $parts 0 end-2]]
Should do what you want.
It goes like this:
Obtains the directory name of the file from which the current script was read to be evaluated, then normalizes it (replaces ~, .. etc).
Splits obtained full pathname at path separators producing a list of path components.
Extracts a new list from the list of path components containing all them from the beginning except the last two, then joins them back to produce the final name.
If you have Tcl < 8.5, the last line will have to be rewritten:
set last [expr {[llength $parts] - 3}]
set pkgloc [eval [list file join] [lrange $parts 0 $last]]

Make a relative path in batch file

I have a batch file that I wont know what drive letter it will be as I will be moving around alot.
For example:
The adobe files reside in: J:\Files\New folder\USB\Adob
The batch file is executed from: J:\Files\New folder\USB\USBSTICK
So I tried the code:
xcopy /s /y "%~dp0\..\..\USB\Adob\*" "C:\Program Files\"
But it will not copy the files. How can I get it dynamic?
As the drive letter seems to be the relative part of your scenario. I believe this should work better for you, unless I have misunderstood you.
xcopy /s /y "%~d0\Files\New folder\USB\Adob\*" "C:\Program Files\"
For more variables that you can use follow these steps:
From CMD, type for /? and read at the bottom.
%~I - expands %I removing any surrounding quotes (")
%~fI - expands %I to a fully qualified path name
%~dI - expands %I to a drive letter only
%~pI - expands %I to a path only
%~nI - expands %I to a file name only
%~xI - expands %I to a file extension only
%~sI - expanded path contains short names only
%~aI - expands %I to file attributes of file
%~tI - expands %I to date/time of file
%~zI - expands %I to size of file
%~$PATH:I - searches the directories listed in the PATH
environment variable and expands %I to the
fully qualified name of the first one found.
If the environment variable name is not
defined or the file is not found by the
search, then this modifier expands to the
empty string
The modifiers can be combined to get compound results:
%~dpI - expands %I to a drive letter and path only
%~nxI - expands %I to a file name and extension only
%~fsI - expands %I to a full path name with short names only
%~dp$PATH:I - searches the directories listed in the PATH
environment variable for %I and expands to the
drive letter and path of the first one found.
%~ftzaI - expands %I to a DIR like output line
In the above examples %I and PATH can be replaced by other valid
values. The %~ syntax is terminated by a valid FOR variable name.
Picking upper case variable names like %I makes it more readable and
avoids confusion with the modifiers, which are not case sensitive.
From my understanding of your hierarchy, this would also work:
xcopy /s /y "..\Adob\*" "C:\Program Files\"
since J:\Files\New folder\USB is a common prefix, and you're running the batch file from J:\Files\New folder\USB\USBSTICK, this should work no matter what drive letter you're dealing with.
I'm not sure that I fully understand your problem. But it seems to me that there are several solutions that would solve the problem as I understand it.
If the paths are always fixed, but only the drive letter may vary, you could just use a relative path:
xcopy /s /y "..\Adob\*" "C:\Program Files\"
Calling the batch program from any drive would then work as expected provided the batch file always exists in USBSTICK which exists in the same dir as Adob.
If the source path can vary, just replace the varying part with a parameter, and call the batch file with the correct path:
xcopy /s /y "%1\*" "C:\Program Files\"
Calling the batch program from any drive AND location would then work as expected when you supply it with the correct path:
xcopybatch J:\Files\New folder\USB\Adob
Try without the "\", as it is part of %~dp0. I mean, the contents of %~dp0 will always end with a "\".
Example:
xcopy /s /y "%~dp0..\..\USB\Adob\*" "C:\Program Files\"
Edit
Just in case ...
is Adob\* or Adob\*.* ??

How to get folder path from file path with CMD

I need path to the folder that contains cmd file.
With %0 I can get the file name. But how to get the folder name?
c:\temp\test.cmd >> test.cmd
P.S. My current directory != folder of the script.
For the folder name and drive, you can use:
echo %~dp0
You can get a lot more information using different modifiers:
%~I - expands %I removing any surrounding quotes (")
%~fI - expands %I to a fully qualified path name
%~dI - expands %I to a drive letter only
%~pI - expands %I to a path only
%~nI - expands %I to a file name only
%~xI - expands %I to a file extension only
%~sI - expanded path contains short names only
%~aI - expands %I to file attributes of file
%~tI - expands %I to date/time of file
%~zI - expands %I to size of file
The modifiers can be combined to get compound results:
%~dpI - expands %I to a drive letter and path only
%~nxI - expands %I to a file name and extension only
%~fsI - expands %I to a full path name with short names only
This is a copy paste from the "for /?" command on the prompt.
Related
Top 10 DOS Batch tips (Yes, DOS Batch...) shows batchparams.bat (link to source as a gist):
C:\Temp>batchparams.bat c:\windows\notepad.exe
%~1 = c:\windows\notepad.exe
%~f1 = c:\WINDOWS\NOTEPAD.EXE
%~d1 = c:
%~p1 = \WINDOWS\
%~n1 = NOTEPAD
%~x1 = .EXE
%~s1 = c:\WINDOWS\NOTEPAD.EXE
%~a1 = --a------
%~t1 = 08/25/2005 01:50 AM
%~z1 = 17920
%~$PATHATH:1 =
%~dp1 = c:\WINDOWS\
%~nx1 = NOTEPAD.EXE
%~dp$PATH:1 = c:\WINDOWS\
%~ftza1 = --a------ 08/25/2005 01:50 AM 17920 c:\WINDOWS\NOTEPAD.EXE
The accepted answer is helpful, but it isn't immediately obvious how to retrieve a filename from a path if you are NOT using passed in values. I was able to work this out from this thread, but in case others aren't so lucky, here is how it is done:
#echo off
setlocal enabledelayedexpansion enableextensions
set myPath=C:\Somewhere\Somewhere\SomeFile.txt
call :file_name_from_path result !myPath!
echo %result%
goto :eof
:file_name_from_path <resultVar> <pathVar>
(
set "%~1=%~nx2"
exit /b
)
:eof
endlocal
Now the :file_name_from_path function can be used anywhere to retrieve the value, not just for passed in arguments. This can be extremely helpful if the arguments can be passed into the file in an indeterminate order or the path isn't passed into the file at all.
In order to assign these to variables, be sure not to add spaces in front or after the equals sign:
set filepath=%~dp1
set filename=%~nx1
Then you should have no issues.
In case anyone wants an alternative method...
If it is the last subdirectory in the path, you can use this one-liner:
cd "c:\directory\subdirectory\filename.exe\..\.." && dir /ad /b /s
This would return the following:
c:\directory\subdirectory
The .... drops back to the previous directory.
/ad shows only directories
/b is a bare format listing
/s includes all subdirectories. This is used to get the full path of the directory to print.
I had same problem in my loop where i wanted to extract zip files in the same directory and then delete the zip file. The problem was that 7z requires the output folder, so i had to obtain the folder path of each file. Here is my solution:
FOR /F "usebackq tokens=1" %%i IN (`DIR /S/B *.zip` ) DO (
7z.exe x %%i -aoa -o%%i\..
)
%%i was a full filename path and %ii\.. simply returns the parent folder.
hope it helps.
In case the accepted answer by Wadih didn't work for you, try echo %CD%
This was put together with some edited example cmd
#Echo off
Echo ********************************************************
Echo * ZIP Folder Backup using 7Zip *
Echo * Usage: Source Folder, Destination Drive Letter *
Echo * Source Folder will be Zipped to Destination\Backups *
Echo ********************************************************
Echo off
set year=%date:~-4,4%
set month=%date:~-10,2%
set day=%date:~-7,2%
set hour=%time:~-11,2%
set hour=%hour: =0%
set min=%time:~-8,2%
SET /P src=Source Folder to Backup:
SET source=%src%\*
call :file_name_from_path nam %src%
SET /P destination=Backup Drive Letter:
set zipfilename=%nam%.%year%.%month%.%day%.%hour%%min%.zip
set dest="%destination%:\Backups\%zipfilename%"
set AppExePath="%ProgramFiles(x86)%\7-Zip\7z.exe"
if not exist %AppExePath% set AppExePath="%ProgramFiles%\7-Zip\7z.exe"
if not exist %AppExePath% goto notInstalled
echo Backing up %source% to %dest%
%AppExePath% a -r -tzip %dest% %source%
echo %source% backed up to %dest% is complete!
TIMEOUT 5
exit;
:file_name_from_path <resultVar> <pathVar>
(
set "%~1=%~nx2"
exit /b
)
:notInstalled
echo Can not find 7-Zip, please install it from:
echo http://7-zip.org/
:end
PAUSE
IMHO the simplest yet most powerful method to get the full path of a file is:
Start Notepad
Copy and Paste the following text:
#echo %1 | clip 3
Save the file as "CopyAsPath.bat"
Now you can either
drag a file over that batch
or
copy it into your SendTo menu folder (%USERPROFILE%\AppData\Roaming\Microsoft\Windows\SendTo)
Whether you follow the option 1 or the option 2, one millisecond later you can either
"press Ctrl-V"
or
use "Right Mouse Click -> Paste"
to paste the full path+filename wherever you want.
Simple, powerful, and without using any external Windows tool.

Resources