I have a list of files named:
file.txt
file (1).txt
file (2).txt
etc.
Where the greater (number) is the last file updated.
I want a .bat script that allows get the content of file (maxnumer).txt to file.txt.
dir /B /OD /TW file*.txt lists file names in sort order of last written time (cf. dir /?).
Next commented batch script could do the job for you:
#echo OFF
SETLOCAL EnableExtensions
rem delete empty `_lastfile` variable
set "_lastfile="
rem store file name of last written time into `_lastfile` variable
for /F "delims=" %%G in ('dir /B /OD /TW file*.txt 2^>NUL') do set "_lastfile=%%~G"
rem ↑↑↑↑↑↑ suppress errors
rem check the `_lastfile` variable
rem is defined?
rem AND is different from "file.txt"?
if defined _lastfile if /I "%_lastfile%" NEQ "file.txt" copy /Y "%_lastfile%" "file.txt"
Resources (required reading):
(command reference) An A-Z Index of the Windows CMD command line
(additional particularities) Windows CMD Shell Command Line Syntax
(%~G etc. special page) Command Line arguments (Parameters)
(2>NUL etc. special page) Redirection
Here is a bit of a hacky answer.
This script will move all files from file (1).txt up to file (10).txt to file.txt , leaving only file.txt which now contains the text that was in file (10).txt
Doing it in ascending order will ensure the highest number that exists is the last one to be moved.
#echo off
set /P name=Enter name of file without extension:
echo enter extension:
set /P ext=.
echo. & echo.
echo these actions will be performed: & echo.
FOR /L %%A IN (1,1,10) DO #echo move /y "%name% (%%A).%ext%" "%name%.%ext%"
echo. & pause & echo.
FOR /L %%A IN (1,1,10) DO move /y "%name% (%%A).%ext%" "%name%.%ext%"
pause
You could use IF EXIST %name% (%%A).%ext% To stop the script from trying to move files that don't exist, but it doesn't really affect anything to do that, so I didn't bother.
The script above will do it for a specific named file. To do it for all files in a directory will be possible, here are some hints to get you going
use a dir /b >filenames.txt to get all files in a directory listed in a text file
to perform an action for every line in a textfile do
for /f "usebackq delims= tokens=* %%a in (`filenames.txt`)" DO (
::some stuff here
)
The way I would go about it would be to get filenames.txt , manually delete all the (n) files so you just have a list of the "non-duplicate" filenames, and use that as your input. You
There are probably more elegant solutions out there, but with all the peculiarities of batch I wouldn't be surprised to find they are pages and pages long.
If you want to keep all the original files not just end up with the final file.txt with no copies, then I you want to use COPY
If you want to keep all the original files, then you would want to use COPY not MOVE.
In that case, to remove all superfluous operations (i.e. only copy the highest numbered file, not copy all the files in order) then something like IF NOT EXIST %name% (!B!).%ext% where !B!=%%A+1 within your (now multiline) for loop and use Setlocal EnableDelayedExpansion on to make the arithmetic work properly. But it's not really necessary, copying 1, then 2, then 3, then 4 does the same thing if a little slower than skipping 1 2 and 3 and just doing 4.
I hope this helps enough to point you in the right direction, feel free to ask questions if this isn't clear.
Related
I create a lot of hardlinks every week. When time comes to clean them, I find myself using the "DeleteAllHardlinks.bat" for ln (https://schinagl.priv.at/nt/ln/ln.html) but I have to drag and drop everyfile one after the other.
I would love to find a way to just select 100 files and drop them on the .bat, wait a while and find all those files and hardlinks deleted for good. Is there anyway to change the .bat file to allow this? (or maybe any other different method to acomplish the same?)
#echo off
REM
REM Check for commandline args
REM
if "[%~1]" == "[]" goto error
set LN=ln.exe
REM
REM List hardlink sibblings and delete all siblings
REM
for /f "delims=" %%a in ('#%LN% --list "%~1"') do (
del /f "%%a"
)
goto ausmausraus
:error
echo DeleteAllHardlinks: Argument is missing. Usage DeleteAllHardlinks ^<filename^>
echo e.g. DeleteAllHardlinks c:\data\myfile.txt
:ausmausraus
echo on
Thanks in advance!
Big thanks to Mofi!
The batch file could be very easily modified to support not just first argument, but all file name argument strings passed to the batch file by using one more for loop and %* as explained by call /?, i.e. use as replacement for the existing for loop:
for %%I in (%*) do for /F "delims=" %%J in ('ln.exe --list "%%~I" 2^>nul') do del /F "%%~J"
But the application starting the batch file has to pass each file name enclosed in double quotes to work properly.
Just using the for as offered in the comment solved the issue perfectly.
I have a file MyFile.ini with a section in it [MySection]
I want to add a new line with a return after it under that section, via a batch file.
Here's the batch file I last tried:
#echo off
Set file=MyFile.ini
Set section=[MySection]
Set newline=MyNewValue=MyNewSetting
for /f "tokens=*" %%l in (%file%) do (
(echo %%l)>> "%file%"
if /i "%%l"=="%section%" (
(echo %newline%)>> "%file%"
)
)
exit
The above has no effect on the ini file.
I want the ini file to go from this:
[MySection]
SomeExistingValue=SomeExistingSetting
To this:
[MySection]
SomeExistingValue=SomeExistingSetting
MyNewValue=MyNewSetting
Any answer will be greatly appreciated because once I can figure this out I can replicate it and add several settings to my file, it's all a bit tedious doing it manually, especially when I do the exact same thing to the exact same file, every time. The file name never changes, the section always exists, the setting I am adding never exists, so all that stuff does not need to be accounted for, nor does making a backup of the file, I can just unzip my backed up file if it gets messed up.
Here's an example based upon my commented methodology:
#Echo Off
Set "file=MyFile.ini"
Set "section=[MySection]"
Set "newline=MyNewValue=MyNewSetting"
Copy /Y "%file%" "%file%.bak">NUL 2>&1||Exit /B 1
(For /F "Tokens=1*Delims=]" %%G In (
'^""%__AppDir__%find.exe" /N /V ""^<"%file%.bak"^"'
)Do If /I "%%H"=="%section%" (Echo=%%H
Echo %newline%)Else Echo=%%H)>"%file%"
Rem Del "%file%.bak"
I have Remarked the last line, because I think it's more important to ensure that the resultant file, %file%, is correct, before you Delete the original, %file%.bak. The choice to do so ultimately lies with yourself.
I am trying to find all the pictures on my computer and copy them all to just 1 folder. Here is the command I am using in Admin level Command Prompt.
C:>dir /s /b *.jpg | findstr /v .jpg > AllPics
For some reason I get this output: "Access is denied."
What can I do to fix this?
List the full path names of the JPG files in the current directory and all subdirectories:
dir /s /b *.jpg
Redirect the standard output stream of the dir command to the standard input stream of the findstr command. Exclude any file that contains .jpg (case sensitive) in the full path name:
| findstr /v .jpg
The result of the previous action seems to be counter-intuitive as it will negate a lot of output generated by the dir command.
Write the standard output stream of the findstr command to a file called AllPics (without a file extension) in the current directory:
> AllPics
The current directory seems to be set to the root of the C: drive. Because administrator privileges are required to save files directly to the root of the C: drive, you'll receive the "Access is denied" error message.
As pointed out by TripeHound, the "Access is denied" error might be caused because there already exists a folder named AllPics on the root of the C: drive.
One way to get the desired result is to parse each JPG file to the xcopy command:
setlocal
set "files=c:\*.jpg"
set "destDir=c:\AllPics"
for /f "delims=" %%f in ('dir "%files%" /b /s') do (
xcopy "%%f" "%destDir%\"
)
I've added another variant of the script that alters the destination name of the file to be copied which prevents potentially overwriting any possible duplicates.
setlocal enabledelayedexpansion
set "files=c:\*.jpg"
set "destDir=c:\AllPics"
for /f "delims=" %%f in ('dir "%files%" /b /s') do (
for %%d in ("%destDir%\%%~nf*") do (
set /a count+=1
)
xcopy "%%f" "%destDir%\%%~nf!count!%%~xf*"
set count=
)
Due to delayed expansion, the last variant of the script is unable to handle files that contain carets (^) within their fully qualified path name.
When a command-line contains environment variables that are expanded at execution time (like the count variable in this script), the entire command-line seems to be parsed twice. The for-loop variables (%%f and variants thereof) will be expanded during the first parse, the count variable is expanded during the second parse. Because the for-loop variables are already expanded when the second parse takes place, any singular carets present in the values of the for-loop variables are swallowed by the parser and omitted from the final result.
Here is the revision of the script that should take care of the problem described:
setlocal enabledelayedexpansion
set "type=.jpg"
set "source=c:\"
set "dest=c:\AllPics"
for /r "%source%" %%f in ("*%type%") do (
for %%d in ("%dest%\%%~nf*") do (
set /a count+=1
)
set "source=%%f"
set "dest=%dest%\%%~nf"
xcopy "!source!" "!dest!!count!%type%*"
set count=
)
I do this with WinRAR. Archive files from different folders with recursive scan, then extract in one folder (with e option, not x which restores the directory tree).
I need to analyze the results provided in a list of CSV files where I need to have the file name and its second line, this for the set of files of the result directory in a single output. I found on Stack Overflow a piece of code to get the file name and a piece of code to get the second line of a file. Frankly it is beyond my knowledge to patch them together.
This is what I have so far:
setlocal enabledelayedexpansion
#echo "filename secondline"
for /f "delims=" %%a in ('dir /b /s *.*') do (#echo %%~nxa
set filename=%%~a
echo %filename%
pause
for /f "tokens=1,* delims=:" %i% in ('findstr /n /r . %filename%') do(if %i% geq 2 if %i% leq 2 echo %j% )
rem pause
)
endlocal
How to get the file name and the second line of all CSV files in directory into another CSV file?
It is not very clear what you want. So let me add some information about what I understood.
There are some CSV files in a directory like C:\Temp which is the current directory on executing the batch file which contains for example:
C:\Temp\DataFile1.csv
first name,second name,address,country
John,Doe,"20 Baker Street, Nowhere",Fantasy Island
x,y,anywhere,anyland
C:\Temp\DataFile2.csv
first name,second name,address,country
Jane,Doe,"5 Hammer Lane, Somewhere",Happy Island
x,y,anywhere,anyland
And in same directory one more CSV file with name ExtractionResults.csv should be created containing finally for this example:
"C:\Temp\DataFile1.csv",John,Doe,"20 Baker Street, Nowhere",Fantasy Island
"C:\Temp\DataFile2.csv",Jane,Doe,"2 Hammer Lane, Somewhere",Happy Island
The file names in first column should be quoted in case of one of the CSV files have a comma in file name.
For such a task the following commented batch file could be used:
#echo off
rem Name of results file without file extension with or without path.
set "ResultsFileName=ExtractionResults"
rem Delete a perhaps already existing results file from a previous run.
del "%ResultsFileName%.csv" 2>nul
rem Delete also a perhaps already existing temporary results file from
rem a not completed previous run. A temporary file with a file extension
rem different to CSV is used for the results in case of results file is
rem created also in current directory with file extension CSV. This must
rem be done to avoid that the second line of the results file is included
rem in the results, too.
del "%ResultsFileName%.tmp" 2>nul
rem Call for each CSV file in current directory the subroutine
rem GetSecondLine with full name of the CSV file.
for %%I in (*.csv) do call :GetSecondLine "%%~fI"
rem Use command MOVE instead of RENAME to rename the file in case of results
rem file name is specified at top with an absolute or relative path as the
rem command RENAME does not allow as second parameter a file name with path.
move /Y "%ResultsFileName%.tmp" "%ResultsFileName%.csv"
rem Exit this batch file and return to parent batch file or command processor.
goto :EOF
rem This is a subroutine. It prints to the temporary results file
rem just the second line or nothing if the file contains just 1 line.
rem The execution of goto :EOF results here in exiting the subroutine.
:GetSecondLine
for /F "usebackq skip=1 delims=" %%L in ("%~1") do (
echo "%~1",%%L>>"%ResultsFileName%.tmp"
goto :EOF
)
goto :EOF
Well, the code above contains more comment lines than commands. But for an even better understanding of the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
del /?
for /?
goto /?
move /?
rem /?
And read also the Microsoft article Using command redirection operators for an explanation of >> and 2>nul.
First of all, I am completely newbie here in batch file scripting and willing to learn it.
Let's say that I have a bunch of files in a folder with file extension/file-ext: ext1, ext2, and ext3.
every single file has incrementation such as:
filename.file-ext.1
filename.file-ext.2
filename.file-ext.3
...
filename.file-ext.x
The main target is to backup the old increments(from 1 to x-1), then backup them in a new folder, and then change the last increment "x" to "1".
Any kind of help from you would be highly appreciated.
The main problem when file names contain numbers is that they appear in alphabetic, not numeric, order. For example:
filename.file-ext.1
filename.file-ext.10
filename.file-ext.11
filename.file-ext.12
filename.file-ext.2
filename.file-ext.3
filename.file-ext.4
filename.file-ext.5
filename.file-ext.6
filename.file-ext.7
filename.file-ext.8
filename.file-ext.9
The last file in previous list should be 12, but the last name given by for or dir commands is 9. The Batch file below uses a two-pass approach: it first review the whole list and get the real last numeric file, then it process the list again and backup all files excepting the last one.
#echo off
setlocal EnableDelayedExpansion
rem First pass: Get the last numeric file
set last=0
for %%a in (filename.ext.*) do (
set num=%%~Xa
if !num:~1! gtr !last! set last=!num:~1!
)
rem Second pass: Backup all files excepting the last one
for %%a in (filename.ext.*) do (
if %%a neq filename.ext.%last% move %%a \backupDir
)
rem Rename the last file, if it is not 1
if %last% gtr 1 ren filename.ext.%last% filename.ext.1
The program above was written as simplest and clearest as possible. You must insert some quotes if file names may have spaces.
#ECHO OFF
SETLOCAL
SET filename=FILENAME
SET fileext=FILE-EXT
SET "deedpoll="
FOR /f "delims=" %%i IN ('dir /b /a-d /o:-d "%filename%.%fileext%*"') DO (
IF DEFINED deedpoll ECHO MOVE "%%i" "\newdir\" >nul
IF NOT DEFINED deedpoll SET "deedpoll=%%i"
)
IF DEFINED deedpoll ECHO REN "%deedpoll%" "%filename%.%fileext% 1"
since we have no clue as to the real filename or file extension involved, we can only assume that they will be simple alphamerics and spaces. I've also assumed that your destination directory exists and that your current directory is the one containing your fileset.
And I've assumed that your sequencing will work on DATE - it's the NEWEST file that does NOT get backed up (moved to newdir) and renamed ...1
It's quite simple. First clear deedpoll then perform a dir listing of the /b bare filename, /a-d with no directorynames /o:-d in reverse-order of date.
The first filename assigned to %%i will thus be the newest. deedpoll is not yet set, so the move is not executed, and then deedpoll is assigned the name of the newest file.
Since deedpoll is now set (or defined) then the move will be done for each of the other filenames matching the supplied mask.
finally, deedpoll is renamed to version 1.
Note that the REName and MOVE commands are merely ECHOed to the screen. This is to allow you to ensure that the batch would do what you want it to do. If all is correct, remove both of these ECHO keywords to activate the rename and move.