I am attempting to create a batch for loop (Windows XP and newer command prompts) that iterates through a string containing one or more asterisks. How can I do this? Here is an example:
#FOR %%A IN (A* B) DO #ECHO %%A
The expected output (what I am trying to get) is the following:
A*
B
However, what I am actually getting with the command above is B and only B. For some reason, everything with an asterisk is being ignored by the loop. I have attempted escaping the asterisk with 1-4 carets (^), backslashes (\), percent signs (%), and other asterisks (*), all to no avail. Thanks in advance for illuminating me.
IN CASE YOU WANT MORE INFORMATION:
The purpose of this is to parse a path out of a list of space-separated partial paths. For example, I want to copy C:\Bar\A.txt, C:\Bar\B.txt, and C:\Bar\C*.txt to C:\Foo\ using the following approach:
#SET FILE_LIST=A B C*
#FOR %%A IN (%FILE_LIST%) DO #COPY C:\Bar\%%A.txt C:\Foo\
If there is another alternative way to do this (preferably without typing each and every copy command since there are ~200 files, which is the same reason I don't want to store the full path for every file), I would appreciate the help. Thanks again,
-Jeff
the asterisks works the way its intended, in your case,
#FOR %%A IN (A* B) DO #ECHO %%A
expands A* to all the files that begin with A.
A possible way to do what you want, is just to use this expansion
#ECHO off
PUSHD C:\bar
SET FILE_LIST=A.txt B.txt C*.txt
FOR %%A IN (%FILE_LIST%) DO (
IF EXIST %%A COPY %%A C:\Foo\
)
POPD
This may help:
#echo off
set "it=a*b .txt-b*.txt-c*.txt-d.txt"
set /a i=0,fn=3
:startLoop
set /a i=i+1
for /f "tokens=%i%delims=-" %%m in ("%it%") do echo %%m
if %i% lss %fn% goto:startLoop
Related
I have a batch file that (among other things) turns a list like this:
'foo_ph1-1.tif', 'foo_ph2-1', 'foo_ph2-2'
into a list like this, in a local variable called INVNOS:
'fooph1', 'fooph2', 'fooph2'
I want to remove the duplicates from the second list. I've been trying to do this when I create the list, from the answers to this question, to no avail.
Here's how I make the list.
#echo off
setlocal ENABLEDELAYEDEXPANSION
for %%f in ("*.tif") do #echo %%~nf>>list.lst
set FNAMES=
set INVNOS=
for /f %%i in ('type list.lst') do (
set FNAMES=!FNAMES!'%%i.jpg',
for /f "tokens=1 delims=-" %%a in ("%%i") do (
set BEFORE_HYPHEN=%%a
set INVNOS=!INVNOS!'!BEFORE_HYPHEN:_=!',
)
)
set "FNAMES=%FNAMES:~0,-2%"
set "INVNOS=%INVNOS:~0,-2%"
echo %INVNOS%
endlocal
Solutions with findstr won't work because I need to initialize INVNOS with an empty string, and I get stuck with the difference between % and '!', and slicing, inside the for loop.
I know this is easy in Python, however I'd like to do it with what's native (Windows 10/Windows Server), so CMD or Powershell.
Any suggestions?
Just to sketch the bigger picture, INVNOS (inventory numbers) is derived from directories full of tif's, so we can check whether or not they exist in some sql database.
I would approach the problem differently:
#echo off
setlocal ENABLEDELAYEDEXPANSION
for %%f in (*.tif) do (
for /f "delims=-" %%g in ("%%~nf") do set "~%%g=."
)
for /f "delims=~=" %%a in ('set ~') do set "INVOS='%%a', !INVOS!"
set "INVOS=%INVOS:~0,-2%
echo %INVOS:_=%
The trick is to define variables for each filename (the variableNAMES contain the filenames. A variable can only exist once, so per definition, there are no duplicates)
With another for loop extract the names from the defined variables and join them. The underscores can be deleted in one go instead of removing them from each substring.
When needed, you can delete the variables with for /f "delims==" %%a in ('set ~') do set "%%a=", but they are destroyed anyway when the script ends. (same line when you want to be sure, no variable starting with ~ is defined by accident before you set them)
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
:: The values assigned to these variables suit my system and test environment
SET "sourcedir=u:\your files"
SET "tempfile=%temp%\tempfile.txt"
:: remove variables starting :
FOR /F "delims==" %%a In ('set : 2^>Nul') DO SET "%%a="
(for %%f in ("%sourcedir%\*.tif") do echo %%~nf)>"%tempfile%"
set "FNAMES="
set "INVNOS="
for /f "usebackqdelims=" %%i in ("%tempfile%") do (
set FNAMES=!FNAMES!'%%i.jpg',
for /f "tokens=1 delims=-" %%a in ("%%i") do (
set "BEFORE_HYPHEN=%%a"
SET "before_hyphen=!BEFORE_HYPHEN:_=!"
IF NOT DEFINED :!BEFORE_HYPHEN! set "INVNOS=!INVNOS!'!BEFORE_HYPHEN:_=!', "&SET ":!BEFORE_HYPHEN!=Y"
)
)
set "FNAMES=%FNAMES:~0,-2%"
set "INVNOS=%INVNOS:~0,-2%"
echo %INVNOS%
IF DEFINED tempfile DEL "%tempfile%"
GOTO :EOF
You would need to change the value assigned to sourcedir to suit your circumstances. The listing uses a setting that suits my system.
I deliberately include spaces in names to ensure that the spaces are processed correctly.
%tempfile% is used temporarily and is a filename of your choosing.
The usebackq option is only required because I chose to add quotes around the source filename.
it is standard practice on SO to use the syntax set "var=value" for string
assignments as this ensures stray trailing spaces on the line are ignored.
Evil trailing space on OP's code set INVNOS... within the for ... %%a loop.
Given OP's original filename list, foo_ph1-1.tif foo_ph2_1 foo_ph2-2, the processing should produce fooph1 fooph21 fooph2, not fooph1 fooph2 fooph2 as claimed.
My testing included foo_ph2-2.tif
The code is essentially the same, but first clearing any environment variables that start :, on the Irish principle.
The temporary file nominated is recreated avoiding the (unfulfilled) requirement to first delete it.
BEFORE_HYPHEN is explicitly expunged of underscores before the if not defined test is applied. I selected : because : can't be part of a filename. Once the name is applied to the invnos list, the :!BEFORE_HYPHEN! variable is established to prevent further accumulation of repeat BEFORE_HYPHEN values into invnos.
If you wanted to step up to PowerShell, something like this could be done in a .bat file script. Of course, It would be easier to write and maintain if it were all written in PowerShell.
=== doit.bat
#ECHO OFF
FOR /F "delims=" %%A IN ('powershell -NoLogo -NoProfile -Command ^
"(Get-ChildItem -File -Filter '*.tif' |" ^
"ForEach-Object { '''' + $($_.Name.Split('-')[0].Replace('_','')) + '''' } |" ^
"Sort-Object -Unique) -join ','"') DO (
SET "INVNOS=%%~A"
)
ECHO INVNOS is set to %INVNOS%
EXIT /B
Get-ChildItem produces a list of all the *.tif files in the directory. Split() does what "delims=-" does in a FOR loop. The [0] subscript chooses everything up to the first '-' character in the file name. Replace will remove the '_' characters. Sort-Object removed duplicates to produce a unique list. The -join converts the list of names to a single, comma delimited string. The resulting string is stored into the INVNOS variable.
Do you really want APOSTROPHE characters around each name in the list?
I want to get last word from a sentence or file path
I tried solutions from other articles and forums but can't seem to figure it out. here is what I have so far
#echo off
set a="C:\Some\Random\File\Path.txt"
echo %a%
set b=%a:\= %
echo %b%
for /f "tokens=*" %%a in (%b%) do #echo %%a %%b %%c
pause
All I need is the last word of what ever file path I put in %a%
Just to answer specifically on your question:
#echo off
set "a=C:\Some\Random\File\Path.txt"
for %%i in (%a%) do echo %%~nxi
pause
Also see how I used the double quotes on the setting of the variable, This allows us to get rid of any wanted whitespace after the value, and also if we add the quotes after the equal, they become part of the value, which is not what we really want.
To understand the variable references, for from cmdline for /? I suggest you also see set /?
Additionally, based on your comment, if you want to set it as a viarble, simply do it :)
#echo off
set "a=C:\Some\Random\File\Path.txt"
for %%i in (%a%) do set variablename=%%~nxi
echo %variablename%
pause
While Gerhards solution IS preferable (for path), your approach wasn't that bad.
You just have to use a simple for,
so it iterates over the space seperated elements which therefor have to be unquoted.
Repeatedly setting the same variable will have the last value/word persisting.
:: Q:\Test\2019\01\18\SO_54246604.cmd
#echo off
set "a=C:\Some\Random\File\Path.txt"
echo %a%
for %%a in (%a:\= %) do Set "b=%%a"
echo %b%
pause
> SO_54246604.cmd
C:\Some\Random\File\Path.txt
Path.txt
I am trying to rename a lot of files. I only want to change the extention from ".pdf.OCR.pdf" to ".pdf"
So far I got the following code
rem for /r myPDFfolder %%i in (*.pdf.OCR.pdf) do ren "%%i" "%%~ni.pdf"
But it does not appear to work with the extension that has multiple dots -- what am I doing wrong?
Extension is the part of file name after the last dot.
Use string replacement to strip the unneeded part:
setlocal enableDelayedExpansion
for /f "eol=* delims=" %%i in ('dir /s /b "r:\*.pdf.OCR.pdf"') do (
set "name=%%~nxi"
ren "%%i" "!name:.pdf.OCR=!"
)
P.S. Parsing of dir is used to make the code more robust in case a different text is stripped which might have changed the sorting order and cause for to process the file twice or more times.
There is no need for a batch file. A moderate length one liner from the command prompt can do the trick.
If you know for a fact that all files that match *.pdf.ocr.pdf have this exact case: .pdf.OCR.pdf, then you can use the following from the command line:
for /r "myPDFfolder" %F in (.) do #ren "%F\*.pdf.ocr.pdf" *O&ren "%F\*.pdf.o" *f
The first rename removes the trailing .pdf, and the second removes the .OCR. The above works because *O in the target mask preserves everything in the original file name through the last occurrence of upper-case O, and *f preserves through the last occurrence of lower-case f. Note that the characters in the source mask are not case sensitive. You can read more about how this works at How does the Windows RENAME command interpret wildcards?
If the case of .pdf.ocr.pdf can vary, then the above will fail miserably. But there is still a one liner that works from the command line:
for /r "myPDFfolder" %F in (*.pdf.ocr.pdf) do #for %G in ("%~nF") do #ren "%F" "%~nG"
%~nF lops off the last .pdf, and %~nG lops off the .OCR, which leaves the desired extension of .pdf.
You should not have to worry about a file being renamed twice because the result after the rename will not match *.pdf.ocr.pdf unless the original file looked like *.pdf.ocr.pdf.ocr.pdf.
If you think you might want to frequently rename files with complex patterns in the future, then you should look into JREN.BAT - a regular expression renaming utility. It is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward. Full documentation is embedded within the script.
Assuming JREPL.BAT is in a folder that is listed within your PATH, then the following simple command will work from the command line, only renaming files that match the case in the search string:
jren "(\.pdf)\.OCR\.pdf$" $1 /s /p "myPDFfolder"
If you want to ignore case when matching, but want to force the extension to be lower case, then:
jren "\.pdf\.ocr\.pdf$" ".pdf" /i /s /p "myPDFfolder"
Alternative solution, without delayed expansion (remove ECHO to actually rename any files):
#echo off
rem iterate over all matching files:
for /F "delims=" %%A in (
'dir /S /B /A:-D "myPDFfolder\*.pdf.OCR.pdf"'
) do (
rem "%%~nA" removes last ".pdf"
for /F %%B in ("%%~nA") do (
rem "%%~nB" removes ".OCR" part
for /F %%C in ("%%~nB") do (
rem "%%~nC" removes remaining ".pdf"
ECHO ren "%%~fA" "%%~nC.pdf"
) & rem next %%C
) & rem next %%B
) & rem next %%A
NOTE: The directory tree is enumerated before for iterates through it because otherwise, some items might be skipped or tried to be renamed twice (see this post concerning that issue).
I wonder what's wrong with my coding, because it's not working
I want to rename all the png files inside Chris.
but it failed
for /f in ('C:/Users/Chris/Downloads/images/*.png')
do ren "C:\Users\Chris\Downloads\images\*.png" "%date:~10,4%-%date:~4,2%-%date:~7,2%_%HR%%time:~3,2%-img.png"
No need for /f in argument, no need for quotes but your missing a variable declaration
The variable should be used in the do-part otherwise the for is not realy helpful
the for will enumerate the full path so you need to strip the filename using ~n
the do-part must be directly behind the for-statement or it needs to be inside round brackets
here's the complete code:
for %%i in (C:/Users/Chris/Downloads/images/*.png) do (
ren "%%i" "%date:~10,4%-%date:~4,2%-%date:~7,2%_%HR%%time:~3,2%-%%~niimg.png"
)
If order to use a for loop, you need to specify a variable to use (even if you don't use a variable in the loop at all), otherwise you'll get a syntax error. While variables can only be one letter, this is pretty much the only time in batch that variables are case-sensitive, so you've got 52 letters, plus a few additional characters that I've seen used, like #. Additionally, do must always be on the same line as the ).
A for /F loop can process strings, text files, and other batch commands.
To process strings, use double quotes: for /F %%A in ("hello world") do echo %%A
To process batch commands, use single quotes: for /F %%A in ('dir /b') do echo %%A
To process text files, do not use any quotes at all: for /F %%A in (C:\Users\Chris\image_list.txt) do echo %%A
You may also want to go into the directory that you're processing just to make things easier.
pushd C:\Users\Chris\Downloads\images
for /F %%A in ('dir /b *.png') do (
REM I'm not sure what the %HR% variable is supposed to be, so I'm ignoring it.
ren "%%A" "%date:~10,4%-%date:~4,2%-%date:~7,2%_%HR%%time:~3,2%-img.png"
)
I am running windows 7 64 bit
I have a directory, full of sub directories, each subdirectory has many files.
I want to write a batchfile which will iterate through the subdirectories in the current directory, copy the first file in each subdirectory to the parent directory, renaming the file .txt
so far I have this:
`for /D %%s in (./*) do
(
cd %%s
i=1
for /f %%a in (./*) do
(
if i==1
cp %%a >> ./../%%s.txt
i=i+1
else
)
cd ..
)`
All I am getting is that the syntax of the command is incorrect.
I have tried using echos to see how far I get and I am not getting past the first line.
Batch syntax is very specific.
for... do (
whatever
if ... (
whatever
) else (
whatever
The open-parentheses must be in the position indicated. whatever may be on the following line or the same line as the (
If you have a sequence of statements (for an if-true condition, for example) then these must be parenthesised
The close-parenthesis before an else must be on the samp physical line as the else.
cp is invalid - unless you're using cygwin, the command is copy
i=i+1 is invalid - use set /a i=i+1 (/a for arithmetic expressions, else it's assumed to be s string expression)
%var% for the value of a variable. i will never be equal to 1 except within a block statement (parenthesised series of statements) then you need to invoke delayedexpansion (an option of setlocal) and use !var! as %var% is resolved to be the value of var at the time the expression is parsed, not executed.
/ is a switch indicator, not a directory separator. \ is a directory-separator. Windows can often make the substitution, but not when there is a syntax ambuity. Best to use the correct character.
"quote" arguments that may contain separators (like spaces.) It's not strictly necessary in a cd statement, but it's harmless there and is required almost everywhere else.
setlocal enabledelayedexpansion
for /D %%s in (.\*) do (
cd "%%s"
set /a i=1
for /f %%a in (.\*) do (
if !i!==1 (
cOpY %%a >> ".\..\%%s.txt"
set /a i=i+1
) else ( )
cd ..
)
Although... I'd be tempted to use PUSHD "%%s" in place of the first cd and popd for the second.
Not sure what your copy is aimed at achieving. To append to an existing file, I'd use type. There is a copy syntax that implements append this file to that but I use it so rarely I'd have to look it up.
commandname /?
from the prompt will provide (sometimes obscure) documentation.
I think this way is simpler and easier to understand:
#echo off
setlocal EnableDelayedExpansion
for /D %%s in (*) do (
set "firstFile="
for %%a in ("%%s\*.*") do if not defined firstFile set "firstFile=%%a"
copy "!firstFile!" "%%s.txt"
)
An observation on previous code:
The .\anyName although correct, is usually never used. A dot indicate "current folder" and .\anyName indicate the anyName below current folder, that is exactly the same than just anyName.
EDIT: New method to copy the Nth file
#echo off
setlocal EnableDelayedExpansion
for /D %%s in (*) do (
set i=0
for %%a in ("%%s\*.*") do (
set /A i+=1
if !i! equ N copy "%%a" "%%s.txt"
)
)
You must write a number in place of N above.