Batch File - Get filename from directory and save as variable - batch-file

I am trying to read in a directory and get the filename from that directory.
I then want to save the filename as a variable and echo that filename out.
Here is the code I am using:
for /F %%a in ('dir C:\Users\username\Documents\Training\Pentaho\Outputs\BatchFileOutput\ *.csv') do set FileName=%%a
echo %FileName%
I am getting the following when command prompt runs:
"File not found
Directory"
Does anyone know how to resolve this or where I'm going wrong?
Thanks

Safer way of doing the same:
#echo off
setlocal
set "yourDir=C:\Users\username\Documents\Training\Pentaho\Outputs\BatchFileOutput\"
set "yourExt=*.csv"
pushd %yourDir%
for %%a in (%yourExt%) do echo %%a
popd
endlocal
Sets both: Your directory and the extension you are searching for, Changes the directory to the one previously setted possibly including a /drive change and then runs a loop over all files matching your extension and echo them out. To save only the last one you can use:
...do set fileName=%%a
echo %FileName%
Or to use them all within the loop you can use:
#echo off
Setlocal EnableDelayedExpansion
REM Other things done here
do (
REM Do stuff with %%a here
Set filename=%%a
echo !filename!
echo !filename:~0,6!
echo !filename:a=b!
)
If you just want to echo them, you can just go for echo %%a. If you want to do other things like string-substitution or substrings as described in the comments you need DelayedExpansion as shown above. There are a lot of questions on SO as well.
Note that you can get different "parts" of the path of your file. Have a look on this answer I always have a look on as well. Alternatively check the documentation for the for command typing for /? into the command-line.

Related

How to properly use rename (ren) in Batch

