I want to echo all directories which do not starts let's say with a letter. I write following snippet
#echo off
for /d %%g in (*) do (
echo %%~nxg
)
and it prints out all directories. How should I filter out those starts with a? I'm trying do something with IF-Statement, but fail miserably.
Output filtering
dir /ad /b | findstr /i /v /b "a"
List the directories (/ad) and pipe (|) the output of the command into findstr, that will select the lines that does not contain (/v) at the beginning (/b) the string "a", case insensitive (/i)
Substring
#echo off
setlocal enableextensions enabledelayedexpansion
for /d %%a in (*) do (
set "name=%%~na"
if /i not "!name:~0,1!"=="a" echo %%a
)
for replaceable parameters do not allow the use of substring operations, so a variable is needed. But as the variable is changed inside a block of code (code in parenthesis) and normal variable expansion is done when the block is parsed, not when it is executed, delayed expansion is needed to retrieve (read) the value from the changed variable.
The problem with this code is it will fail when folder's names contain ! as it will be consumed by the parser in the set command. To handle it, delayed expansion should be enabled/disabled when needed
#echo off
setlocal enableextensions disabledelayedexpansion
for /d %%a in (*) do (
set "name=%%~na"
setlocal enabledelayedexpansion
for %%b in ("!name:~0,1!") do (
endlocal
if /i not "%%~b"=="a" echo %%a
)
)
Character removal
#echo off
setlocal enableextensions disabledelayedexpansion
for /d %%a in (*) do (
for /f "tokens=* delims=aA" %%b in ("a%%a") do (
if /i "%%~b"=="%%~a" echo %%a
)
)
Here a for /f is used, configured to see the letters aA as delimiters but to retrieve all the tokens. This will make the for /f remove all the delimiters (aA) from the start of the line. If the remaining data is equal to the initial data, the name of the folder does not start with a or A
for /d %%g in (*) do (
for /f "eol=a tokens=* delims=" %%# in ("%%~nxg") do echo %%~fg
)
To do it with an IF, try this:
#ECHO OFF
SetLocal EnableDelayedExpansion
FOR /D %%g IN (*) DO (
SET tmp=%%~nxg
IF NOT "!tmp:~0,1!"=="a" (
ECHO %%~nxg
)
)
Related
So my mission is to loop through a directory and set a variable to capture the zip file name.
then use that variable to parse what is on the left of the string before the underscore. That way I can name a log file. I searched high and low but I'm not seeing any good examples on Stack. when I use %%i it will return a full directory path. (which is not needed here.) If I use the %%z I get null back how can I pass my %zipfile% variable into my nested loop In()?
setlocal enableextensions enabledelayedexpansion
set dir1="C:\test\"
set 7zip="C:\Program Files\7-Zip\7z.exe"
set output="C:\test\Filelist.txt"
REM enter folder location
cd C:\test
REM loop through zip files
for /r %%i in (*.zip) do ( set zipfile=%%~nxi
for /F " delims=_" %%z in (%zipfile%) do (set log="%%z_file_list.txt")
)
Pause
REM Del %log%
Does the following help you out?
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "dir1=C:\test"
Set "7zip="%ProgramFiles%\7-Zip\7z.exe"
Set "output=C:\test\Filelist.txt"
For /F "Delims=" %%G In (
'Set "PATHEXT=" ^& %SystemRoot%\System32\where.exe /F /R "%dir1%" "?*_*.zip" 2^>NUL'
) Do (
For /F "Delims=_" %%H In (
"%%~nG"
) Do (
Echo Set "log=%%H_file_list.txt"
)
)
Pause
I have two files :
old_names.txt
oldname1
oldname2
new_names.txt
newname1
newname2
I would like to search my folder for file names containing "old_names" and replace "old_name" string with corresponding "new_name".
for /f %%i in (old_names.txt) do (
// get corresponding %%j new_name
for /f %%a in ('dir /b /a-d *%%i*') do ren %%a %%j
)
How can I retrieve the corresponding new_name ?
For your file pair concept you basically need to read the two files line by line simultaneously, if I understand correctly, which can be accomplished like illustrated in this answer. So here is a possible code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=D:\path\to\root_dir" & rem // (directory containing files to rename)
set "_FILE1=D:\path\to\old_names.txt"
set "_FILE2=D:\path\to\new_names.txt"
rem // Count number of lines of 1st file (2nd file is not checked):
for /F %%C in ('^< "%_FILE1%" find /C /V ""') do set "NUM1=%%C"
setlocal EnableDelayedExpansion
rem // Change into root directory:
cd /D "%_ROOT%" || exit /B 1
rem // Here input redirection is used to read both files simultaneously:
9< "!_FILE1!" 8< "!_FILE2!" (
rem // Loop through the number of lines of the 1st file:
for /L %%I in (1,1,%NUM1%) do (
rem // Read a line from the 1st file:
<&9 (set "LINE1=" & set /P "LINE1=")
rem // Read a line from the 2nd file:
<&8 (set "LINE2=" & set /P "LINE2=")
rem /* Assign line strings to `for` variables to later avoid nested
rem delayedly expanded environment variables: */
for /F "tokens=1,2 delims=| eol=|" %%I in ("!LINE1!|!LINE2!") do (
rem // Get list of files matching the partial name:
for /F "delims= eol=|" %%F in ('dir /B /A:-D "*!LINE1!*"') do (
endlocal
rem // Store current file name:
set "NAME=%%F"
setlocal EnableDelayedExpansion
rem // Do the actual sub-string replacement and rename file:
ECHO ren "!NAME!" "!NAME:%%I=%%J!"
)
)
)
)
endlocal
endlocal
exit /B
After having checked the correct output, remove the upper-case ECHO command!
This does not work if any of the following characters occur in the two name files: =, !, ^; the ~ must not occur as the first character in any line of the first file (old names).
As per my comment. Create a single file called names.txt and add the strings you want to replace and what you want to replace it with:
dummy replacement
dummy2 replacement2
then the script needs to be in the same directory, or you have to specify path to the files:
#echo off
setlocal enabledelayedexpansion
for /f "tokens=1,2" %%i in (names.txt) do (
for /f %%a in ('dir /b /a-d ^| findstr "%%i"') do (
set "oldname=%%a"
set "newname=!oldname:%%i=%%j!"
echo ren "!oldname!" "!newname!"
)
)
or by specifying path:
#echo off
setlocal enabledelayedexpansion
for /f "tokens=1,2" %%i in (names.txt) do (
for /f %%a in ('dir /b /a-d "D:\PATH" ^| findstr "%%i"') do (
set "oldname=%%a"
set "newname=!oldname:%%i=%%j!"
echo ren "!oldname!" "!newname!"
)
)
Once you are happy that it prints the files to replace to screen, simply remove the echo from the last line of code to actually perform the ren
#Echo off&SetLocal EnableExtensions EnableDelayedExpansion
cd "C:\Documents and Settings\John\Desktop\New\Interest\f2"
Pushd "C:\Documents and Settings\John\Desktop\New\Interest\f2"
Set Line#=26
Set /A LOfs=24 -1, Len=34 - LOfs
For %%A in (*.txt) do For /F "tokens=1* delims=:" %%B in (
'Findstr /N ".*" "%%A" ^|Findstr "^%Line#%:"'
) do if %errorlevel% == 0 Set "Line=%%C"&Ren "%%~fA" "!Line:~%LOfs%,%Len%! - %%A!""
Popd
In the above I am trying to change the filename of files in a directory with text in it at a certain position.
If line 26 is blank do nothing and do not change filename.
I have gone wrong somewhere and am going round in circles.
Can anyone help?
Thank you.
You don't state how your script fails, but I can see some potential problems. I also see possible simplifications.
You certainly don't need both CD and PUSHD
I got rid of the numeric variables and included the number literals in the actual code. You can revert back to variables if you want.
You don't need the outer FOR loop. FINDSTR can search multiple files when using wildcards in the file name, and then it will include the filename, followed by : in the output. So if you add the /N option, output will have the form filename:line#:text. You can then adjust the 2nd FINDSTR to return only the correct line numbers.
It is not enough to ignore blank lines. Your rename only works if there is at least one valid file name character after the 23rd character. Filenames cannot include :, *, ?, /, \, <, >, or |. (I may have missed some). I adjusted the FOR /F delims and the FINDSTR search to compensate.
FOR variable expansion like %%A will corrupt values if they contain ! and delayed expansion is enabled. ! is a valid character in file names. So the delayed expansion must be toggled on and off within the loop.
I believe the following will do what you want. The code below will simply echo the rename commands. Remove the ECHO before the ren once it gives the correct results.
#echo off
setlocal disableDelayedExpansion
pushd "C:\Documents and Settings\John\Desktop\New\Interest\f2"
for /f "tokens=1,3 delims=:*?\/<>|" %%A in (
'findstr /n "^" "*.txt" ^| findstr "^[^:]*:26:.......................[^:*?\\/<>|]"'
) do (
set "old=%%A"
set "line=%%B"
setlocal enableDelayedExpansion
ECHO ren "!old!" "!line:~23,11! - !old!"
endlocal
)
popd
An slightly different method to Daves:
#Echo Off
Set "SrcDir=%UserProfile%\Desktop\New\Interest\f2"
Set "Mask=*.txt"
Set "Line#=26"
Set "LOfs=23"
Set "Len=11"
If /I Not "%CD%"=="%SrcDir%" Pushd "%SrcDir%"2>Nul&&(Set _=T)||Exit/B
For /F "Tokens=1-2* Delims=:" %%A In ('FindStr/N "^" "%Mask%" 2^>Nul'
) Do If "%%B"=="%Line#%" If Not "%%~C"=="" (Set "Line=%%C"
SetLocal EnableDelayedExpansion
If Not "!Line:~%LOfs%,%Len%!"=="" (
If Not Exist "!Line:~%LOfs%,%Len%! - %%A" (
Ren "%%A" "!Line:~%LOfs%,%Len%! - %%A"))
EndLocal)
If "_"=="T" PopD
This method don't require findstr.exe nor toggle setlocal/endlocal, so it should run faster. Also, it avoids to re-process any already renamed file changing the plain for %%A by a for /F combined with dir command.
#Echo off
SetLocal EnableDelayedExpansion
cd "C:\Documents and Settings\John\Desktop\New\Interest\f2"
Set /A Line#=26, LOfs=24 -1, Len=34 - LOfs
For /F "delims=" %%A in ('dir /A-D /B *.txt') do (
rem Read the desired line from this file
(for /L %%i in (1,1,%Line#%) do set "Line=" & set /P "Line=") < "%%A"
if defined Line ECHO Ren "%%~fA" "!Line:~%LOfs%,%Len%! - %%A"
)
Note also that when this Batch file ends the current directory is automatically recovered to the current one when setlocal command was executed, so pushd/popd commands are not needed either.
I have some files in the form:
filename1 1 extra1.ext
filename1 2.ext
filename1 3 extra2.ext
...
filename2 1.ext
filename2 100 extra3.ext
...
filename20 1.ext
filename20 15 extra100.ext
(etc.)
...where filename1, filename2, etc., can contain spaces, symbol ' but not numbers. And extra1, extra2, etc, can contain anything. The number in the file name enclosed by spaces does not repeat per same filename1, filename2, etc.
What i want is to remove the extra things of the files that contain it. That is, to get from filename20 15 extra100.ext to filename20 15.ext
My first attempt is this:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "FILE=file name 11 con sosas extras 2.txt"
set "ext=txt"
set "folder=."
for /F "tokens=1,* delims=0123456789" %%A in ("!FILE!") do (set "EXTRA=%%B")
set "FIRST=!FILE:%EXTRA%=!"
set "filename=!FIRST!.!ext!"
echo !EXTRA!
echo !filename!
echo rename "!folder!\!FILE!" "!filename!"
that seems to work, but if i change it to receive parameters, it doesn't:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "FILE=%1"
set "ext=%2"
set "folder=%3"
for /F "tokens=1,* delims=0123456789" %%A in ("!FILE!") do (set "EXTRA=%%B")
set "FIRST=!FILE:%EXTRA%=!"
set "filename=!FIRST!.!ext!"
echo !EXTRA!
echo !filename!
echo rename "!folder!\!FILE!" "!filename!"
where %1 is the filename, %2 is the extension and %3 is the folder in which the files are. Probably, the extension can be extracted inside the batch, but i don't know how to do it.
On another hand, i plan to use this batch into another one. There, there will be a for loop in (*.txt) and i don't know how to differentiate between files that have extra things (and then call this batch) from files that doesn't (and then not call this batch).
Regards,
use your method to extract the "extra-portion". In a second step, remove that extra-portion:
#echo off
setlocal enabledelayedexpansion
set "FILE=file name 11 con sosas extras 2.txt"
for /f "tokens=1,* delims=1234567890" %%a in ("%file%") do set new=!file:%%b=!%%~xb
echo %new%
%%~xb gives you the extension.
Here is a batch script that seeks the first purely numeric string portion enclosed within SPACEs, or in case it appears at the end, preceded by a SPACE, that occurs after some other text not consisting of SPACEs only. The part in front of the found number followed by a SPACE followed by the number itself are used for building the new file name.
This approach handles all valid characters for file names properly, even ^, &, %, !, ( and ).
So here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=.\test"
for /F "eol=| delims=" %%F in ('
dir /B "%_SOURCE%\*.ext" ^| findstr /R /I ^
/C:"^..* [0123456789][0123456789]*\.ext$" ^
/C:"^..* [0123456789][0123456789]* .*\.ext$"
') do (
set "FILE=%%F"
call :SPLIT FIRST NUM REST "%%~nF"
if defined NUM (
setlocal EnableDelayedExpansion
ECHO rename "!_SOURCE!\!FILE!" "!FIRST! !NUM!%%~xF"
endlocal
)
)
endlocal
exit /B
:SPLIT rtn_first rtn_num rtn_rest val_string
setlocal DisableDelayedExpansion
set "RMD=" & set "NUM=" & set "STR=%~4"
:LOOP
for /F "tokens=1,2,* delims= " %%I in ("%STR%") do (
if not "%%J"=="" (
(for /F "delims=0123456789" %%L in ("%%J") do rem/) && (
if not "%%K"=="" (
set "STR=%%J %%K"
goto :LOOP
)
) || (
set "NUM=%%J"
if not "%%K"=="" (
set "RMD=%%K"
)
)
)
)
set "STR=%~4"
if not defined NUM goto :QUIT
set "STR=%STR% "
call set "STR=%%STR: %NUM% =|%%"
for /F "delims=|" %%L in ("%STR:^^=^%") do set "STR=%%L"
:QUIT
(
endlocal
set "%~1=%STR%"
set "%~2=%NUM%"
set "%~3=%RMD%"
)
exit /B
After having tested the script, remove the upper-case ECHO command to actually rename any files.
This is my first posting so if the format is not as it supposed to be please excuse me for this. (Suggestions for
improvement are welcome.)
I am trying to create a batchfile that will read last lines from logfiles and copy them to a new file.
Until now I have found here a way to read the last line.
Code would be something like:
for /f %%i in ('find /v /c "" ^< someFile.txt') do set /a lines=%%i
set /a startLine=%lines% - 1
more /e +%startLine% someFile.txt > lastLines.txt
The above code works for one file at a time. What I need is to read the last line from all files in a known list and add this line to a new .csv file.
I have been using the following code for getting the 4th entry in the logfiles but it returns every line of every logfile:
for /f %%x in (%list%) do for /f "delims=.txt, tokens=4" %%i in (%%x.txt) do echo %%x, %%i >> output.csv
What I would need is a sort of combination of both but I don't know how to combine them and make the complete last line be copied to the .csv file.
===
#Magoo:
Thanx for your reaction.
In every logfile can be 1 to >100 lines with comma separated information. Something like:
"LOGON,6-1-2015,12:43:39,USERNAME,HOSTNAME,,,,192.168.209.242,00:21:5A:2E:64:5E"
The last code with the 4th entry was used to get a list of all accounts that had logged in to the computers. This code gave me a very large list of all logon/logoff events on all computerlogs I checked in %list%.
In %list$ I had all the names of logfiles I wanted to be checked. This returned all lines.
For a new batchfile I need only the last logon/logoff entry and I want the whole last line.
So I have a .txt file with the hostnames of all computers I need to examine.
This .txt file will be read line by line via the variable %list%.
From every logfile I need only the last line copied to an output file.
===
I just tried the solution offered by JosefZ. Unfortunately this does not work for me yet. No lastlines are copied to the resultfile. In the code I removed the extra entry for possible lastlines for there are no empty lines in the logs, I also added an entry for the hostname I want to be available in the result. JosefZ had the filename there:
#ECHO OFF >NUL
#SETLOCAL enableextensions disabledelayedexpansion
type nul>output.csv
set "list=_listing.txt"
for /F "tokens=*" %%x in ('type "%list%"') do (
set "host=%%~x"
for /F "tokens=*" %%G in ('type "%%~x"') do set "lastline=%%G"
call :lline
)
:endlocal
#ENDLOCAL
goto :eof
:lline
set "filename=.\logs\%filename:&=^&%.txt"
echo %host%,%lastline%>>output.csv
goto :eof
The resultfile shows only the hostnames. I'll puzzle some more with this but all tips are welcome!
===
Got it!!!
#ECHO OFF >NUL
#SETLOCAL enableextensions disabledelayedexpansion
type nul>output.csv
set "list=_listing.txt"
for /F "tokens=*" %%x in ('type "%list%"') do (
set filename= :: *empty previous filename*
set lastline= :: *empty previous lastline*
set "host=%%~x"
set "filename=.\logs\%host%.txt" :: *creating the filename from path+hostname+extention*
for /F "tokens=*" %%G in ('type "%filename%"') do set "lastline=%%G"
call :lline
)
:endlocal
#ENDLOCAL
goto :eof
:lline
echo %host%,%lastline%>>output.csv
goto :eof
Your approach with line numbering could fail if a file has more trailing empty lines. Fortunately for /F loop ignores (does not iterate) empty lines; let's put to use this feature: in the script used next practices:
disabledelayedexpansion to allow ! in file names
set "list=_listing.txt" where the _listing.txt contains list of file names (full path and extension .txt including), one file name on one line: got by dir /b /s *.txt>_listing.txt
type nul>files\output.csv to empty the output file (optional)
set "lastline=!!!file empty!!!" to initialize variable %lastline%; could be set "lastline=" as well
call :lline to process variables %filename% and %lastline%
set "filename=%filename:&=^&%" to allow & in file names
The script is as follows:
#ECHO OFF >NUL
#SETLOCAL enableextensions disabledelayedexpansion
type nul>files\output.csv
set "list=_listing.txt"
for /F "tokens=*" %%x in ('type "%list%"') do (
set "filename=%%~x"
set "lastline=!!!file empty!!!"
rem the whole line
for /F "tokens=*" %%G in ('type "%%~x"') do set "lastline=%%G"
rem the fourth token only
rem for /F "tokens=4" %%G in ('type "%%~x"') do set "lastline=%%G"
call :lline
)
:endlocal
#ENDLOCAL
goto :eof
:lline
set "filename=%filename:&=^&%"
echo %filename% %lastline%
rem >>files\output.csv
goto :eof
Sample _listing.txt file:
d:\bat\files\1exclam!ation.txt
d:\bat\files\2exc!lam!ation.txt
d:\bat\files\11per%cent.txt
d:\bat\files\12per%cent%.txt
d:\bat\files\17per%Gcent.txt
d:\bat\files\18per%%Gcent.txt
d:\bat\files\21ampers&nd.txt
d:\bat\files\22ampers&&nd.txt
Output:
d:\bat>lastlines
d:\bat\files\1exclam!ation.txt 0 15.01.2015 1:52:28.48 -15072 20465
d:\bat\files\2exc!lam!ation.txt 6 15.01.2015 1:52:28.50 3250 16741
d:\bat\files\11per%cent.txt -8 15.01.2015 1:52:28.50 -3692 27910
d:\bat\files\12per%cent%.txt !!!file empty!!!
d:\bat\files\17per%Gcent.txt 0 15.01.2015 1:52:28.56 14508 12374
d:\bat\files\18per%%Gcent.txt 1 15.01.2015 1:52:28.56 30540 26959
d:\bat\files\21ampers&nd.txt 15.01.2015 1:22:50.18
d:\bat\files\22ampers&&nd.txt 15.01.2015 1:22:50.18
Honestly, all that ballast is for (possibly) trailing empty lines in files and for (possibly) ! and & in file names only; all could be done with
for /f %%x in (%list%) do for /f "skip=%startLine% tokens=4" %%i in (%%x) do echo %%x, %%i >> output.csv
You should use a simple FOR to iterate a list of values, not FOR /F.
Something like the following should work:
#echo off
setlocal enableDelayedExpansion
>>output.csv (
for %%F in (
"file1.log"
"file2.log"
"file3.log"
etc.
) do (
for /f %%A in ('find /v /c "" <%%F') do set /a skip=%%A-1
more +!skip! %%F
)
)
The quotes around the file names are there in case you get a name with spaces.
You could use your LIST variable if it looks something like
set LIST="file1.log" "file2.log" "file3.log" etc.
#echo off
setlocal enableDelayedExpansion
set LIST="file1.log" "file2.log" "file3.log" etc.
>>output.csv (
for %%F in (%LIST%) do (
for /f %%A in ('find /v /c "" <%%F') do set /a skip=%%A-1
more +!skip! %%F
)
)
If any of your file names contain the ! character, then you must toggle delayed expansion ON and OFF within your loop. Otherwise the delayed expansion will corrupt the names when %%F is expanded.
#echo off
setlocal disableDelayedExpansion
set LIST="file1.log" "file2.log" "file3.log" etc.
>>output.csv (
for %%F in (%LIST%) do (
for /f %%A in ('find /v /c "" <%%F') do set /a skip=%%A-1
setlocal enableDelayedExpansion
more +!skip! %%F
endlocal
)
)