I would like to write a batch that will copy all of the files in a given source path to a given output path, and plot the target path of the files that were actually changed.
XCOPY is doing almost that. It plots the source paths.
I guess it would be easier with PowerShell, but I would rather keeping it in the bat file.
Extending Sumit's suggestion of the /F option - you could use FOR /F to parse out only the destination files. There is no need for a batch script. It can be a one liner on the interactive command line.
for /f "delims=> tokens=2" %A in ('xcopy /f "someSourcePath\*.txt" "someDestinationPath"') do #echo %A
The above will include an unwanted leading space on each line of output. If needed, you can use an extra FOR /F to remove the leading space.
for /f "delims=> tokens=2" %A in ('xcopy /f "someSourcePath\*.txt" "someDestinationPath"') do #for /f "tokens=* delims= " %B in ("%A") do #echo %B
Either way, the file count at the end is also removed.
Double all % as %% if you put the command in a batch script.
The solution is even simpler if you have my JREPL.BAT regular expression text processing utility - a purely script based utility (hybrid JScript/batch) that runs natively on any Windows machine from XP onward.
xcopy /F "someSourcePath\*.txt" "someDestinationPath" | jrepl "> (.*)" $1 /jmatch
If you want to preserve the file count at the end, then you could use an even simpler variation
xcopy /F "someSourcePath\*.txt" "someDestinationPath" | jrepl "^.*> " ""
You can print source path and new destination path together with F switch in xcopy
Consider this example
xcopy * DRIVE-LETTER:\New\ /F
Will output in this format
F:\OLD-FOLDER\DSCN.JPG -> F:\New\DSCN.JPG
F:\OLD-FOLDER\DSCN1.JPG -> F:\New\DSCN1.JPG
This script takes two arguments: the source dir and the dest dir
It is a functional example. (You'll need to customize the output to suit your needs.) The script does not check for errors. You may (or may not) wish to do that.
#SETLOCAL ENABLEDELAYEDEXPANSION
#ECHO OFF
REM
SET "SRC_PATH=%~1"
SET "DST_PATH=%~2"
MKDIR "%DST_PATH%"
REM Copy the files
FOR %%f IN (%SRC_PATH%\*.*) DO (
SET "SRC_FILE=%%~f"
SET "DST_FILE=%DST_PATH%\%%~nxf"
#ECHO SOURCE FILE: !SRC_FILE!
#ECHO DEST FILE : !DST_FILE!
COPY "!SRC_FILE!" "!DST_FILE!"
)
REM Recursively descend into the subdirectories
FOR /D %%d IN (%SRC_PATH%\*.*) DO (
SET "SRC_SUBDIR=%%~d"
SET "DST_SUBDIR=%DST_PATH%\%%~nxd"
#ECHO SOURCE DIR: !SRC_SUBDIR!
#ECHO DEST DIR : !DST_SUBDIR!
cmd /c %~s0 "!SRC_SUBDIR!" "!DST_SUBDIR!"
)
Alter the ECHO statements to do what you need.
Related
I am trying to create a batch file:
The batch file will locate the path of executable first. Then, the path will be stored in a variable for later use.
This is my code:
#echo off
setlocal
set directoryName=dir/s c:\ABCD.exe
rem run command
cmd /c %directoryName%
pause
endlocal
The command prompt does return me with the executable's path but the path is not stored in the variable. Why is it so?
Reading your question, it appears that you're not really wanting to save the path of the executable file at all, but the file name complete with it's full path:
I prefer the Where command for this type of search, this example searches the drive in which the current directory resides:
#Echo Off
Set "mPth="
For /F "Delims=" %%A In ('Where /R \ "ABCD.exe" /F 2^>Nul'
) Do Set "mPth=%%A"
If Not Defined mPth Exit /B
Rem Rest of code goes here
The variable %mPth% should contain what you need. I have designed it to automatically enclose the variable value in doublequotes, if you wish to not have those, change %%A on line 4 to %%~A. If the file is not found then the script will just Exit, if you wish it to do something else then you can add that functionality on line 5.
Note: the code could find more than one match, if it does it will save the variable value to the last one matched, which may not be the one you intended. A robust solution might want to include for this possibility.
Edit (this sets the variable, %mPth% to the path of the executable file only)
#Echo Off
Set "mPth="
For /F "Delims=" %%A In ('Where /R \ "ABCD.exe" /F 2^>Nul'
) Do Set "mPth=%%~dpA"
If Not Defined mPth Exit /B
Set "mPth=%mPth:~,-1%"
Rem Rest of code goes here
Lets walk through your code
set directoryName=dir/s c:\ABCD.exe
This fills the variable directory name with the value dir/s c:\ABCD.exe.
cmd /c %directoryName%
This executes the command in directoryname. There is no line in your code that saves the files location to a variable.
Extracting the path of a file can be done as follows
#echo off
setlocal
set executable=c:\location\ABCD.exe
FOR %%A IN ("%executable%") DO Set executablepath=%%~dpA
echo executablepath: %executablepath%
pause
endlocal
%executablepath% will contain c:\location\
The value that you are assigning to directoryname is dir /s c:\abc.exe.
this value is then substituted for %directoryname% in your cmd line, which executes the command dir/s..., showing you the location(s) of abc.exe in the familiar dir format.
If what you want is just the directoryname in directoryname, then you need
for /f "delims=" %%a in ('dir /s /B /a-d "c:\abc.exe"') do set "directoryname=%%~dpa"
which will first execute the dir command, then process each line of output from that command and assign it in its entirety to %%a.
The dir command shown would "display" the matching names found in the nominated directory (c:\) and its subdirectories (/s) in basic form (/b) - that is, names only, no size or date or report-headers or report-footers, and a-d without directorynames (should they match the "mask" abc.exe)
The delims= option to the for /f command instructs that the entire line as output by the command in single-quotes, be assigned to %%a.
When the result is assigned to the variable directoryname, only the Drive and Path parts are selected by using the ~dp prefix the the a.
Note that only the very last name found will be assigned to the variable as any earlier assignment will be overwritten by a succeeding assignment.
This may or may not be what you are looking for. This script searches through the PATH variable and looks for files that have and extension in the PATHEXT variable list.
#SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
#SET EXITCODE=1
:: Needs an argument.
#IF "x%1"=="x" (
#ECHO Usage: %0 ^<progName^>
GOTO TheEnd
)
#set newline=^
#REM Previous two (2) blank lines are required. Do not change!
#REM Ensure that the current working directory is first
#REM because that is where DOS looks first.
#PATH=.;!PATH!
#FOR /F "tokens=*" %%i in ("%PATH:;=!newline!%") DO #(
#IF EXIST %%i\%1 (
#ECHO %%i\%1
#SET EXITCODE=0
)
#FOR /F "tokens=*" %%x in ("%PATHEXT:;=!newline!%") DO #(
#IF EXIST %%i\%1%%x (
#ECHO %%i\%1%%x
#SET EXITCODE=0
)
)
)
:TheEnd
#EXIT /B %EXITCODE%
Note that this may find multiple executables. It may also find multiple types of executables. The current directory is also included first since that is what the shell, cmd.exe, does.
M:>whence wc
.\wc.BAT
.\wc.VBS
C:\Users\lit\bin\wc.BAT
C:\Users\lit\bin\wc.VBS
I am trying to get the file version of all the files inside a folder which I managed to do (but not in a good way) but now I want to stick the folder name along with the version so I would know which version is for which folder.
I am not very good in command line and only use it for some small tasks whenever I need it so my apology in advance..
Here is what I have done:
For /d %%a in (C:\high\low\*) Do (For /d %%* in (%%a) Do wmic datafile where name="%%~da\\high\\low\\%%~nx*\\bin\\Services.dll" get Version /value)
and I get output as:
`Version=2.2.0.1 Version=2.2.0.4 Version=2.2.0.4....Version=2.2.0.4
there are 20 folders under C:\high\low and I want to go into the bin directory of each sub folder so I can see which folder has been upgraded and which one is not.
Edit
There are more than 20 folders and structure is like this:
C:\high\low\office.Services.Bolton\bin\Services.dll
C:\high\low\office.Services.Slough\bin\Services.dll
C:\high\low\office.Services.Hull\bin\Services.dll
.
.
.
C:\high\low\office.Services.Cosham\bin\Services.dll
I want to check the version number of Services.dll and need the output as:
Bolton - 2.2.0.1
Slough - 2.3.0.1
Hull - 2.5.0.1
.
.
.
Cosham - 2.0.0.0
Thanks in advance..
Instead of stacking for /d you could do a dir /b/s to find all Services.dll and parse the nasty (cr,cr,lf) output of wmic with a for /f:
#Echo off&SetLocal EnableExtensions EnableDelayedExpansion
For /F "tokens=*" %%A in (
'Dir /B/S C:\high\low\Services.dll ^|findstr /i "bin\\Services.dll$"'
) Do (
Set "DLL=%%~fA"
Set "DLL=!DLL:\=\\!"
For /F "tokens=1* delims==" %%B in (
'wmic datafile where name^="!DLL!" get Version /value^|findstr Version'
) Do For /f "delims=" %%D in ("%%C") Do Echo Version: %%D %%~fA
)
The first For /f parses the output of the commamd inside the '' iterating through each line passed in %%A (I prefer upper case variables to better distinguish between lower case~ modifiers.
Since Dir will allow wildcards only in the last element I can't do a Dir /B/S C:\high\low\*\bin\Services.dll
To ashure I get only Services.dll in a bin folder I pipe dir output to findstr /i "bin\\Services.dll$ (findstr uses by default a limited RegEx so the \ has to be escaped with another one, the $ anchors the expression at the end of the line).
The wmic command needs the backslashes in the path also escaped what is possible with string substitution (works only with normal variables)
In a (code block) we need delayed expansion to get actual values for variables changed in the code block, so ! instead of % for framing the variable names
The 2nd For /f parses wmic output splitting at the equal sign, assigning content to var %%C
EDIT added another for /f to remove the wmic cr
I want to list all files (with relative path) in a folder with a specific sub-folder, and write that list to a text file. The folder is a network folder so I can not set it as current directory. All this via batch script.
So like this:
Folder structure:
\\OtherComputer\MyFiles\libs
--File1.dll
--File2.dll
\\OtherComputer\MyFiles\libs\Editor\
----File3.dll
I want to generate a file with the following text:
File1.dll
File2.dll
Editor\File3.dll
This is how far I have come:
SET LibPath=\\OtherComputer\MyFiles\
break >"%LibPath%metadata"
for %%F in (%LibPath%*.*) do (
echo %%F>>"%LibPath%metadata"
)
for %%F in (%LibPath%Editor\*.*) do (
echo Editor\%%F>>"%LibPath%metadata"
)
But this solution write the full path. I tried this to remove the path:
set str=%%F:%LibPath%=%
But it does not seem to handle variable SET or the %%F variable too well inside a for-loop.
Is it even possible? Would like to not have to write a C# executable for this.
Thanks in advance!
Try the following:
#echo off
setlocal enabledelayedexpansion
set "dir=\\OtherComputer\MyFiles\"
FOR /F "tokens=*" %%f IN ('dir /s /b /A-d "%dir%"') do (
set "full=%%f"
echo !full:*%dir%=!
)
setlocal enabledelayedexpansion, in addition to creating a localized environment, turns on delayed expansions of variables, which allows variables to be modified inside the for loop's body and used with their modified values. (see help set). Note that in order for a variable to be expanded dynamically, you must enclose it in !, not %; e.g., !full! instead of %full%.
Note: A side effect of enabling delayed expansion is that any ! characters are interpreted as part of a variable reference, resulting in potentially unwanted removal of ! chars. from strings, such as in echo hi!. To output a literal ! while delayed expansion is enabled, use ^^! in unquoted strings, and ^! in double-quoted strings. Furthermore, literal ^ chars. in double-quoted strings then have to be represented as ^^.
dir /s /b /A-d "%dir%" lists all files - and files only, due to excluding directories with /A-d - in the current subtree (/s), as paths only (b). Note that using /s implies that full paths are output.
set "full=%%f" sets aux. variable full to the absolute path being processed in the current loop iteration.
!full:*%dir%=! then strips the input dir.'s path from the absolute path using prefix string substitution, leaving the relative path desired (again, see help set).
Just to contrast this with a PowerShell (v3+) solution, which demonstrates how much more advanced PowerShell is compared to cmd.exe.:
$dir = '\\OtherComputer\MyFiles\'
(Get-ChildItem -File -Recurse $dir).FullName.replace($dir, '')
I have a way to to this with two bat files:
code1.bat: Looping through all the files in "C:\Mypath"
#echo off
FOR /F "tokens=*" %%G IN ('dir /s /b C:\Mypath\*.*') do (
CALL code2.bat %%G
)
code2.bat: removing the main path string
SET _var=%1
SET _endbit=%_var:*Mypath\=%
Echo %_endbit%
Probably can be done in one file... take a look at http://ss64.com/nt/
The xcopy commmand is capable of returning relative paths. If the /L switch is given, nothing is actually copied but all items are listed that would be copied without /L. The pushd command handles UNC paths correctly (type pushd /? in command prompt for details). So the following code snippet should do what you want:
#echo off
pushd "\\host\share\folder"
> "\path\to\listfile.txt" xcopy /L /Y /C /I /E ".\*.*" "%TMP%"
popd
The above code results in each line to be prefixed with .\. In addition, it returns a summary line like # File(s). The code below gets rid of all those artefacts:
#echo off
pushd "\\host\share\folder"
> "\path\to\listfile.txt" (
for /F "tokens=2 delims=:" %%I in ('
xcopy /L /Y /C /I /E "*.*" "%TMP%"
') do (
echo(%%I
)
)
popd
Here I changed the file pattern from .\*.* to *.* which results in outputs like Z:rel\path\to\item.ext (rather than .\rel\path\to\item.ext). The for /F loop parses the output of xcopy and removes everything up to the first :, so the drive letter Z: is deleted. Since the : is a forbidden character for file and directory names, it cannot appear in any of the paths. Since the summary line # File(s) does not contain any columns, it is not enumerated by for /F.
#echo off
for %%a in (*.xhtml) do (
ren "%%~fa" "epub_%%~nxa"
)
I am using the code for insert a text ("epub_") to all the file names.
The file name is
00_Cover_Page.xhtml
01_Halftitle.xhtml
02_Title.xhtml
03_Copyright.xhtml
04_Dedication.xhtml
05_Preface.xhtml
06_Contents.xhtml
It's renaming good except "00_Cover_Page.xhtml"
epub_epub_00_Cover_Page.xhtml ("epub_" Inserted twice in the filename only)
epub_01_Halftitle.xhtml
epub_02_Title.xhtml
epub_03_Copyright.xhtml
epub_04_Dedication.xhtml
epub_05_Preface.xhtml
epub_06_Contents.xhtml
How could it be happened?
As pointed out in MC ND's comment, an explanation of the behavior is available at https://stackoverflow.com/a/19705611/1012053. The FOR loop buffers only a portion of the directory, and when it goes back to disk to read additional file entries, it can pick up already renamed files.
jeb's answer on that same question explains how to avoid the problem by using a FOR /F loop processing a DIR /B command - the output of the DIR /B command is captured in its entirety before iterations begin.
#echo off
for /f "eol=: delims=" %%F in ('dir /b /a-d *.xhtml') do ren "%%F" "epub_%%~nxa"
An alternative is to use my JREN.BAT regular expression renaming utility. JREN.BAT is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward.
jren "^" "epub_" /fm *.xhtml
Use CALL JREN if you put the command within a batch script.
Somehow the FOR loop picks up the already renamed files. You can avoid this in (at least) 2 ways:
Filter on the orignal filenames if possible:
for %A in (0*.xhtml) do #ren "%~nxA" "epub_%~nxA"
Create a filename list beforehand and use it in the loop:
set tmpfile=files%RANDOM%.tmp
dir /b *.xhtml > %tmpfile%
for /f %A in (%tmpfile%) do #ren "%~nxA" "epub_%~nxA"
del %tmpfile%
set tmpfile=
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.