I am trying to rename every image in a directory to add the date that each file was created, however, I keep either getting "invalid syntax" or "A duplicate file name exists, or the file cannot be found"
I am running Windows 10, and accessing the images off a flash drive (hence the short file path). I tried having all the code in one for-loop, when that didn't work I tried using batch functions, no dice. I did see someone mention on another thread to use delayed expansion, I would be up for using this if someone could give a better explanation than the /? command.
#echo off
REM batch file is placed in top of F drive, same as "images 2017+"
cd "F:\images 2017+"
FOR /R "F:\images 2017+" %%F in (*.jpg) do call :renER "%%~nF" "%%~tF"
goto :eof
:renER
cd "F:\images 2017+"
pause
echo %1
echo %2
rename %1.jpg %1_%2.jpg
pause
goto :eof
:end
For every .jpg file in "images 2017+", the date which that file was created would be stuck onto the end after a space.
thisIsMyFile.jpg made at 5-13-2017, would become thisIsMyFile 5-13-2017.jpg
Current output
EDIT:
I am CDing into the same directory as the images are, then using the passed variables to locate the correct image (The date is one of the passed variables, and shows up in the echo command).
I notice that you only want the date, not the time so you can do that as follows using your existing Call to a label, There is also no need to use FOR /R in this case so I'll use a normal for loop:
#echo off
FOR %%A IN ("F:\images 2017+\*.jpg") DO (
CALL :RenER "%%~fA" %%~tA
)
GOTO :eof
:RenER
PAUSE
ECHO %1
ECHO %2
SET "_tmp=%~2"
SET "_tmp=%tmp:/=-"
REN "%~1" "%~n1_%_tmp%%~x1"
PAUSE
GOTO :eof
Notice how above we are dropping the Time off immediately by not wrapping it in quotes since you don't want that to be part of the file name.
You can also forgo the call to a label entirely without needing delayed expansion by using a second loop, as a matter of preference I think this is quite a bit cleaner!
#echo off
FOR %%A IN ("F:\images 2017+\*.jpg") DO (
FOR /F "Tokens=1-3 Delims=/ " %%a IN ('echo.%%~tA') DO (
PAUSE
ECHO.%%~fA
ECHO.%%~tA
REN "%%~fA" "%%~nA_%%a-%%b-%%c%%~xA"
PAUSE
)
)
this is nice and clean and with a minor edit we can paste it directly into the CMD Prompt which is nicer still This is because we are not using DelayedExpansion, Calling a Label, or using Temp variables so by changing the %%s to %s, we can then Paste this directly into the CMD Line which is often more convenient when doing these sorts of operations:
This Multi-line will do just fine to be pasted into CMD directly:
FOR %%A IN ("F:\images 2017+\*.jpg") DO (
FOR /F "Tokens=1-3 Delims=/ " %a IN ('echo.%~tA') DO #(
PAUSE
ECHO.%~fA
ECHO.%~tA
REN "%~fA" "%~nA_%a-%b-%c%~xA"
PAUSE
)
)
or, as a single line to paste into CMD if you prefer:
FOR %A IN ("F:\images 2017+\*.jpg") DO #( FOR /F "Tokens=1-3 Delims=/ " %a IN ('echo.%~tA') DO #( PAUSE& ECHO.%~fA& ECHO.%~tA& REN "%~fA" "%~nA_%a-%b-%c%~xA"& PAUSE ) )
no need to cd anywhere. ren takes a full path/filename for source - just the destination must be a filename only. So ... do call :renER "%%~fF" "%%~tF" is fine (no need to snip the extension and add it again later). In the subroutine reformat the time to a valid string and reassemble the destination file name:
#echo off
FOR /R "F:\images 2017+" %%F in (*.jpg) do call :renER "%%~fF" "%%~tF"
goto :eof
:renER
pause
echo %1
echo %2
set "string=%~2"
set "string=%string::=-%"
set "string=%string:/=-"
ECHO rename "%~1" "%~n1_%string%%~x1"
pause
goto :eof
:end
NOTE: I disarmed the rename command. Remove the ECHO after troubleshooting, if it works as intended.
#Stephan's answer is probably the best approach. But if you want to change directories ...
The windows shell has a working drive/volume, and on each drive/volume a current working folder. cd changes the working folder on a disk; to change the working folder on a drive (which is not the working drive) and to make that drive the working drive, you need to use cd /d, in this case cd /d "F:\images 2017+".
(A plain cd in this instance changes the working folder on F:\, but if your working folder is on C: -- as I'm guessing is the case -- it will not be changed.)
Assuming command extensions are enabled, you should also be able to use pushd and popd. pushd behaves like cd /d but also saves your previous location; popd returns you to that previous location. (And IIRC pushd will accept UNC paths.)
So at the beginning of your script, pushd "F:\images 2017+", and at the end popd.
I tend to favor pushd/popd over cd because invocations can be nested. So you can do things like
(assume working directory is C:\Users\IoCalisto):
pushd "F:\images 2017+"
(working directory is now F:\images 2017+)
pushd "Z:\images 2015-2016"
(working directory is now Z:\images 2015-2016)
popd
(working directory is now F:\images 2017+)
popd
(working directory is now C:\Users\IoCalisto)
... with this approach, your scripts will have fewer "side effects" and be more modular, or at least modularizable.

Rename Files using wildcard paths

Recently I started working and my first task is to write a batch file that automatically changes filenames to filename_date with the original file-ending.
For that you should be able to write paths into a textfile (e.g. paths.txt) and when you start the program, it should take any line (=path->file) from there and rename it.
I got it to work on my PC quiet well but as I gave it to testing they asked to make the use of wildcards Z:\Path\*.* possible.
My current code looks as follows:
#echo off
setlocal EnableDelayedExpansion
cd %~dp0
For /F "tokens=*" %%m in (paths.txt) do (
set path=%%~dpm
set name=%%~nxm
pushd "!path!"
dir
For /r !path! %%f in (!name!) do (
set path=%%~dpf
set name=%%~nf
set ending=%%~xf
set datsave=%%~nxf
set "name=!name!_"
set "name=!name!!date:~6,4!"
set "name=!name!!date:~3,2!"
set "name=!name!!date:~0,2!"
set "name=!name!!ending!"
copy "!datsave!" "!name!"
del "!datsave!"
cls
popd
)
)
I know that a lot of it is probably easier and more efficient to do, but this is my first batch project and I am quiet happy except for the wildcard problem.
So an example would be:
C:\Some\Path\*.*
This line would be in paths.txt.
With the splitting
set path=%%~dpf
set name=%%~nf
set ending=%%~xf
set datsave=%%~nxf
I get the following:
path: C:\Some\Path
name: C:\Some\Path
ending: -empty-
datsave: C:\Some\Path
because name is set to the Path at the start of the first FOR-Loop. But that seems to be working if I do not use wildcards.
Now the question: Why does this happen and how do I get rid of it? Or do I just use the wrong type of wildcards?
Again: This is my first time I work with batch, so it might be something simple ;)
Ok, I figured out 2 problems and now it works
set name=%%~nxm evaluates the wildcard. Even if name is *.txt it will return bar.txt.
I replaced that by a basename computation instead: set name=!name:*\=! done enough times (not very subtle but hey batch files forces us to do such things) which preserves the wildcard
The other problem is the for /R loop: after pushd, the argument needs to be . or it won't be scanned.
Last minor one: use rename instead of copy plus delete. It preserves file time and is very fast. Copying then deleting a large file can take a long time.
#echo off
set DEPTH=20
setlocal EnableDelayedExpansion
cd %~dp0
For /F %%m in (paths.txt) do (
set pth=%%~dpm
set z=%%m
set name=!z!
rem brutal basename. We cannot break the inner loop or
rem it would break the upper loop too
for /L %%I in (1,1,%DEPTH%) do set name=!name:*\=!
rem but we can check if it is really a basename
set chkname=!name:*\=!
if not !chkname!==!name! ( echo please increase DEPTH value
pause
exit /B)
rem set name=%%~nxm
pushd "!pth!"
For /r . %%f in (!name!) do (
set pth=%%~dpf
set name=%%~nf
set ending=%%~xf
set datsave=%%~nxf
set "name=!name!_!date:~6,4!!date:~3,2!!date:~0,2!!ending!
echo renaming "!datsave!" to "!name!"
rem ren "!datsave!" "!name!"
popd
)
)
paths.txt contains just a line C:\full\path\to\test\*.txt
my test directory contains 2 text files and 1 other file
output:
renaming "bar.txt" to "bar_20160812.txt"
renaming "foo.txt" to "foo_20160812.txt"
(just uncomment the ren line to get the job done)
Weeeeell First of all thanks again to #Jean-François Fabre and #aschipfl for their patience with me :)
After the hint with the second batch file I had to test a few things as not everything worked as fine, but now everything works great!
Code of the Main file:
#echo off
setlocal EnableDelayedExpansion
cd %~dp0
set DEPTH=20
For /F %%m in (paths.txt) do (
pause
set pth=%%~dpm
REM pushd !pth!
REM set origpth=!cd!
REM popd
set z=%%m
set name=!z!
For /L %%i in (1,1,%DEPTH%) do set
name=!name:*\=!
set chkname=!name:*\=!
if not !chkname!==!name! ( echo depth to small
pause
exit /B)
rem set name=%%~nxm
pushd "!pth!"
For /r . %%f in (!name!) do (
pushd %~dp0
call renamefiles.bat %%f REM "!origpth!"
popd
)
)
And the code of the sub-file:
#echo off
REM set pth=%~dp1
REM set origpth=%2
REM set origpth=%origpath:"=%\
REM If !pth!==%origpth% (
set path=%~dp1
set name=%~n1
set ending=%~x1
set datsave=%~nx1
pushd !path!
set "name=!name!_!date:~6,4!!date:~3,2!!date:~0,2!!ending!"
pause
echo renaming "!datsave!" to "!name!"
rem "!datsave!" "!name!"
cls
popd
REM )
EDIT: After testing around a bit I figured, that subfolders are included as well! I put extra code to both codes marked with REM and two extra spaces. Take out those REM's and the programm will not longer include subfolders when renaming :)

