How to modify lines that hold a given string with new information and save it as a text file - string

I am working on modifying our batch files where we call #make functions inside. We want to add a script inside the batch file that checks an external header file, finds the line with date information(APP_VERSION_DATE) and updates the information there with new date information(I figured out how to fetch windows date information with batch, this is not an issue)
I know what steps to follow but batch syntax feels completely counter intuitive to me and I am stuck.
These are the steps I would like to follow:
1- Go through the app_version.h file line by line(for /f)
2- Find the lines with string APP_VERSION_DATE(if findstr...)
3- delete everything except APP_VERSION_DATE
4- CONCAT date information to APP_VERSION_DATE like APP_VERSION_DATE "23-05-2022"
5- Keep echoing every other line
6- Pipeline the information a new header file.
7- Delete header file
8- Rename the new header line as the old one.
set strToFind="app_version_date"
set result="Not Found"
for /f "tokens=2 delims=[]" %%A in ('findstr %strToFind% %filename%') do (
set result=%%A
if defined result (
if %result%==this is something
echo hurra this is it
) ELSE echo
)
this is where I am at right now and I am obviously still too far off to do something I want to do.
I am able to make a program that can find a given string in a file and change it but in this case I want to find the line that has the string I am searching for, delete the rest and modify it. I want to find the line where the string is and modify it, not the string itself. This is simply because date information as shown below;
#define APP_VERSION_DATE [2022-05-16 12:13]
won't be static and ever changing with each compile attempt.
I have something like this but this is too far from what I want to do.
Any help would be great! Thanks in advance

Replace date/time in header file app_version.h
There could be used the following commented batch file for this task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "HeaderFile=app_version.h"
if not exist "%HeaderFile%" exit /B 20
rem Get current local date/time in format [yyyy-MM-dd HH:mm].
for /F "tokens=1-5 delims=/: " %%G in ('%SystemRoot%\System32\robocopy.exe "%SystemDrive%\|" . /NJH') do set "AppVersionDate=[%%G-%%H-%%I %%J:%%K]" & goto UpdateHeaderFile
rem Let FINDSTR output all lines of the header file with a line number and
rem a colon at the beginning for processing really all lines including the
rem empty lines in the header file and output all lines without the line
rem number and the colon with exception of the line containing the string
rem #define APP_VERSION_DATE which is ignored and instead is output a line
rem defined here with the local date/time determined before. All lines output
rem by the loop are written into a newly created temporary header file.
:UpdateHeaderFile
(for /F delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /N "^" "%HeaderFile%" 2^>nul') do (
set "Line=%%I"
setlocal EnableDelayedExpansion
if "!Line:#define APP_VERSION_DATE=!" == "!Line!" (
echo(!Line:*:=!
) else (
echo #define APP_VERSION_DATE %AppVersionDate%
)
endlocal
))>"%HeaderFile%.tmp"
rem Replace the original header file with the temporary header file.
if exist "%HeaderFile%.tmp" move /Y "%HeaderFile%.tmp" "%HeaderFile%" >nul
rem Delete the temporary header file if the command line above failed
rem because of the original header file is read-only or write-protected
rem or currently opened by an application with shared access denied.
if exist "%HeaderFile%.tmp" del "%HeaderFile%.tmp"
endlocal
The environment variable HeaderFile can be defined with an absolute path or a relative path.
Please read the chapter Usage of ROBOCOPY to get current date/time in my answer on Time is set incorrectly after midnight for an explanation of the first for /F command line.
Please read next my answer on How to read and print contents of text file line by line? It describes in full details the second for /F loop with the small modification of an additional IF condition to replace the line containing the string #define APP_VERSION_DATE with an current date/time.
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 /?
echo /?
endlocal /?
exit /?
findstr /?
goto /?
if /?
move /?
rem /?
set /?
setlocal /?
See also single line with multiple commands using Windows batch file for an explanation of the unconditional command operator &.
Create header file current_date_time.h with date/time
The task could be done much easier if the file app_version.h contains anywhere the line:
#include "current_date_time.h"
The batch file could be in this case just:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "tokens=1-5 delims=/: " %%G in ('%SystemRoot%\System32\robocopy.exe "%SystemDrive%\|" . /NJH') do echo #define APP_VERSION_DATE [%%G-%%H-%%I %%J:%%K]>"current_date_time.h"& goto EndBatch
:EndBatch
endlocal
This batch file always creates new the file current_date_time.h with just the single line:
#define APP_VERSION_DATE [2022-05-23 18:48]
And this single preprocessor macro definition line is included on compilation into app_version.h.
Define preprocessor macro APP_VERSION_DATE with current date/time
Every C/C++ compiler has an option to define a preprocessor macro on the command line and the option can be used multiple times on the command line to define multiple preprocessor macros.
For example see:
GNU gcc/g++: Options Controlling the Preprocessor explaining case-sensitive option -D
Microsoft C/C++: /D (Preprocessor Definitions)
So it is possible to define an environment variable with the current date/time in the wanted format with the single command line below and reference this environment variable value on running the C/C++ compiler with the appropriate option.
#set "AppVersionDate=" & for /F "tokens=1-5 delims=/: " %%G in ('%SystemRoot%\System32\robocopy.exe "%SystemDrive%\|" . /NJH') do #if not defined AppVersionDate set "AppVersionDate=[%%G-%%H-%%I %%J:%%K]"
GNU gcc/g++ would be run later with -D "APP_VERSION_DATE=%AppVersionDate%" and Microsoft C/C++ compiler with /D "APP_VERSION_DATE=%AppVersionDate%" as one of the options on compilation of the C/C++ source code files.
There are also the predefined macros __DATE__ and __TIME__:
GNU gcc/g++: Predefined Macros
Microsoft C/C++: Predefined macros
Search also for information about the environment variable SOURCE_DATE_EPOCH which gives control over the timestamp added by the C/C++ compiler itself to the produced binaries.

