I have a large grouping of text files that have the basic pattern of
Files 1 Header
1. Data
2. Data
3. Data
My hope is to run a batch that will read it in and remove the 1st line and the three characters of the remaining lines and output like below.
Data
Data
Data
Tried building a loop to grab each line and then put it through %line:~3%but its not working as excepted and I'm not sure how to remove the first line of the text file to begin with.
Any help would be greatly appreciated.
for %%a in (*.txt) do for /f "skip=1usebackq tokens=1*" %%t in ("%%a") do >>%%~na.new echo %%u
Put each filename matching *.txt into %%a then read each line of the file, skipping the first, and tokenise on the first space (default delimiter), outputting the second (* token = rest-of-line) token by appending to filename samenameastextfile.new (%%~na.new)
This should create a new set of files with the processed results.
It looks like you want to process CSV files with removing header line and first data column containing just the row number.
This batch code might be the right one for your file reformatting task:
#echo off
for /F "delims=" %%I in ('dir * /A-D /B 2^>nul') do (
for /F "usebackq skip=1 tokens=1*" %%A in ("%%~I") do echo %%B>>"%%~nI.tmp"
move /Y "%%~nI.tmp" "%%~I"
)
The outer loop runs command DIR to get a list of file names in current directory and processes each file name from this list independent on what happens with the files in current directory while running the loop.
The inner loop processes each line from current file with the exception of first line because of skip=1. The lines are split up because of tokens=1* into two strings:
the number with the point being assigned to loop variable A, and
everything else from line after the spaces/tabs after number and point being assigned to next loop variable B.
The string assigned to loop variable B is output with command ECHO and redirected into a temporary file with same name as current file, but file extension tmp.
After processing all lines of current file, the temporary file is moved over the current file to replace it, i.e. delete current file and rename temporary file to name of current file.
Note 1: The files in current directory should not have the file extension tmp as in this case the batch code would not work.
Note 2: The batch file should not be in current directory with the files to modify or something other than * is used on DIR command line which excludes the batch file for being also processed.
For understanding 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.
dir /?
echo /?
for /?
move /?
Read also the Microsoft article about Using Command Redirection Operators.
Related
Been trying to create an additional batch script that processes files for me. I either get send 1 or several .pdf test files in a .rar file.
So what I am trying to aim for is:
If the first variable 1 is named 'test' then
Is there a .rar file in the folder from variable 2 then
Extract to a folder and then delete .rar file
else
check that there is a .pdf file and then copy to folder
Else
Tell the user that neither a file or a archive has been found
I've managed to scrape this together but I need help trying to expand it further to include all the options:
#echo off
set "cat=%1"
IF "%cat%"=="test" ( for /f %%G in ('dir *.rar /b') do set filename=%%~G)
echo %filename%
This only gives me half the file name as they have gaps in the filename, also need to change the dir in the 3rd line to be looking in variable 2 that is sent in.
To add to it I've just been told that it's the same for .txt files, the multiples are sent to me in a .rar file
I suggest to open a command prompt, run call /? and read the output help. The help explains how the arguments – also called options or parameters, but not variables – of a batch file can be referenced from within a batch file.
It is advisable to check if a batch file is called with at least one argument if it must be called with at least one argument and output a help for correct usage of the batch file if it was started without any argument or if it was started with /? which is the default on Windows to get help about a command or program.
The manual for console version of WinRAR is the file Rar.txt in program files folder of WinRAR. It can be read in this text file after opening it with a double click that Rar.exe can extract one or more *.rar archive files found in a directory. For that reason it is not really necessary to use command FOR. But it is advisable for this task to use command FOR as the RAR file(s) should be deleted after successful extraction of the RAR archive(s).
Let us look on the FOR command line for /f %%G in ('dir *.rar /b') do and what it does on execution.
FOR with option /F to process a text file content or a single string or the output of a command line results in this case in starting a command process in background with %ComSpec% /c and the command line between the two ' appended. So executed by the Windows command process cmd.exe processing the batch file with for /F is the following with Windows installed into C:\Windows as by default:
C:\Windows\System32\cmd.exe /c dir *.rar /b
The command DIR executed by separate command process in background
searches in current directory
for directory entries (files or directories)
matching the wildcard pattern *.rar
and not having hidden attribute set (implicit default is /A-H on option /A not specified at all)
and outputs to handle STDOUT the found directory entries matching the criteria above in bare format line by line which means with just file/folder name without path and never enclosed in double quotes even on containing a space or one of these characters &()[]{}^=;!'+,`~.
An error message is output by DIR to handle STDERR of background command process if it cannot find any directory entry matching the search criteria.
FOR respectively the command process processing the batch file redirects the output to handle STDERR of the background command process to its own STDERR handle which results in getting it displayed in console window in this case. But the output to handle STDOUT of started background command process is captured by FOR respectively the command process processing the batch file and is processed line by line after started background command process terminated itself.
FOR used with option /F always ignores empty lines. This does not matter here because of DIR does not output empty lines on being executed with option /B.
for /F splits up a non-empty line by default into substrings using normal space and horizontal tab as string delimiters and assigns by default just first space/tab separated string to the specified loop variable which is here the loop variable G. for /F ignores by default additionally also a processed line if the first substring after splitting the line up starts with a semicolon because of eol=; is the default for end of line option.
So the command line for /f %%G in ('dir *.rar /b') do causes several problems on processing the list of directory entries output by DIR.
For a file/folder name containing a space just the first space/tab separated part of the file/folder name is assigned to loop variable G instead of complete name. For example a name like My Archive.rar results in just My is assigned to the loop variable G.
A file/folder name with one or more leading spaces is assigned to loop variable G without those leading spaces which means again that G does not hold complete name. For example a name like TwoLeadingSpaces.rar results in getting assigned to loop variable G just TwoLeadingSpaces.rar without the two leading spaces and the file (or folder) is not found on referencing the value of loop variable G.
A file/folder name with a semicolon at beginning after zero or more leading spaces is completely ignored by command FOR for further processing. For example names like ;Test.rar (name beginning with a semicolon) or ;TestWithALeadingSpace.rar (name with leading space and a semicolon) are completely ignored for further processing by FOR.
The points 2 and 3 are usually no problem as file/folder names with leading space(s) or a semicolon at beginning are really very rare. But a file/folder name with a space occurs very often.
A solution would be using FOR without option /F:
for %%G in (*.rar) do
FOR searches now itself for non-hidden files (not directories) in the current directory matching the wildcard pattern *.rar and assigns a found file name without path to loop variable G and next runs the command(s) after do. There is no additional command process started and there is no substring splitting done.
But there is a problem with this very simple solution in case of the commands executed for each found file name delete, move or rename files matched by the wildcard pattern *.rar. The list of directory entries matching the wildcard pattern changes on each iteration of the body of the FOR loop while command FOR queries the directory entries one after the other with executing the commands between each directory query. This is especially on FAT16, FAT32 and exFAT drives a real problem, but can result also in unexpected behavior on NTFS drives.
Whenever a FOR loop is used to process a list of files which could change during the iterations of the loop because of deleting, moving or renaming the files matched by a wildcard pattern, it is better to process a list of files loaded completely into memory before first iteration of the loop.
So a better solution for this task with the requirement to delete a RAR archive file after successful extraction is:
for /F "eol=| delims=" %%I in ('dir *.rar /A-D /B 2^>nul') do
The DIR option /A-D results in ignoring directory entries with attribute directory. So output by DIR are just file names matching the wildcard pattern in current directory including hidden RAR archive files.
2^>nul is passed as 2>nul to the background command process which results in redirecting the error message output by DIR on no *.rar file found to device NUL to suppress it.
Read the Microsoft article about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
The for /F option eol=| changes the end of line character from ; to |. No file name can have a vertical bar in its file name according to Microsoft documentation about Naming Files, Paths, and Namespaces. So no file name is ignored anymore by FOR because of end of file option.
The for /F option delims= changes the delimiters list for line splitting into substrings to an empty list of delimiters which disables the line splitting behavior completely. So a file name with one or more spaces anywhere in file name is assigned completely to the specified loop variable I.
The task description is not very clear regarding to what to do depending on the batch file arguments, especially if the first argument is not case-insensitive test.
However, the following commented batch file could be working for this task on being called with first argument being test or with no arguments at all or with /? as first argument.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
if "%~1" == "" goto OutputHelp
if "%~1" == "/?" goto OutputHelp
if /I not "%~1" == "test" goto MoreCode
set "SourceFolder=%~2"
if defined SourceFolder goto CheckFolder
echo/
echo Error: Folder with RAR or PDF file(s) not specified on command line.
goto OutputHelp
:CheckFolder
rem Replace all forward slashes by backslashes in folder name.
set "SourceFolder=%SourceFolder:/=\%"
rem Append a backslash to folder path if it does not end with a backslash.
if not "%SourceFolder:~-1%" == "\" set "SourceFolder=%SourceFolder%\"
rem Check the existence of the source folder.
if exist "%SourceFolder%" goto ProcessFolder
echo/
echo Error: Folder "%SourceFolder%" does not exist.
goto OutputHelp
:ProcessFolder
rem Get full qualidfied folder name, i.e. the folder name
rem with its absolute path and ending with a backslash.
for %%I in ("%SourceFolder%") do set "SourceFolder=%%~fI"
rem Define the destination folder for the PDF files extracted from the
rem RAR archive file(s) in source folder or copied from source folder.
set "DestinationFolder=C:\Temp\Test\"
rem Search for all *.rar files in folder passed with second argument and
rem extract all *.pdf files in each RAR archive file to the configured
rem destination folder. Rar.exe creates the destination folder automatically
rem if it is not already existing. The batch file is halted after processing
rem a RAR file on which Rar.exe exited with a value greater 0. Read the exit
rem codes documentation of Rar.exe at bottom of text file Rar.txt for more
rem information about the RAR exit codes. See Rar.txt also for the meaning
rem of the few RAR switches used here.
set "RarFileCount=0"
for /F "eol=| delims=" %%I in ('dir "%SourceFolder%*.rar" /A-D /B 2^>nul') do (
set /A RarFileCount+=1
"%ProgramFiles%\WinRAR\Rar.exe" e -cfg- -idcdp -or -- "%SourceFolder%%%I" *.pdf "%DestinationFolder%"
if not errorlevel 1 (del /A /F "%SourceFolder%%%I") else echo/& pause
)
if %RarFileCount% == 0 goto CheckFiles
if %RarFileCount% == 1 (set "PluralS=") else set "PluralS=s"
echo/
echo Info: Processed %RarFileCount% *.rar file%PluralS% in folder "%SourceFolder%".
goto EndBatch
:CheckFiles
echo Info: There are no *.rar files in folder "%SourceFolder%".
if exist "%SourceFolder%*.pdf" goto CopyFiles
echo Info: There are no *.pdf files in folder "%SourceFolder%".
goto EndBatch
:CopyFiles
rem Copy all PDF files in source folder to destination folder. xcopy.exe
rem creates destination folder automatically if it is not already existing.
echo/
%SystemRoot%\System32\xcopy.exe "%SourceFolder%*.pdf" "%DestinationFolder%" /C /I /Y
goto EndBatch
:OutputHelp
echo/
echo Usage: %~n0 [TEST] [Folder with RAR or PDF file(s)]
echo/
echo If the first argument is case-insensitive TEST, the second argument
echo specifies the folder containing the RAR files to extract or the PDF
echo files to copy to destination folder. The folder must be specified
echo with first argument being TEST.
echo/
pause
goto EndBatch
:MoreCode
rem Add here the code to execute on first argument is not case-insensitive TEST.
:EndBatch
endlocal
For understanding 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 /?
dir /?
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
rem /?
set /?
setlocal /?
xcopy /?
"%ProgramFiles%\WinRAR\Rar.exe" /?
You can use this:
#echo off
set "cat=%~1"
IF "%cat%"=="test" (
for %%G in (*.rar) do set filename=%%G
)
echo %filename%
Here wildcard is used to get all the rar files in the directory.
I have a file Staffel artikelen met € bedragen.xlsx and I'd like to copy it with new file name StaffelArtikelen.xlsx into a different directory. I think the Euro sign in file name creates an error.
How can I avoid this error:
Source files cannot be renamed on the source location.
I tried:
set dest1=\\serverb02\DATA\Databeheer\StaffelArtikelen.xlsx
set source=\\serverb02\DATA\Uitwisseling
pushd "%source%"
for /f "tokens=*" %%G in ('dir Staffel artikelen met *.xlsx /b /a-d /od') do SET "newest=%%G"
copy "%newest%" "%dest1%"
The result is a 1 kB file (should be +20 kB) in destination location. I expect StaffelArtikelen.xlsx.
Files in source directory:
DS-Staffel artikelen met%korting variable.xlsx
Staffel artikelen met%korting variable.xlsx
Staffel artikelen met € bedragen variable.xlsx
The latest modified file should be copied with StaffelArtikelen.xlsx as destination file name.
This batch file could be used for this task, too.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "dest1=\\serverb02\DATA\Databeheer\StaffelArtikelen.xlsx"
set "source=\\serverb02\DATA\Uitwisseling"
for /F "eol=| delims=" %%I in ('dir "%source%\*Staffel artikelen met*.xlsx" /A-D-H /B /O-D 2^>nul') do (
copy /Y /B "%source%\%%I" "%dest1%" >nul
goto CopyDone
)
:CopyDone
rem Other commands can be inserted here.
endlocal
A file/folder name must be enclosed in " if it contains a space or one of these characters &()[]{}^=;!'+,`~ as explained by help of Windows command processor output on running in a command prompt window cmd /? on last help page. Other argument strings like variable=value of command set should be also enclosed in double quotes to get all characters inside quoted argument string interpreted as literal characters.
Command FOR with option /F and a command line specified between ' results in starting one more command process in background with %ComSpec% /c and the specified command line. So executed by FOR is with Windows being installed into C:\Windows:
C:\Windows\System32\cmd.exe /c dir "\\serverb02\DATA\Uitwisseling\*Staffel artikelen met*.xlsx" /A-D-H /B /O-D 2>nul
DIR searches
in directory \\serverb02\DATA\Uitwisseling
just for non-hidden files because of option /A-D-H (attribute not directory and not hidden)
matching the wildcard pattern *Staffel artikelen met*.xlsx
and outputs in bare format because of option /B just the names of the files without file path
ordered reverse by last modification date (default) because of option /O-D which means latest/newest modified file first and oldest file last.
The file names are output to handle STDOUT (standard output) of background command process. This output is captured by FOR respectively the command process running the batch file.
It is possible that no directory entry matches the specified search criteria resulting in printing an error message by DIR to handle STDERR (standard error) which is redirected by FOR to STDERR of command process running the batch file. This error message can be suppressed by redirecting it to device NUL by started cmd.exe running in background.
Read the Microsoft documentation about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
FOR with option /F processes the captured standard output of started command process line by line after started cmd.exe terminated itself as follows:
Empty lines are always ignored by FOR, but empty lines do not occur here.
A line is split up by default into substrings using normal space and horizontal tab as delimiters and just first space/tab separated string is assigned to specified loop variable I. This line splitting behavior is not wanted in this case as the file names contain definitely spaces and the entire file name should be assigned to loop variable I and not just the file name part up to first space. For that reason delims= defines an empty list of delimiters to turn off line splitting completely.
Next FOR checks if first substring, i.e. entire file name in this case, starts with default end of line character ; which is a valid character for first character of a file name. For that reason eol=| redefines end of line character to vertical bar which no file name can contain according to Microsoft documentation page Naming Files, Paths, and Namespaces.
BTW: Using just option tokens=* results in running the line splitting, but everything after leading spaces/tabs is assigned to specified loop variable even if there are spaces after first non-space character. It is very unusual that a file name starts with one or more spaces, but it is possible. tokens=* would assign the file name without the leading spaces to loop variable I and further processing fails because of file with missing spaces at beginning of file name is not found or in worst case a different file is processed having same file name as file to process after the removed leading spaces.
So on first iteration of the loop the file name of newest file found by DIR is assigned to the loop variable I without path. This file is copied as binary file to destination directory with new file name StaffelArtikelen.xlsx and overwriting in destination directory a file existing there already with same file name without prompting user.
Note: The file copy can fail if
source file data cannot be read because of missing file reading permissions or
source file is currently opened by an application which prevents reading the file data while opened by the application or
destination file cannot be written because of missing file writing permissions or
destination file exists already and is currently opened by an application which prevents overwriting the file data while opened by the application.
The provided batch file contains no check on file copy being successful or failed.
The command GOTO is used to exit the loop after newest file is copied to ignore all other file names output by DIR and continue batch file processing below the line with label CopyDone.
For understanding 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.
copy /?
dir /?
echo /?
endlocal /?
for /?
goto /?
rem /?
set /?
setlocal /?
When dealing with file names containing spaces, you have to double quote them.
Otherwise dir will treat the space separated parts as individual items to list
Staffel
artikelen
met
*.xlsx
As there are presumably no files without extension Staffel, artikelen, met only *.xlsx will return the newest of ALL present excel files and
as you are not showing the source file in your batch it is copied to the new name without being noticed.
This should do as expected:
set dest1=\\serverb02\DATA\Databeheer\StaffelArtikelen.xlsx
set source=\\serverb02\DATA\Uitwisseling
pushd "%source%"
set "newest="
for /f "tokens=*" %%G in ('dir "Staffel artikelen met *.xlsx" /b /a-d /od') do SET "newest=%%G"
if defined newest (
copy "%newest%" "%dest1%"
) else (
Echo no file found matching "Staffel artikelen met *.xlsx"
)
I need a batch script to keep only the newest N files based on the timestamp from filename and only if the size is bigger than 150 KB (if the size is less, means the backed up file is damage and can be deleted).
I found similar scripts but only for Linux.
The script should first delete all the files with size less than 150 KB and keep the newest N files using as reference the timestamp from filename not the modified date of the files.
This .txt file is modify a few times per day except the weekend.
The file is backed up a few times per day and the date and time are added in filename.
Example of filename: Test_2019-01-16_21-57-34.txt
Path is: Z:\
OS is Windows 8
This task could be done with the following batch file:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "Z:\" || goto :EOF
for /F "eol=| delims=" %%I in ('dir /A-D-H /B /OS "Test_*.txt" 2^>nul') do if %%~zI LEQ 153600 (del "%%I") else goto DeleteOldFiles
:DeleteOldFiles
for /F "skip=5 eol=| delims=" %%I in ('dir /A-D-H /B /O-N "Test_*.txt" 2^>nul') do del "%%I"
popd
endlocal
The directory Z:\ is set as current directory using command PUSHD with exiting batch file execution with goto :EOF on failure.
Next command FOR executes with cmd.exe /c (more precisely %ComSpec% /c) in a separate command process in background the command line:
dir /A-D-H /B /OS "Test_*.txt" 2>nul
The command DIR outputs
the names of non-hidden files because of option /A-D-H (attribute not directory and not hidden)
in bare format without any additional data because of option /B
ordered by size with smallest file first and largest file last because of option /OS
matching the wildcard pattern Test_*.txt in current directory.
An error message output by DIR on not finding any directory entry in current directory matching these criteria is suppressed by redirecting the error message from handle STDERR to device NUL.
Read the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
FOR captures everything written to handle STDOUT of started command process and processes the captured output line by line after started cmd.exe terminated itself.
FOR with option /F ignores empty lines which do not occur here on using DIR with option /B.
FOR would split up each line into substrings (tokens) using normal space and horizontal tab as delimiters and would assign just first space/tab delimited string to specified loop variable I. This line splitting behavior is disabled by using delims= to define an empty list of string delimiters. delims= is not really necessary here in this case because of the file names of the backup files do not contain a space character.
FOR would also ignore lines on which the first substring after splitting up the line into substrings (not necessarily the substring assigned to the loop variable) starts with a semicolon because of ; is the default end of line character. A file name can start with a semicolon and so it is better to redefine end of line character with eol=| or eol=? to vertical bar or question mark which no file name can contain at all. In this case eol=| would not be necessary because output by DIR are only file names starting with Test_ and so the default eol=; does not need to be overwritten with eol=|.
The IF command compares size of current file with value 153600 which is 150 KiB and the file is deleted if its size is less or equal this value.
Note: Windows command processor uses signed 32-bit arithmetic. So a file with more than 2,147,483,647 bytes (2 GiB) is not correct processed by this IF condition. It would be necessary to work around this signed 32-bit arithmetic limitation if backup files can be ever larger than 2 GiB.
Otherwise the first loop is exited with a jump to line below label DeleteOldFiles on current file being larger than 150 KiB because this file and all other files output by DIR after this file have a file size greater than 150 KiB.
The second FOR loop runs again command DIR in a separate command process in background. But the DIR option /OS is replaced by /O-N to get the file names output in reverse order according to file name. The file name with newest date is output first by DIR and the file name with oldest date in name is output last because of using international date format in the file names.
The second FOR command line contains additionally the option skip=5 to skip first five file names, i.e. the newest five backup files.
If DIR outputs more than five file names, all other files being older than the first five output file names are deleted by command DEL executed unconditionally by second FOR.
Finally the initial current directory is set again as current directory using POPD.
For understanding 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.
del /?
dir /?
echo /?
for /?
goto /?
if /?
popd /?
pushd /?
See also:
Where does GOTO :EOF return to?
Single line with multiple commands using Windows batch file for an explanation of ||.
One more hint: Replace Z:\ by the UNC path if drive letter Z: is assigned to a network resource. And make sure the batch file is executed by an account which has the permissions to access the network resource and delete files in that folder on network resource in case of this batch file is executed as scheduled task. See also What must be taken into account on executing a batch file as scheduled task?
Does anyone know how to replace first space in a file name by a text like _TEMP- after the first 8 characters of the file name?
Also I was hoping to write a batch file that can change multiple files in a directory with similar file name structure.
For example:
Before: 12345678 Hello World.txt
After: 12345678_TEMP-Hello World.txt
Here is a simple code demonstrating usage of FOR for this task:
#echo off
set "FileName=12345678 Hello World.txt"
for /F "tokens=1*" %%I in ("%FileName%") do echo ren "%FileName%" "%%I_TEMP-%%J"
set "FileName="
echo/
pause
The file name is assigned here to an environment variable.
The command FOR with option /F splits the file name string specified in double quotes up into substrings using the default delimiters space and horizontal tab.
With tokens=1* is defined that first space/tab delimited string is assigned to specified loop variable I. The asterisk is responsible for not further splitting up on spaces/tabs after first string and instead assign everything after space(s)/tab(s) after first string to next loop variable which is according to ASCII table the loop variable J. So for this example I gets assigned 12345678 and J gets assigned Hello World.txt
The command to execute is here ECHO which prints the command line for renaming the file which would be executed without echo.
Another batch code which renames all non hidden files in current directory with a space in file name not containing already _TEMP- in file name:
#echo off
for /F "eol=| delims=" %%# in ('dir "* *" /A-D-H /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /L /V /C:_TEMP- 2^>nul') do (
for /F "tokens=1*" %%I in ("%%#") do ren "%%#" "%%I_TEMP-%%J"
)
The outer FOR executes in a separate command process started with cmd.exe /C in background the command line:
dir "* *" /A-D-H /B 2>nul | C:\Windows\System32\findstr.exe /L /V /C:_TEMP- 2>nul
DIR searches in current directory because of /A-D-H (attribute not directory and not hidden) just for non hidden files matching the wildcard pattern * * and outputs just the found file names because of /B (bare format). An error message in case of no file matching these criteria is suppressed by redirecting the error message written to handle STDERR to the device NUL.
The file names output by DIR to handle STDOUT are redirected to STDIN of command FINDSTR which searches in the list of file names case-sensitive and literally for the string _TEMP-. But output are not the lines containing this string, but the inverted result because of /V. So output are by FINDSTR to handle STDOUT all lines not containing the string _TEMP-. A possible error message output by FINDSTR to handle STDERR is suppressed by redirecting it to device NUL.
The outer FOR captures the output to STDOUT of background command process and then processes the captured output line by line. It is very important here to process a captured list of file names because the found files to modify in current directory are renamed by the command inside the FOR loop. Otherwise on using FOR itself to find files matching wildcard pattern * * it could happen that a renamed file is processed multiple times or some files are skipped because of list of files in current directory changes during loop execution.
Empty lines are always ignored by FOR, but the list of file names does not contain empty lines.
for /F would also ignore lines starting with a semicolon by default. This behavior is changed by specifying eol=| to skip lines starting with a vertical bar. File names can begin with a semicolon, but file names cannot contain a vertical bar. So it is made sure not skipping any file name with eol=|.
for /F with default options would split a line (file name) up on spaces/tabs and would assign just first substring to specified loop variable #. This behavior is not good here because the complete file name from filtered output of DIR is needed for the file renaming task. For that reason delims= is used specifying an empty list of delimiters resulting in no splitting up the line (file name) into substrings (tokens).
It could be that a file is named for example 12345678 Hello World.txt with multiple spaces in series in file name. This is the reason for first getting assigned to loop variable # the complete file name as found by DIR in current directory.
The file name is split up into two substrings as explained above and then renamed which is successful if there is not already a file or folder with new name in current directory and the file to rename is not opened currently by an application with sharing access denied.
The batch file above could be coded also with a single command line in batch file:
#for /F "eol=| tokens=1*" %%I in ('dir "* *" /A-D-H /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /L /V /C:_TEMP- 2^>nul') do #ren "%%I %%J" "%%I_TEMP-%%J"
Or the single command line is really executed directly in a command prompt window as follows with just one percent sign on referencing the loop variables I and J:
for /F "eol=| tokens=1*" %I in ('dir "* *" /A-D-H /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /L /V /C:_TEMP- 2^>nul') do #ren "%I %J" "%I_TEMP-%J"
But this single command line version works only for files with always a single space character between first part and remaining part of file name, i.e. it works for 12345678 Hello World.txt but does not work for 12345678 Hello World.txt.
For understanding 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.
dir /?
echo /?
findstr /?
for /?
pause /?
ren /?
set /?
Read also the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul and |. The redirection operators > and | must be escaped with caret character ^ on FOR command line to be interpreted as literal characters when Windows command interpreter processes this command line before executing command FOR which executes the embedded command line with using a separate command process started in background.
Here's a possible batch file solution for replacing the 9th character with your given string, _TEMP-; it utilises PowerShell:
#Echo Off
Set "SD=%UserProfile%\Documents"
PowerShell -C "GI '%SD%\???????? *.txt'|Ren -N {$_.BaseName.Remove(8,1).Insert(8,'_TEMP-')+$_.Extension} -Wh"
Pause
Just change %UserProfile%\Documents on line 2 to the folder containing the .txt files, (if they're not really .txt files either, change txt on line 3 to the appropriate string or wildcard. If the string you wish to add is also different, please replace _TEMP- on line 3 with your desired string too). The script is currently designed to show you what would happen, were the files to be renamed; if you're happy with the output, change the script thus:
#Echo Off
Set "SD=%UserProfile%\Documents"
PowerShell -C "GI '%SD%\???????? *.txt'|Ren -N {$_.BaseName.Remove(8,1).Insert(8,'_TEMP-')+$_.Extension}"
If you didn't want to remove the space after those 8 characters, then:
#Echo Off
Set "SD=%UserProfile%\Documents"
PowerShell -C "GI '%SD%\???????? *.txt'|Ren -N {$_.BaseName.Insert(8,'_TEMP-')+$_.Extension} -Wh"
Pause
…and similarly if you're happy with the output, change the script to this:
#Echo Off
Set "SD=%UserProfile%\Documents"
PowerShell -C "GI '%SD%\???????? *.txt'|Ren -N {$_.BaseName.Insert(8,'_TEMP-')+$_.Extension}"
I'm sure there will be better ways of doing this, even with PowerShell, but given that your provided information is lacking, I'm unable to suggest anything else at this time.
I'm trying to understand a pretty complex (to me it is anyhow) batch file that somebody else has written.
The person who wrote it is no longer around so it's down to me to try and figure out what the hell is going on.
To start with, I need to understand what this line is doing: -
for /f "skip=4 tokens=1,5" %%A in ('dir /tc /o-d %i%\PICKS*') do (
Can anybody help?
1. How to get help on Windows commands?
Help on any Windows standard command can be get by running the command with parameter /? from within a command prompt window.
Try it out with executing in a command prompt window dir /? and for /?.
Or run this long command line with multiple commands:
dir /? >"%UserProfile%\Desktop\Help_on_DIR_FOR.txt" & for /? >>"%UserProfile%\Desktop\Help_on_DIR_FOR.txt" & %SystemRoot%\Notepad.exe "%UserProfile%\Desktop\Help_on_DIR_FOR.txt"
This is like running the three command lines:
dir /? >"%UserProfile%\Desktop\Help_on_DIR_FOR.txt"
for /? >>"%UserProfile%\Desktop\Help_on_DIR_FOR.txt"
%SystemRoot%\Notepad.exe "%UserProfile%\Desktop\Help_on_DIR_FOR.txt"
It runs command DIR to output its help with redirecting the help into file Help_on_DIR_FOR.txt on the desktop. Next it runs command FOR to output its help with appending it to file Help_on_DIR_FOR.txt. And last Windows Notepad is started to display the file Help_on_DIR_FOR.txt.
See the Microsoft documentation about Using command redirection operators for the details on the redirection operators > and >>.
See the Wikipedia article about Windows Environment Variables for the details on the predefined environment variables UserProfile and SystemRoot.
Other sources for help on Windows commands are:
Microsoft's command-line reference
SS64.com - A-Z index of the Windows CMD command line
2. What to know about text encoding of batch files?
The text in a batch file is encoded using one byte per character. That means on using Windows Notepad to create/edit a batch file it is necessary to make sure to save the batch file using ANSI encoding.
Text files using a text encoding with only one byte per character are limited to 256 characters. So there must be a table to define which byte value represents which character. There are many such tables as the mankind on this planet uses more than 256 characters. For text encoding the used code page defines the table used for character encoding.
In a Windows command prompt window (console) is used by default an OEM (Original Equipment Manufacturer) code page while in Windows GUI applications like Windows Notepad is used by default a Windows (ANSI) code page.
Which code page is used depends on Windows region setting as defined for the used account. For example in North American countries code page 437 is used for console and Windows-1252 by Windows GUI applications while in Western European countries code page 850 is used for console and also Windows-1252 by Windows GUI applications.
Run in a command prompt window the command chcp to get displayed which code page is used on console according to region (country) set for your account.
The knowledge about code pages is important on writing a batch file using a Windows GUI editor which contains text with characters with a code value greater 127 decimal because for the upper half of the 256 characters the used code page defines which value represents which character.
It can be easily seen that all non ASCII characters of the help are displayed wrong in Notepad in case of not using English Windows and looking on help output for DIR and FOR to console redirected into a text file viewed with Windows Notepad because of the text file was not created using the Windows (ANSI) code page as expected by Notepad.
3. What does the command line ask for?
Let us analyze the command line
for /f "skip=4 tokens=1,5" %%A in ('dir /tc /o-d %i%\PICKS*') do (
by executing following commented batch file:
#echo off
rem Setup a local environment for the commands below.
setlocal EnableExtensions DisableDelayedExpansion
rem Define the directory to process.
set "i=%TEMP%\UnderstandingDirFor"
rem Create this directory temporarily.
md "%i%" 2>nul
rem Create a file with PICKS at beginning of file name in the directory.
echo PICKS_SampleFile.tmp>"%i%\PICKS Sample File.tmp"
rem Copy the batch file into directory with PICKS in file name.
copy "%~0" "%i%\PICKS_ExampleFile.bat" >nul
rem Copy another file into directory with PICKS in file name.
copy "%SystemRoot%\System32\cmd.exe" "%i%\PICKS cmd.exe" >nul
rem Copy a file into directory with a file name not starting with PICKS.
copy "%SystemRoot%\System32\sort.exe" "%i%" >nul
cls
echo First output is from:
echo/
echo dir "%%i%%\PICKS*"
echo/
dir "%i%\PICKS*"
echo/
pause
echo/
echo ===============================================================================
echo/
echo Second output is from:
echo/
echo dir /tc /o-d "%%i%%\PICKS*"
echo/
dir /tc /o-d "%i%\PICKS*"
echo/
pause
echo/
echo ===============================================================================
echo/
echo Third output is from:
echo/
echo for /f "skip=4 tokens=1,5" %%%%I in ('dir /tc /o-d "%%i%%\PICKS*"')
echo/
for /f "skip=4 tokens=1,5" %%I in ('dir /tc /o-d "%i%\PICKS*"') do (
echo Token 1 = loop variable I = %%I
echo Token 5 = loop variable J = %%J
)
echo/
pause
echo/
echo ===============================================================================
echo/
echo Fourth output is from:
echo/
echo for /F "skip=5 tokens=1,4*" %%%%I in ('dir /A-D /O-D /TC "%%i%%\PICKS *" 2^^^>nul')
echo/
for /F "skip=5 tokens=1,4*" %%I in ('dir /A-D /O-D /TC "%i%\PICKS *" 2^>nul') do (
if /I "%%J" == "PICKS" (
echo Token 1 = loop variable I = %%I
echo Token 5 = loop variable K = %%K
)
)
echo/
pause
rem Delete the created directory with the four files.
rd /S /Q "%i%"
rem Purge the local environment and restore initial environment.
endlocal
Note: The batch file uses "%i%\PICKS*" instead of %i%\PICKS* to work also for directory paths containing a space character or one of these characters &()[]{}^=;!'+,`~. The path of the directory for temporary directories and files referenced with %TEMP% included in %i% could contain a space character which requires the usage of double quotes.
The first output into console window is from:
dir "%i%\PICKS*"
It can be seen on output that the files (or directories) matching the wildcard pattern PICKS* are listed with last modification date at beginning and in order as used internally by the file system.
The file sort.exe is not output because it's file name does not start with PICKS.
What DIR outputs exactly depends on language of Windows. But the first five lines are the header and the last three lines are the summary in language of Windows.
The second output into console window is from:
dir /tc /o-d "%i%\PICKS*"
The output of the list is now in reverse order by date/time because of option /o-d using creation date because of the option /tc.
By default the file/directory with the oldest date is output first and the file/directory with the newest date is output last on using /od. The order is reverse with /o-d which means output is first the newest and last the oldest file/directory.
It can be seen on comparing the two outputs that the creation date of all copied files is newer than their last modification date.
The reason is that the creation date is the date a file/directory was created in current directory and NOT when the file itself was created the first time anywhere.
For that reason the creation date is most often not really useful and using the creation date might be also not useful here. But this can't be determined without knowing what the entire batch file is written for.
The third output into console window is from:
for /f "skip=4 tokens=1,5" %%I in ('dir /tc /o-d "%i%\PICKS*"') do (
echo Token 1 = loop variable I = %%I
echo Token 5 = loop variable J = %%J
)
FOR starts a new command process in background using cmd.exe with option /C for an automatic close for executing the specified command line dir /tc /o-d "%i%\PICKS*". To be more precise there is executed by FOR:
%ComSpec% /c dir /tc /o-d "C:\Users\UserName\AppData\Local\Temp\PICKS*"
ComSpec is a predefined environment variable with full qualified file name of Windows command processor which is usually C:\Windows\System32\cmd.exe.
The output of the entire command process to handle STDOUT is captured by FOR and processed line by line after background command process finished and closed itself.
for /F ignores by default all empty lines. The other lines are by default first split up into substrings using normal space and horizontal tab as string delimiters which means no substring contains any space/tab. If the first space/tab delimited substring starts now with a semicolon being the default end of line character, the line is not further processed by FOR and so also ignored like an empty line. Then the first substring is assigned to specified loop variable and the single command or the command block is executed next.
Command FOR is instructed with option skip=4 to skip the first four lines of captured output and start processing the output with line five which is the last empty line of header of DIR output.
Command FOR is instructed with option tokens=1,5 to not assign only first space/tab delimited string to specified loop variable I, but also the fifth space/tab delimited string to the loop variable being the next one after I in ASCII table if there is a fifth substring (= token) at all which is loop variable J.
This substrings/tokens to loop variable assignments feature is the reason why loop variables are case-sensitive while environment variables are not case-sensitive.
Command FOR ignores a line if the first specified token as specified with tokens= cannot be determined from a line. But it does not ignore lines on which not all strings of interest can be found in line as it can be seen on third output.
What fifth space/tab delimited string assigned to loop variable J should be in original batch code depends on what the directory referenced with %i% contains - files or directories - and how the file/directory names starting case-insensitive with PICKS really look like.
For the example I assumed PICKS* matches files with file name starting with PICKS (note the space at end) and more characters not containing a space character.
By looking on third output it can be seen that the names of the three PICKS* are not assigned all to loop variable J as expected and the summary lines are also processed.
I suppose this does not happen in original batch file because of file/directory names do not contain another space character after PICKS and the FOR loop is exited with command GOTO after processing first file/directory name.
The fourth output into console window is from:
for /F "skip=5 tokens=1,4*" %%I in ('dir /A-D /O-D /TC "%i%\PICKS *" 2^>nul') do (
if /I "%%J" == "PICKS" (
echo Token 1 = loop variable I = %%I
echo Token 5 = loop variable K = %%K
)
)
This is an improved version suitable to output only the creation date and the name of the files in temporary directory matching the wildcard pattern.
The DIR option /A-D is added to ignore subdirectories and get just a list of files of which file name is matched by the wildcard pattern.
The wildcard pattern is modified to PICKS * which results in command DIR not outputting anymore a line for file PICKS_ExampleFile.bat because of the underscore instead of the space in the file name.
On DIR command line 2^>nul is appended for suppressing a perhaps output error message. Command DIR outputs an error message to handle STDERR not processed by FOR and therefore written to console when it can't find a file matching the wildcard pattern in the specified directory or when the directory does not exist at all. This error message is redirected to device NUL to suppress it. The redirection operator > must be escaped here with caret character ^ to be interpreted first as literal character on parsing FOR command line by Windows command interpreter, but as redirection operator on execution of DIR command line in background command process.
The skip option of command FOR is modified to skip=5 to skip the first 5 lines, i.e. the entire header block of command DIR.
The tokens option of command FOR is modified to tokens=1,4*. This results on lines containing a file name matching the wildcard pattern PICKS * in assigning creation date of file to loop variable I, PICKS to loop variable J and everything after the space(s) after PICKS in line without further splitting up on spaces/tabs to loop variable K.
The IF condition compares case-insensitive, because of /I, the string assigned to loop variable J with PICKS and outputs creation date and file name only if the compared strings are equal. This IF condition filters out the summary lines on output.
Because of * in tokens=1,4* the entire file name with the exception of PICKS is output even if the file name contains more space characters like in Sample File.tmp.
4. How to debug a batch file?
Windows command processor outputs by default each command line respectively command block defined with ( ... ) after processing it before execution. The output of the command lines/blocks before execution can be disabled with using #echo off at top of the batch file whereby # prevents already the output of this command line.
But on debugging a batch file it is often necessary to see what is really executed. This can be achieved with either removing #echo off, or changing it to #echo ON or comment this command line out with #rem #echo off or with ::#echo off whereby the last solution changes the command line into an invalid label line.
It is of course also possible to keep #echo off at top of the batch file and use inside the batch file echo on and some lines below #echo off to get displayed on execution just the commands between those two command lines to debug just a specific block of the batch file.
And for debugging the batch file it should not be executed by just double clicking on it in Windows Explorer. This results in starting cmd.exe implicit with option /C for executing the batch file and close the command process and therefore also the console window immediately on exiting the batch file execution.
The closing of the console window is not good for debugging a batch file because the executed commands can't be seen and also the error message output by Windows command processor on detecting a syntax error resulting in an immediate exit of batch file processing is not readable with console window automatically closed.
For that reason debugging a batch file should be done by opening a command prompt window which results in implicit execution of cmd.exe with option /K to keep command process running and its console window open after execution of the batch file which is entered with its file name and if needed with full path in the command prompt window for execution.
Another advantage of running the batch file from within a command prompt window is the possibility to use UP and DOWN keys to re-select a once entered command line or string entered on a user prompt within batch file with set /P from input buffer of command process.
That's a for-loop with the /f option. It loops through a file or command, in this case the command dir /tc /o-d %i%\PICKS*.
Take a look at what dir /tc /o-d word* on its own does (read up on options wtih dir /?), it'll make understanding the loop easier.
Basically, the output of the command is dir (show files in directory) with the timefield showing the creation date and all files sorted by date in descending order. The command applies to the folder %i% (a variable set beforehand). "PICKS*" is the 'mask', i.e. all files starting with "PICKS" are shown. That's what the wildcard (asterisk) means (any characters can follow).
The for-loop now takes that output and assigns it to %%A (temporary iteration variable). It skips the first 4 lines (in the case of dir just header that you don't want to have), and for each following line (the files) picks out the first and the fifth word (delimited by spaces, as space is the default delimiter). %%A is now the date, %%B (the second specified token/fifth word) the second word of the filename (if there is any). Those variables can be used in the do (... part.