Batch Finding and displaying duplicate strings in file names

I have a question. Is it possibile in batch language to search in folder a part of name that is same like another file and display it.For example i got folder with files :
ggggggsss.mp3
ddddddeee.mp3
ddddddff.mp3
ssssssddd.mp3
aaaaasssss.mp3
11111ssdas.mp3
11111dddd.mp3
...
I need to display in cmd only names of files
ddddddeee
ddddddff
and
11111ssdas
11111dddddd
Because the first six letter are the same. Could someone help me with this problem?
Save this script to test.bat and run from open Cmd Prompt. Replace dir value with path to your folder with .mp3 files:
#echo off
setlocal enabledelayedexpansion
set "dir=%userprofile%\music"
set "pattern1=dddddd" & set "pattern2=11111"
pushd "%dir%"
FOR %%G IN (*.mp3) DO ( set song=%%G
if "!song:~0,6!"=="%pattern1%" echo %%G)
echo/
FOR %%G IN (*.mp3) DO ( set song=%%G
if "!song:~0,5!"=="%pattern2%" echo %%G)
popd
exit /b
See also Extract Substrings.

Padding filenames with 0s in batch

I'm trying to edit some filenames with padded 0s from the command line. I want T9 to become T009 for example.
I tried:
#echo off
set num=99
set newnum=000%num%
set newnum=%newnum:~-4%
rename "T%num%.bmp" "T%newnum%.bmp"
pause
which did indeed changed file t99.bmp to T0099.bmp as I expected. However, when I try a loop like:
#echo off
FOR /L %%G IN (1,1,100) DO (
set num=%%G
echo %%G
set newnum=0000%num%
set newnum=%newnum:~-4%
echo %newnum%
ren "T%num%.bmp" "T%newnum%.bmp"
)
pause
It tells me that 'ECHO is off' and 'The system cannot find the file specified.' Note, it DOES echo %%G so I have no clue what's going on. I think it has something to do with being inside the loop? But I haven't been able to figure that out by googling.
My overall question is: Is there an obvious mistake in my methodology? and Why does the first chunk of code behave as I expect, but not the second?
I just learned batch like an hour ago and so far it's given me a headache. haha.
Set EnableDelayedExpansion then reference the variables with !var! instead of %var%
Fixed code:
#echo off
setlocal EnableDelayedExpansion
FOR /L %%G IN (1,1,100) DO (
set num=%%G
echo !num!
set newnum=0000!num!
set newnum=!newnum:~-4!
echo !newnum!
ren "T%%G.bmp" "T!newnum!.bmp"
)
pause
endlocal
ECHO IS OFF will appear if the variable you are attempting to echo is empty.
For the file not found part, that simply means that the file you are attempting to process is not found. Make sure the .bat file is in the same folder as the .bmp image's. If not, either move it there or cd your way to the path.
To read more about EnableDelayedExpansion, click here
Also, just to let you know, setting the num variable as you did is unnecessary, set newnum=0000%%G would work fine.