Related

String Substitution Using Variables in a FOR loop Batch Syntax

I am trying to use string substitution to truncate a list of full file paths down to relative paths in an existing text file. In addition there is some basic automated renaming. The renaming works as intended but the string substitution I cannot get to work. All the documentation I could find describing string substitution used standalone strings, not strings stored in variables. So I do not know and cannot figure out the proper syntax. I have also read similar questions asked here and attempted those solutions to no avail.
Everything in my script works as intended except for this line:
SET %%I=%%%I:%Temp_Dir%=%
It does not change %%I at all. The input to the FOR loop %List% is a text file that looks like this:
C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out\bin\codesegment.o
C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out\bin\graphic.o
C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out\bin\helper.o
C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out\bin\main.o
C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out\bin\game.out
C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out\bin
C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out
The final output I get right now is identical to the above list.
The desired output should look like this:
\out\bin\codesegment.o
\out\bin\graphic.o
\out\bin\helper.o
\out\bin\main.o
\out\bin\game.out
\out\bin
\out
I know the syntax is supposed to be:
SET string = C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working\out\bin\codesegment.o
SET string = %string:C:\Users\UserName\AppData\Local\Temp\Batch_Renaming_Temp\Working =%
As I said though, I cannot get it to work using variables in a FOR loop. I am also attempting this method of string substitution because the path of %Temp_Dir% is always at the start of every line and is always found in each line once.
Here is the whole script for reference. I would prefer a one line solution like the one I was attempting. I am aware longer solutions are available but due to reasons beyond my control the one-line string substitution is required.
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET Proj_Dir="C:\Users\UserName\Desktop\Project"
SET Temp_Dir=%temp%\Batch_Renaming_Temp\Working
SET Counter=1
SET List="%temp%\Batch_Renaming_Temp\LFN_LIST.TMP"
SET List_Final="%temp%\Batch_Renaming_Temp\LFN_LIST.TXT"
ROBOCOPY /E /MIR %Proj_Dir% "%Temp_Dir%" > NUL
CD "%Temp_Dir%"
DIR /A-D /O:N /B /S > %List%
DIR /AD /O:N /B /S | SORT /R >> %List%
TYPE NUL > %List_Final%
FOR /F "usebackq tokens=* delims=" %%I IN (%List%) DO (
REN "%%I" !Counter!
SET /A !Counter+=1
SET %%I=%%%I:%Temp_Dir%=%
ECHO %%I >> %List_Final%
)
Like #Squashman pointed out in the comments, you cannot "set" a FOR variable.
If your variable depends on other variables indirectly, you need to use CALL SET or delayed expansion.
The easiest solution so far:
(for /F "usebackq tokens=8* delims=\" %%x in (%List%) do echo \%%y) > %List_Final%
It uses \ as a delimiter and pass the 8+th arguments to %%y, and redirects all output to %List_Final%
Tested on a Windows 10 laptop, works perfectly.

Setting a variable from a string inside a TXT file with a batch file

I'm attempting to create a batch file for my company's users to use to migrate files from their older 'personal folder' network share to our new OneDrive implementation.
The problem is, some of our users have change the default sync folder for OneDrive to whatever drive and path they wanted, instead of keeping the default (*c:\users\username*)...for example, the one I'm using to test my BAT file against is using:
D:\OneDrive - Business
However, it does still have to work when it contains more or less delimiters, like C:\OneDrive or C:\This Is Where I Foolishly Store Stuff.
At this point, I've tried using REG QUERY to output to a text file from the UserFolder key in the registry to get the path, and now I'm trying to extract this path FROM the text file in question and set it to a variable. The text file that is created looks like the following:
HKEY_CURRENT_USER\SOFTWARE\Microsoft\OneDrive\Accounts\Business1
UserFolder REG_SZ D:\OneDrive - Business
What I want to do is set the "D:\OneDrive - Business" to a variable to use in a scripted MOVE command.
My code is so awful, that I will refrain from posting it unless someone decides its necessary to answer my question.
I'm sure that for at least a DOZEN of you, this is child's play, so I would really appreciate any help that is provided me!!!
Thanks!!
Here is one batch solution for this task:
#echo off
for /F "skip=1 tokens=1,2*" %%A in ('%SystemRoot%\System32\reg.exe QUERY HKCU\SOFTWARE\Microsoft\OneDrive\Accounts\Business1 /v UserFolder 2^>nul') do if /I "%%A" == "UserFolder" if not "%%C" == "" set "UserFolder=%%C" & goto UserFolderSet
echo No user folder for OneDrive found in Windows registry.
goto :EOF
:UserFolderSet
echo Found user folder: "%UserFolder%"
Better readable is this version doing exactly the same:
#echo off
for /F "skip=1 tokens=1,2*" %%A in ('%SystemRoot%\System32\reg.exe QUERY HKCU\SOFTWARE\Microsoft\OneDrive\Accounts\Business1 /v UserFolder 2^>nul') do (
if /I "%%A" == "UserFolder" (
if not "%%C" == "" (
set "UserFolder=%%C"
goto UserFolderSet
)
)
)
echo No user folder for OneDrive found in Windows registry.
goto :EOF
:UserFolderSet
echo Found user folder: "%UserFolder%"
The command FOR executes the command REG in a background command process with capturing its output written to handle STDOUT.
An error message output by REG to handle STDERR is suppressed by redirecting it to device NUL because of 2^>nul. The redirection operator > is escaped with caret character ^ which is necessary to interpret > first as literal character by Windows command interpreter on parsing the FOR command line. But later on execution of REG command line with 2>nul by FOR > is interpreted as redirection operator. An error message could occur if the registry value does not exist at all in Windows registry.
FOR processes each non empty line of captured output of REG command by splitting the line up into substrings (tokens) using space and tab as delimiter (default).
The option skip=1 instructs FOR to skip the first line of captured output.
The option tokens=1,2* instructs FOR that first space/tab delimited string being here value name UserFolder should be assigned to first loop variable A.
The second space/tab delimited string being here type REG_SZ should be assigned to loop variable B being the next character in ASCII table. Now it should be clear why loop variables are case-sensitive while environment variables are not case-sensitive. This loop variable is not further processed here although it might be good in case of type is REG_EXPAND_SZ instead of REG_SZ as in this case the directory path contains most likely also 1 or more environment variable references which must be expanded before having real directory path.
The string after the spaces/tabs after second space/tab delimited string being in your example D:\OneDrive - Business should be assigned without further splitting up on spaces/tabs to loop variable C because of * after 2 in options string tokens=1,2*.
The first IF condition makes sure the right line is processed as on Windows XP the output of REG starts with a header where only the first line would be skipped.
The second IF condition makes sure the user folder value has a non empty value.
The string of interest is finally assigned to environment variable UserFolder and the FOR loop is exited with a jump to the commands below label UserFolderSet.
The commands below the FOR loop are executed if the registry value was not found in Windows registry.
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.
echo /?
for /?
goto /?
if /?
set /?
Read also the Microsoft article about Using Command Redirection Operators.
Perhaps:
For /F "EOL=H Tokens=2*" %%A In ('Reg Query "HKCU\SOFTWARE\Microsoft\OneDrive\Accounts\Business1" /V "UserFolder"') Do Set "var=%%B"
Echo(%%var%% = %var%
Pause
I won't use a text file but directly Reg.exe to read the registry value
#Echo off
Set "Key=HKCU\SOFTWARE\Microsoft\OneDrive\Accounts\Business1"
Set "Val=UserFolder"
For /f "Tokens=2* delims= " %%A in (
'Reg query "%Key%" /V %Val% ^|find /i "%Val%" '
) Do Set "%Val%=%%B"
Set %Val%
You can combine the output of the [MSDN]: reg with [MSDN]: findstr (to filter out some useless data), and iterate over what's left using [SS64]: for.
Here's the code (it must be run from a batch file):
#echo off
set _KEY_NAME=HKEY_CURRENT_USER\SOFTWARE\Microsoft\OneDrive\Accounts\Business1
set _VALUE_NAME=UserFolder
set _VALUE_DATA=
for /f "tokens=1,2,*" %%f in ('reg query %_KEY_NAME% /v %_VALUE_NAME% /t REG_SZ 2^>NUL ^| findstr %_VALUE_NAME%') do (
set _VALUE_DATA="%%h"
)
echo Data: %_VALUE_DATA%
This is based on the fact that on my machine (Win10), reg query %_KEY_NAME% /v %_VALUE_NAME% /t REG_SZ (with different values for the _KEY_NAME and _VALUE_NAME), output:
HKEY_CURRENT_USER\SOFTWARE\_DummyKey
_DummyValue REG_SZ D:\OneDrive - Business
End of search: 1 match(es) found.
#EDIT0: After looking at #Mofi's solution, I realized that I had no error handling. Added some.

Batch Move based on filename without Delimiters, String Only

Okay, Windows 7 Enterprise x64 here with a Windows batch file question. I am a somewhat basic user with little knowledge of batch file creation. I've modified existing scripts I've found online to my use, that is about it.
I have a piece of software that dumps it's output to a folder with file names concatenated from the project title, current system date, current system time, and project settings.
It will export two files in this format every time the project is saved:
PROJECTTITLE_2016_10_07__09_45_11__A_B_C.iges
PROJECTTITLE_2016_10_07__09_45_11__A_B_C.step
The A, B, and C representing switches used in the software that are specific to the project. These may exist or may not exist as in the file name can be _R_F or _R_F_Z etc.
"PROJECTTITLE" can literally be anything. This is where my problem arises using delimiters. You could potentially have file names like all of the following (using real examples from users):
11475shacklebody_2016_10_07__09_45_11__R_F.iges
11475shacklebody_2016_10_07__09_45_11__R_F.step
test_EFMflow_2016_10_07__09_45_11__R_Z.iges
test_EFMflow_2016_10_07__09_45_11__R_Z.step
untitled16_2016_10_07__09_45_11__R.iges
untitled16_2016_10_07__09_45_11__R.step
#14drop_wire-edm_2016_10_07__09_45_11__R_F_Z.iges
#14drop_wire-edm_2016_10_07__09_45_11__R_F_Z.step
prooftestwithoutupperlandsimproved-4_2016_10_07__09_45_11.iges
prooftestwithoutupperlandsimproved-4_2016_10_07__09_45_11.step
What I want to do is move both the IGES and STEP files from the output folder on the C drive to a new folder on a networked drive with name based on "PROJECTTITLE," creating that folder if needed. I have already task scheduled a -delim based batch to run every night, but found delimiters to not be sufficient for my use.
While it seems like an easy job to do with delimiters, looking for the first underscore, I have had projects with underscores in their names that screws up the folder naming. Since delimiters only work for individual characters, not strings, I have been looking for examples of batch files I can adapt to my use with no luck finding any similar to what I want to do that do not use delimiters.
It seems simple, search for string "_2016," in filename, and take all characters before "_2016" and create a new directory, placing files containing those characters in that folder. I am lost as to how to do this without delimiters though.
What I have working so far, using delimiters looking for underscores, creating folders and moving to those folders on a mapped drive:
for /f "delims=_" %%V in ('dir /b /a-d C:\Output\*_*.iges') do (
mkdir "I:\ENG\PARTS\%%V" 2>nul
move "C:\Output\%%V_*.iges" "I:\ENG\PARTS\%%V" >NUL 2>nul
move "C:\Output\%%V_*.step" "I:\ENG\PARTS\%%V" >NUL 2>nul
)
It is a very simple batch file for what it does, but falls flat on it's face with titles containing underscores.
A few things:
Searching for _2016 would mean the batch file would need yearly maintenance. Could we either search for system %YEAR% or somehow search for "_####_##_##_" with something like a regular expression to get away from searching for system date or a specific year?
EDIT:
While not ideal as the switches are useful, I found a way in the software to not export the project specific switches at the end of the file name. So now the export would be:
PROJECTTITLE_2016_10_07__09_45_11.iges
PROJECTTITLE_2016_10_07__09_45_11.step
Which I know I can trim from right, what, 21 characters, use that name to make a folder and search the directory for all files containing that trimmed string.
It still would be nice to figure out how to search for the specific string though with the switches intact. I am willing to learn, so throw all explanations of your code you can at me.
Double (Late) EDIT:
First, thank you all for answering this, I deeply appreciate your help.
Double underscores in the the filename are quite rare, but do happen, probably due to typos. It becomes more of an issue with the volume of files I'm planning to move with this. The software runs on maybe 100 user machines with 20 or so being heavy users, each saving up to maybe 1000 files a day. The software will automatically save in increments, as the user runs the simulation. I'm trying to give them a way to collaborate and view each other's simulation results over the network (that we actively encourage them to use) and it figures that the software is hard coded to output to the C drive (for "performance" as the vendor tells me). I'm looking into scheduling the files to move every 15 minutes on every PC in their OU instead of nightly to give a near-real-time view on what they are working on.
Another option I explored was symbolically linking the output folder on each machine to the software's folder network drive, but found that this really doesn't solve the user's "organization" issue, putting like project runs in the same folder. This also has the issue of not allowing them to save a project out of the office without VPN, which some users do do, then move their files over when they get back in the office.
Thank you for your assistance.
Next batch script should do the job even if a file name contains cmd poisonous characters like space or % percent sign etc.
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "_fouts=C:\Output" your setting
set "_fouts=D:\test\39924063" my setting
for /F %%G in ('wmic OS get localdatetime ^|find "."') do set "_fyear=%%G"
pushd "%_fouts%"
set "_fyear=%_fyear:~0,4%"
call :doDir
set /A _fyear -= 1
call :doDir
popd
ENDLOCAL
goto :eof
:doDir
rem debugging output echo(%_fyear%
for /f "delims=" %%V in ('
dir /b /a-d "*_%_fyear%_*.iges" "*_%_fyear%_*.step" 2^>NUL') do (
set "_fname=%%~nV" filename without extesion
set "_fexte=%%~xV" extesion only
call :doAll
)
goto :eof
:doAll
call set "_ftail=%%_fname:*_%_fyear%_=%%"
call set "_fproj=%%_fname:_%_fyear%_%_ftail%=%%"
rem debugging output echo("%_fproj%" "%_fname%" "%_fexte%"
ECHO mkdir "I:\ENG\PARTS\%_fproj%" 2>nul
ECHO move "%_fname%%_fexte%" "I:\ENG\PARTS\%_fproj%\"
goto :eof
Output (note that operational mkdir and move commands are merely displayed for debugging purposes using ECHO mkdir and ECHO move, respectively):
==> dir /B /S "D:\test\39924063" /S
D:\test\39924063\#14drop_wire-edm_2016_10_07__09_45_11__R_F_Z.iges
D:\test\39924063\#14drop_wire-edm_2016_10_07__09_45_11__R_F_Z.step
D:\test\39924063\%PROJECT TITLE_2016_10_07__09_45_11__A_B_C.iges
D:\test\39924063\%PROJECT TITLE_2016_10_07__09_45_11__A_B_C.step
==> D:\bat\SO\39924063.bat
mkdir "I:\ENG\PARTS\#14drop_wire-edm"
move "#14drop_wire-edm_2016_10_07__09_45_11__R_F_Z.iges" "I:\ENG\PARTS\#14drop_wire-edm\"
mkdir "I:\ENG\PARTS\%PROJECT TITLE"
move "%PROJECT TITLE_2016_10_07__09_45_11__A_B_C.iges" "I:\ENG\PARTS\%PROJECT TITLE\"
mkdir "I:\ENG\PARTS\#14drop_wire-edm"
move "#14drop_wire-edm_2016_10_07__09_45_11__R_F_Z.step" "I:\ENG\PARTS\#14drop_wire-edm\"
mkdir "I:\ENG\PARTS\%PROJECT TITLE"
move "%PROJECT TITLE_2016_10_07__09_45_11__A_B_C.step" "I:\ENG\PARTS\%PROJECT TITLE\"
==>
Resources (required reading, incomplete):
(command reference) An A-Z Index of the Windows CMD command line
(additional particularities) Windows CMD Shell Command Line Syntax
(%~nV, %~xV etc. special page) Command Line arguments (Parameters)
(set "_fyear=%_fyear:~0,4%" etc.) Extract part of a variable (substring)
(%variable:StrToFind=NewStr% etc.) Variable Edit/Replace
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
REM (
FOR /f "delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*__*" '
) DO (
IF /i "%%~xa"==".iges" CALL :process "%%a"
IF /i "%%~xa"==".step" CALL :process "%%a"
)
REM )>"%outfile%"
GOTO :EOF
:: Process filename "%1"
:process
SET "fullname=%~1"
SET "junk=%fullname:*__=%"
CALL SET "project=%%fullname:%junk%=%%"
SET "project=%project:~0,-13%
ECHO(MD "%destdir%\%project%"
ECHO(MOVE "%sourcedir%\%~1" "%destdir%\%project%\"
GOTO :eof
You would need to change the settings of sourcedir and destdir to suit your circumstances.
The required MD commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MD to MD to actually create the directories. Append 2>nul to suppress error messages (eg. when the directory already exists)
The required MOVE commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MOVE to MOVE to actually move the files. Append >nul to suppress report messages (eg. 1 file moved)
This approach simply performs a directory list without directorynames (/a-d) of each file in the source directory that contains a double-underscore. Each filename matching is assigned to %%a and if the extension part (%%~xa) is one of the targets, then process the filename.
Processing consists of removing all of the characters before the double-underscore and then removing that junk part from the full name, giving project+date. Remove the last 13 characters and you have your project name.
Will have problems with any filename containing certain symbols like % or = but should be fine with underscores.
This version assumes there will not be an instance of _YYYY_ in the PROJECTTITLE.
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
(Set OutDir=I:\ENG\PARTS)
(Set SrcDir=C:\Output)
If Not Exist "%SrcDir%\" Exit/B 1
If Not Exist "%OutDir%\" Exit/B 1
For /F "EOL=Y" %%A In ('WMIC Path Win32_LocalTime Get Year') Do (
For /F "Delims=" %%B In ("%%A") Do Set "ThisYr=%%B")
For /F "Delims=" %%A In ('Where "%SrcDir%:*_%ThisYr%_*.iges"') Do (
If Exist "%%~dpnA.step" Call :Sub %%~nA)
Exit/B
:Sub
Set "BigTit=%~1"
For /F "Delims=" %%A In ('CMD /Q /C "Call Echo %%BigTit:_%ThisYr%_=&:%%"') Do (
If Not Exist "%OutDir%\%%A\" MD "%OutDir%\%%A"
Move "%SrcDir%\%~1*.*" "%OutDir%\%%A">Nul)
Edit, there is no need to export without the potentially useful project switches using the above code.
Supposing the PROJECTTITLE part of the file names does not contain two consecutive underscores, you could use the following script, which splits off the first occurrence of __ and everything after (so the time part and the optional switches are removed), using a standard for loop rather than for /F; then it splits off another 11 characters (hence the remaining date part), using sub-string expansion:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=."
set "_TARGET=."
for /F delims^=^ eol^= %%F in ('
pushd "%_SOURCE%" ^&^& ^(
dir /B /A:-D ^
"*_????_??_??__??_??_??*.iges" ^
"*_????_??_??__??_??_??*.step" ^
^& popd^)
') do (
call :PROCESS "%%F"
)
endlocal
exit /B
:PROCESS val_file
setlocal DisableDelayedExpansion
set "FILE=%~1"
setlocal EnableDelayedExpansion
for %%I in ("!FILE:__=";"!") do (
endlocal
set "ITEM=%%~I"
setlocal EnableDelayedExpansion
goto :NEXT
)
:NEXT
if defined ITEM (
set "ITEM=!ITEM:~,-11!"
md "%_TARGET%\!ITEM!" 2> nul
if not exist "%_TARGET%\!ITEM!\!FILE!" (
move /Y "%_SOURCE%\!FILE!" "%_TARGET%\!ITEM!\" > nul
)
)
endlocal
endlocal
exit /B
Finally I come up with a script that is able to handle even files whose PROJECTTITLE part contain __ on their own. It splits off the last occurrence of __ and everything after (so the optional switches are removed, if any, or the time part is removed otherwise), using a standard for loop rather than for /F; then it splits off another 21 characters (hence the remaining date and time parts), or, if no optional switches were present, just another 11 characters (hence the remaining date part), using sub-string expansion:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=."
set "_TARGET=."
for /F delims^=^ eol^= %%F in ('
pushd "%_SOURCE%" ^&^& ^(
dir /B /A:-D ^
"*_????_??_??__??_??_??*.iges" ^
"*_????_??_??__??_??_??*.step" ^
^& popd^)
') do (
call :PROCESS "%%F"
)
endlocal
exit /B
:PROCESS val_file
setlocal DisableDelayedExpansion
set "FILE=%~1"
set "BASE=%~n1"
set "NAME=" & set "ITEM="
setlocal EnableDelayedExpansion
for %%I in ("!BASE:__=";"!") do (
for /F "delims=" %%E in ("!NAME!!ITEM!__") do (
endlocal
set "NAME=%%E"
set "ITEM=%%~I"
setlocal EnableDelayedExpansion
)
)
if defined NAME (
set "NAME=!NAME:~2,-2!"
echo("!ITEM!"| > nul findstr /R "^\"[0-9][0-9]_[0-9][0-9]_[0-9][0-9]\"$" || (
if defined NAME set "NAME=!NAME:~,-10!"
)
if defined NAME set "NAME=!NAME:~,-11!"
if defined NAME (
md "%_TARGET%\!NAME!" 2> nul
if not exist "%_TARGET%\!NAME!\!FILE!" (
move /Y "%_SOURCE%\!FILE!" "%_TARGET%\!NAME!\" > nul
)
)
)
endlocal
endlocal
exit /B

How to run batch script without using *.bat extension

Is there any method in Windows through which we can execute a batch script without *.bat extension?
This is an interesting topic to me! I want to do some observations about it.
The important point first: A Batch file is a file with .BAT or .CMD extension. Period. Batch files can achieve, besides the execution of usual DOS commands, certain specific Batch-file facilities, in particular:
Access to Batch file parameters via %1 %2 ... and execution of SHIFT command.
Execution of GOTO command.
Execution of CALL :NAME command (internal subroutine).
Execution of SETLOCAL/ENDLOCAL commands.
Now the funny part: Any file can be redirected as input for CMD.exe so the DOS commands contained in it are executed in a similar way of a Batch file, with some differences. The most important one is that previous Batch-file facilities will NOT work. Another differences are illustrated in the NOT-Batch file below (I called it BATCH.TXT):
#echo off
rem Echo off just suppress echoing of the prompt and each loop of FOR command
rem but it does NOT suppress the listing of these commands!
rem Pause command does NOT pause, because it takes the character that follows it
pause
X
rem This behavior allows to put data for a SET /P command after it
set /P var=Enter data:
This is the data for previous command!
echo Data read: "%var%"
rem Complex FOR/IF commands may be assembled and they execute in the usual way:
for /L %i in (1,1,5) do (
set /P line=
if "!line:~0,6!" equ "SHOW: " echo Line read: !line:~6!
)
NOSHOW: First line read
SHOW: Second line
NOSHOW: This is third line
SHOW: The line number 4
NOSHOW: Final line, number five
rem You may suppress the tracing of the execution redirecting CMD output to NUL
rem In this case, redirect output to STDERR to display messages in the screen
echo This is a message redirected to STDERR >&2
rem GOTO command doesn't work:
goto label
goto :EOF
rem but both EXIT and EXIT /B commands works:
exit /B
:label
echo Never reach this point...
To execute previous file, type: CMD /V:ON < BATCH.TXT
The /V switch is needed to enable delayed expansion.
More specialized differences are related to the fact that commands in the NOT-Batch file are executed in the command-line context, NOT the Batch-file context. Perhaps Dave or jeb could elaborate on this point.
EDIT: Additional observations (batch2.txt):
#echo off
rem You may force SET /P command to read the line from keyboard instead of
rem from following lines by redirecting its input to CON device.
rem You may also use CON device to force commands output to console (screen),
rem this is easier to write and read than >&2
echo Standard input/output operations> CON
echo/> CON
< CON set /P var=Enter value: > CON
echo/> CON
echo The value read is: "%var%"> CON
Execute previous file this way: CMD < BATCH2.TXT > NUL
EDIT: More additional observations (batch3.txt)
#echo off
rem Dynamic access to variables that usually requires DelayedExpansion via "call" trick
rem Read the next four lines; "next" means placed after the FOR command
rem (this may be used to simulate a Unix "here doc")
for /L %i in (1,1,4) do (
set /P line[%i]=
)
Line one of immediate data
This is second line
The third one
And the fourth and last one...
(
echo Show the elements of the array read:
echo/
for /L %i in (1,1,4) do call echo Line %i- %line[%i]%
) > CON
Execute this file in the usual way: CMD < BATCH3.TXT > NUL
Interesting! Isn't it?
EDIT: Now, GOTO and CALL commands may be simulated in the NotBatch.txt file!!! See this post.
Antonio
Just use:
type mybat.txt | cmd
Breaking it down...
type mybat.txt reads mybat.txt as a text file and prints the contents. The | says capture anything getting printed by the command on its left and pass it as an input to the command on its right. Then cmd (as you can probably guess) interprets any input it receives as commands and executes them.
In case you were wondering... you can replace cmd with bash to run on Linux.
in my case, to make windows run files without extension (only for *.cmd, *.exe) observed, i have missed pathext variable (in system varailbles) to include .cmd. Once added i have no more to run file.cmd than simply file.
environment variables --> add/edit system variable to include .cmd;.exe (ofcourse your file should be in path)
It could be possible yes, but probably nor in an easy way =) cause first of all.. security.
I try to do the same thing some year ago, and some month ago, but i found no solution about it.. you could try to do
execu.cmd
type toLaunch.txt >> bin.cmd
call bin.cmd
pause > nul
exit
then in toLaunch.txt put
#echo off
echo Hello!
pause > nul
exit
just as example, it will "compile" the code, then it will execute the "output" file, that is just "parse"
instead of parsed you could also just rename use and maybe put an auto rename inside the script using inside toLaunch.txt
ren %0 %0.txt
hope it helped!
It is possible at some degree. You'll need an admin permissions to run assoc and ftype commands. Also a 'caller' script that will use your code:
Lets say the extension you want is called .scr.
Then execute this script as admin:
#echo off
:: requires Admin permissions
:: allows a files with .scr (in this case ) extension to act like .bat/.cmd files.
:: Will create a 'caller.bat' associated with the extension
:: which will create a temp .bat file on each call (you can consider this as cheating)
:: and will call it.
:: Have on mind that the %0 argument will be lost.
rem :: "installing" a caller.
if not exist "c:\scrCaller.bat" (
echo #echo off
echo copy "%%~nx1" "%%temp%%\%%~nx1.bat" /Y ^>nul
echo "%%temp%%\%%~nx1.bat" %%*
) > c:\scrCaller.bat
rem :: associating file extension
assoc .scr=scrfile
ftype scrfile=c:\scrCaller "%%1" %%*
You even will be able to use GOTO and CALL and the other tricks you know. The only limitation is that the the %0 argument will be lost ,tough it can be hardcoded while creating the temp file.
As a lot of languages compile an .exe file for example I think this a legit approach.
If you want variables to be exported to the calling batch file, you could use
for /F "tokens=*" %%g in (file.txt) do (%%g)
This metod has several limitations (don't use :: for comments), but its perfect for configuration files.
Example:
rem Filename: "foo.conf"
rem
set option1=true
set option2=false
set option3=true
#echo off
for /F "tokens=*" %%g in (foo.conf) do (%%g)
echo %option1%
echo %option2%
echo %option3%
pause

Escaping an equals sign in DOS batch string replacement command

I need to replace some text in a JNLP file using a DOS batch file to tune it for the local machine.
The problem is that the search pattern contains an equals sign which is messing up the string replacement in the batch file.
I want to replace the line,
<j2se version="1.5" initial-heap-size="100M" max-heap-size="100M"/>
with specific settings for the initial and max heap sizes.
For example at the moment I have,
for /f "tokens=* delims=" %%a in (%filePath%agility.jnlp) do (
set str=%%a
set str=!str:initial-heap-size="100M"=initial-heap-size="%min%M"!
echo !str!>>%filePath%new.jnlp)
but the = in the search pattern is being read as part of the replacement command.
How do I escape the equals sign so it is processed as text?
The best solution is to download and install Cygwin or GNUWin32 but, if you're really limited to the standard command processor, it can get a little messy.
This is not the fastest method in the world but it's at least functional. This command file processes each line one character at a time, treating specially the case where you find the stanza you're looking for.
#echo off
set init=50M
set max=75M
setlocal enableextensions enabledelayedexpansion
for /f "tokens=* delims=" %%a in (agility.jnlp) do (
set str1=%%a
call :morph
echo !str2!>>agility_new.jnlp
echo !str2!
)
endlocal
goto :eof
:morph
set str2=
:morph1
if not "x!str1!"=="x" (
if "!str1:~0,18!"=="initial-heap-size=" (
set str2=!str2!initial-heap-size="!init!"
set str1=!str1:~24!
goto :morph1
)
if "!str1:~0,14!"=="max-heap-size=" (
set str2=!str2!max-heap-size="!max!"
set str1=!str1:~20!
goto :morph1
)
set str2=!str2!!str1:~0,1!
set str1=!str1:~1!
goto :morph1
)
goto :eof
With the input file:
<j2se version="1.5" initial-heap-size="100M" max-heap-size="100M"/>
next line
===
you end up with:
<j2se version="1.5" initial-heap-size="50M" max-heap-size="75M"/>
next line
===
One cannot simply replace (a substring with) an equal-sign, without splitting up (for-statement with "delims==") or trimming…
But perhaps you could go for this simpler but more confusing approach, using the following statement in your for-loop:
set str=!str:"100M" max-heap-size="%min%M" max-heap-size!
It just combines the string to replace with what comes after instead of what comes before, avoiding any equal-sign replacements entirely.
If you can pass the arguments as something else, such as double underscores, you can iterate through them and convert them to '=' in the batch file.
#rem Replace __ with = in batch files.
#rem This works around the lack of equals signs in args
#rem args contains full args string with substitutions in place
setlocal enabledelayedexpansion
:argloop
if "%~1" NEQ "" (
set str=%~1
set out=!str:__==!
set %~1=!out!
set args=!args!!out!
SHIFT
goto :argloop
)
#rem Can now run program on a line on its own with just %args%
Source: https://github.com/mlabbe/batchargs
Here's an alternative solution. If you can afford to download GNU tools, you can use sed:
C:\test>set a=200
C:\test>sed -i.bak "s/^\(.*initial-heap-size=\"\).*\( max.*\)/\1%a%\"\2/" file

Resources