Windows batch file: get last folder name from path

I'm trying to rename .jpg files which is in one of many subdirectories of e:\study\pubpmc\test\extracted.
I want to rename files to LastFolderName_ImageName.jpg.
(For example if Figure1.jpg is in e:\study\pubpmc\test\extracted\folder1
I want it to be renamed like this: folder1_Figure1.jpg)
So I need to take out the last folder name from the file's path.
Since it's my first time with batch scripting, I'm having a hard time.
I googled and made code similar to it
but it doesn't seem to work out.
Can you help me with it and tell me where I've done wrong?
Thank you! :)
#echo off
cd /D "e:\study\pubpmc\test\extracted"
for /r %%f in (*.jpg) do (
set mydir=%%~dpf
set mydir=%mydir:\=;%
for /f "tokens=* delims=;" %%i in (%mydir%) do call :LAST_FOLDER %%i
goto :EOF
:LAST_FOLDER
if "%1"=="" (
#echo %LAST%
goto :EOF
)
set LAST=%1
SHIFT
goto :LAST_FOLDER
)
JosefZ explains the obvious problems with your code, but he failed to point out a subtle problem, though his code fixed it:
FOR /R (as well as the simple FOR) begin iterating immediately, before it has finished scanning the disk drive. It is possible for the loop to reiterate the already named file! This would cause it to be renamed twice, giving the wrong result. The solution is to use FOR /F with command 'DIR /B', because FOR /F always processes the command to completion before iterating.
JosefZ also provides code that works for most situations. But there is a much simpler solution that works always:
#echo off
for /f "delims=" %%A in (
'dir /b /s /a-d "e:\study\pubpmc\test\extracted\*.jpg"'
) do for %%B in ("%%A\..") do ren "%%A" "%%~nxB_%%~nxA"
The "%%A\.." treats the file name as a folder and walks up to the parent folder. So %%~nxB gives the name of the parent folder.
The command could be run as a long one liner, directly from the command line (no batch):
for /f "delims=" %A in ('dir /b /s /a-d "e:\study\pubpmc\test\extracted\*.jpg"') do #for %B in ("%A\..") do #ren "%A" "%~nxB_%~nxA"
Avoid using :label and :: label-like comment inside (command block in parentheses). Using any of them within parentheses - including FOR and IF commands - will break their context.
Using variables inside (command block in parentheses). Read EnableDelayedExpansion: Delayed Expansion will cause variables to be expanded at execution time rather than at parse time [and CLI parses all the (command block in parentheses) at once]
Next script should work for you. Note rename statement is merely echoed for debugging purposes.
#ECHO OFF >NUL
SETLOCAL enableextensions disabledelayedexpansion
set "fromFolder=e:\study\pubpmc\test\extracted"
rem my debug setting set "fromFolder=D:\path"
for /F "tokens=*" %%f in ('dir /B /S /A:D "%fromFolder%\*.*"') do (
set "mydir=%%~ff"
set "last=%%~nxf"
call :renameJPG
)
#ENDLOCAL
goto :eof
:renameJPG
rem echo "%mydir%" "%last%"
for /f "tokens=*" %%i in ('dir /B /A:-D "%mydir%\*.jpg" 2^>nul') do (
echo ren "%mydir%\%%~nxi" "%last%_%%~nxi"
)
goto :eof
Resources:
SETLOCAL, disableDelayedExpansion, ENDLOCAL etc.
An A-Z Index of the Windows CMD command line
Windows CMD Shell Command Line Syntax
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, Linux 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 e:\study\pubpmc\test\extracted\folder1
ECHO %_Name_From_Path%
Result: folder1
If your program or com file traverses these folders when renaming, then it should be able to get the present working directory ( path ), pwd. You may be able to chop everything but the LAST_FOLDER out of this by also creating a PREVIOUS_FOLDER and doing a string replacement.
Or you may be able to break the folder names at the '\' token from the pwd into an array and use a -1 array reference to get the last folder name.
In any circumstance you'll want to check for a present working directory command.
If your creating a large text list of all these and issuing a single call to the batch file.
Then you may be better off with something like:
(Symmantic code warning )
(copy) /folderbase/some_folder/oneormore1/image_from_oneormore1.jpg (to) /folderbase/some_folder/oneormore1/oneormore1_image_from_oneormore1.jpg
Instead of copy, window uses rename, linux uses mv.
The latter example would require simply creating a duplicate list and replacing the \ with a _ while parsing through the tokens.
The code you've given is difficult to make sense of, so its hard to discern if you can simple concatenate the current folder and image name (stringify) and then write or rename them where they are.

Resources