I'm asking user to provide filename in Windows Batch, by using this command:
set /p my_filename=Enter filename for this project
How can I verify that %my_filename% contains valid Windows filename (not path) and re ask user if it does not?
EDIT
The file in question does not yet exists when running the script (it will be created by script)
#echo off
setlocal enableextensions disabledelayedexpansion
:askFile
rem Retrieve filename. On empty input ask again
set /p "my_file=Enter filename for this project: " || goto :askFile
rem Use delayed expansion to avoid problems with special characters
setlocal enabledelayedexpansion
rem Disable delimiters and end of line characters in for command
for /f delims^=^ eol^= %%a in ("!my_file!") do (
rem Cancel delayed expansion to avoid ! removal during expansion
endlocal
rem Until checked, we don't have a valid file
set "my_file="
rem Check we don't have a path, it is not a folder and the file exists
if /i "%%~a"=="%%~nxa" if not exist "%%~a\" if exist "%%~a" set "my_file=%%~nxa"
)
rem If we don't have a file name (it was not valid) ask again
if not defined my_file goto :askFile
echo Selected file is "%my_file%"
edited to adapt to comments
#echo off
setlocal enableextensions disabledelayedexpansion
:askFile
rem Retrieve filename. On empty input ask again
set /p "my_file=Enter filename for this project: " || goto :askFile
rem See Naming Files, Paths, and Namespaces
rem https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
rem NOTE: From here, we will be enabling/disabling delayed expansion
rem to avoid problems with special characters
setlocal enabledelayedexpansion
rem Ensure we do not have restricted characters in file name trying to use them as
rem delimiters and requesting the second token in the line
for /f tokens^=2^ delims^=^<^>^:^"^/^\^|^?^*^ eol^= %%y in ("[!my_file!]") do (
rem If we are here there is a second token, so, there is a special character
echo Error : Non allowed characters in file name
endlocal & goto :askFile
)
rem Check MAX_PATH (260) limitation
set "my_temp_file=!cd!\!my_file!" & if not "!my_temp_file:~260!"=="" (
echo Error : file name too long
endlocal & goto :askFile
)
rem Check path inclusion, file name correction
for /f delims^=^ eol^= %%a in ("!my_file!") do (
rem Cancel delayed expansion to avoid ! removal during expansion
endlocal
rem Until checked, we don't have a valid file
set "my_file="
rem Check we don't have a path
if /i not "%%~a"=="%%~nxa" (
echo Error : Paths are not allowed
goto :askFile
)
rem Check it is not a folder
if exist "%%~nxa\" (
echo Error : Folder with same name present
goto :askFile
)
rem ASCII 0-31 check. Check file name can be created
2>nul ( >>"%%~nxa" type nul ) || (
echo Error : File name is not valid for this file system
goto :askFile
)
rem Ensure it was not a special file name by trying to delete the newly created file
2>nul ( del /q /f /a "%%~nxa" ) || (
echo Error : Reserved file name used
goto :askFile
)
rem Everything was OK - We have a file name
set "my_file=%%~nxa"
)
echo Selected file is "%my_file%"
goto :eof
You could strip posible path with a for
Check with findstr Regular Expression for only allowed chars
Another findstr to exclude possible device names
:: Q:\Test\2017\08\20\SO_45780452.cmd
#Echo off
:loop
set "my_filename="
set /p "my_filename=Enter filename for this project: "
set "test="
for %%A in ("%my_filename%") Do Set "test=%%~nxA"
If "%test%" neq "%my_filename%" (
Echo no drive/path please
Goto :loop
)
echo:%my_filename%|findstr /i "^[0-9A-Z.-_]*$" >Nul 2>&1 ||(
Echo invalid chars
goto :loop
)
:: check possible device name and reject
echo:%my_filename%|findstr /i "^aux$ ^con$ ^com[0-9]*$ ^lpt[0-9]*$ ^nul$ ^prn$" >Nul 2>&1 && (
Echo invalid device name
goto :loop
)
:: my_filename should be a tolerable name
Very elementary filename validity control:
echo %my_filename%| findstr /R /C:"*\.*"
There could be glitches with reserved DOS-times names (CON, PRN, NUL, etc..) and/or some special characters, so maybe you could simply try to store something into file under provided name, and than check if file was created successfully. Something like:
:getfilename
set /p my_filename=Enter filename for this project
echo A>%my_filename%
if not exist %my_filename% (echo Wrong filename & goto getfilename) else (del %my_filename% >NUL)
ALL UNTESTED!! Check if working directory is empty first.
Related
The following creates a numbered list of links from a remote directory like
1 Link!1
2 Link!2
3 Link!3
4 Link!4
5 Link!5
.
#echo off
setlocal ENABLEDELAYEDEXPANSION
megals --reload /Root/
set /p var1="enter folder name: " & megals /Root/var1
set /a c=0
FOR /F "tokens=1 usebackq" %%i in (`megals -n -e /Root/%%var1%%`) do (
set /a c=c+1
echo !c! %%i
set string[!c!]=%%i
)
set /P number=Enter number:
echo !string[%number%]!
pause
First Problem: All the links contain a ! character which gets removed by delayedexpansion, rendering the link useless. The links require the ! as it is part of the link.
Second Problem: I'm trying to integrate this into a program, and I can't use findstr because it will list the link and filename on the same line, and when the filenames contain parentheses the program crashes. So I have to use usebackq because it lets me get just the link, without needing to deal with the filenames.
Findstr will list Link!1 Filename (the whole line)
Usebackq lets me just get Link!1
I can't use Findstr because when filenames contain parentheses the program will crash, which can only be solved by delayedexpansion.
This is a follow-up post from here, which I got stuck on: (Shows the Program)
https://stackoverflow.com/questions/49564553/create-a-numbered-list-based-on-a-given-list-of-strings#=
You can see the findstr method there, and how it causes crashes when filenames contain parentheses, which can be fixed with delayedexpansion, but that removes the ! character which is essential as it is part of the link.
Edit: Seems to be working now, thanks
Working Code
#echo off
:start:
megals --reload /Root/
set /p var1="dir? " & megals /Root/%%var1%%
for /f "tokens=1,* delims=:" %%A in ('megals -n /Root/%%var1%% ^|findstr
/n "." ') do (
set Link[%%A]=%%B
Echo %%A %%B
)
setlocal DisABLEDELAYEDEXPANSION
set /a c=0
FOR /F "tokens=1 usebackq" %%i in (`megals -n -e /Root/%%var1%%`) do (
set /a c+=1
call set "string[%%c%%]=%%i"
)
set /P number="Enter number: "
FOR /F "tokens=*" %%g IN ('call echo %%string[%number%]%%') do (SET VAR2=%%g)
echo %Var2%
echo.
Megadl %VAR2% & echo. && goto :start:
pause
https://megatools.megous.com/man/megals.html#_megatools
You really should double quote your whole set commands.
Using the alternate delayed expansion type with a call:
#echo off
setlocal DisABLEDELAYEDEXPANSION
megals --reload /Root/
set /p var1="enter folder name: " & megals /Root/var1
set /a c=0
FOR /F "tokens=1 usebackq" %%i in (`megals -n -e /Root/%%var1%%`) do (
set /a c+=1
call echo %%c%% %%i
call set "string[%%c%%]=%%i"
)
set /P number=Enter number:
call echo %%string[%number%]%%
pause
The simple solution is not using delayed environment variable expansion, for example by using command CALL.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
megals.exe --reload /Root/
rem Prompt user for folder name and verify that the user has really entered
rem a folder name and remove double quotes to prevent an exit of batch file
rem execution on further processing because of an invalid command line syntax.
:EnterFolder
set "FolderName="
set /P "FolderName=Enter folder name: "
rem Has the user entered anything at all?
if not defined FolderName goto EnterFolder
rem Remove all double quotes from folder name string?
set "FolderName=%FolderName:"=%"
rem Is anything left from folder name after removing double quotes?
if not defined FolderName goto EnterFolder
megals.exe "/Root/%FolderName%"
rem Get first space/tab separated string of each line output by megals
rem assigned to an environment variable which form an array of strings.
rem Redefine end of line character from semicolon to vertical bar as it
rem is impossible that a line starts with a vertical bar. Command CALL
rem is used to double process the SET command line by Windows command
rem processor which is the alternate solution for delayed expansion.
echo/
set "Count=0"
for /F "eol=|" %%I in ('megals.exe -n -e "/Root/%FolderName%"') do (
set /A Count+=1
call echo %%Count%% %%~I
call set "string[%%Count%%]=%%~I"
)
if %Count% == 0 echo There is nothing! & goto EndBatch
echo/
rem Prompt user for number name and verify that the user has really entered
rem a decimal interpreted number which must be in range of 1-%Count%.
:EnterNumber
set "Number="
set /P "Number=Enter number (1-%Count%): "
rem Has the user entered anything at all?
if not defined Number goto EnterNumber
rem Remove all double quotes from number string?
set "Number=%Number:"=%"
rem Is anything left from number string?
if not defined Number goto EnterNumber
rem Contains the number string any non digit character?
for /F "delims=0123456789" %%I in ("%Number%") do goto EnterNumber
rem Remove all leading zeros from number string to avoid interpreting
rem the entered number as octal number on the two IF comparisons below.
:LeadingZeros
if not %Number:~0,1% == 0 goto CheckNumber
set "Number=%Number:~1%"
if defined Number goto LeadingZeros
rem The number was 0 which is less than 1.
goto EnterNumber
rem Check number in range of 1 to %Count% which requires to convert the
rem number strings on both sides of the comparison operators to signed
rem 32-bit integers by Windows command processor in background.
:CheckNumber
if %Number% GTR %Count% goto EnterNumber
if %Number% LSS 1 goto EnterNumber
rem Output the string according to entered number by forcing Windows command
rem processor again to double processing the command line because of CALL.
call echo %%string[%number%]%%
rem Restore previous environment with discarding all the
rem environment variables defined by this batch file.
:EndBatch
endlocal
pause
Another solution is using a subroutine like OutputAndSet.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
megals.exe --reload /Root/
rem Prompt user for folder name and verify that the user has really entered
rem a folder name and remove double quotes to prevent an exit of batch file
rem execution on further processing because of an invalid command line syntax.
:EnterFolder
set "FolderName="
set /P "FolderName=Enter folder name: "
rem Has the user entered anything at all?
if not defined FolderName goto EnterFolder
rem Remove all double quotes from folder name string?
set "FolderName=%FolderName:"=%"
rem Is anything left from folder name after removing double quotes?
if not defined FolderName goto EnterFolder
megals.exe "/Root/%FolderName%"
rem Get first space/tab separated string of each line output by megals
rem assigned to an environment variable which form an array of strings.
rem Redefine end of line character from semicolon to vertical bar as it
rem is impossible that a line starts with a vertical bar. Command CALL
rem is used to double process the SET command line by Windows command
rem processor which is the alternate solution for delayed expansion.
echo/
set "Count=0"
for /F "eol=|" %%I in ('megals.exe -n -e "/Root/%FolderName%"') do call :OutputAndSet "%%~I"
if %Count% == 0 echo There is nothing! & goto EndBatch
echo/
rem Prompt user for number name and verify that the user has really entered
rem a decimal interpreted number which must be in range of 1-%Count%.
:EnterNumber
set "Number="
set /P "Number=Enter number (1-%Count%): "
rem Has the user entered anything at all?
if not defined Number goto EnterNumber
rem Remove all double quotes from number string?
set "Number=%Number:"=%"
rem Is anything left from number string?
if not defined Number goto EnterNumber
rem Contains the number string any non digit character?
for /F "delims=0123456789" %%I in ("%Number%") do goto EnterNumber
rem Remove all leading zeros from number string to avoid interpreting
rem the entered number as octal number on the two IF comparisons below.
:LeadingZeros
if not %Number:~0,1% == 0 goto CheckNumber
set "Number=%Number:~1%"
if defined Number goto LeadingZeros
rem The number was 0 which is less than 1.
goto EnterNumber
:OutputAndSet
set /A Count+=1
echo %Count% %~1
set "string[%Count%]=%~1"
goto :EOF
rem Check number in range of 1 to %Count% which requires to convert the
rem number strings on both sides of the comparison operators to signed
rem 32-bit integers by Windows command processor in background.
:CheckNumber
if %Number% GTR %Count% goto EnterNumber
if %Number% LSS 1 goto EnterNumber
rem Output the string according to entered number by forcing Windows command
rem processor again to double processing the command line because of CALL.
call echo %%string[%number%]%%
rem Restore previous environment with discarding all the
rem environment variables defined by this batch file.
:EndBatch
endlocal
pause
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 /?
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
rem /?
set /?
setlocal /?
See also:
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files
How to set environment variables with spaces?
Where does GOTO :EOF return to?
I wan to search for a specific string in the file-path and double that particular string.
Example: if my filepath is C:\Users\XXXX\Desktop\g%h.txt
I want to search and get the % symbol and I need to double it like this C:\Users\XXXX\Desktop\g%%h.txt
How to do this with batch file?
in batchfiles, you have to escape some special chars, so cmd knows NOT to treat them as "special". A percent-sign is escaped with another percent-sign:
#echo off
setlocal enabledelayedexpansion
for %%i in (*.txt) do (
set name=%%i
echo old name: !name!
set name=!name:%%=%%%%!
echo new name: !name!
)
Here is sample to match any txt that contain one or more %
#echo off
setlocal enabledelayedexpansion
for /f "delims=" %%i in ('dir /b /a-d *.txt ^|findstr /i "%%"') do ( set file=%%i
rem Here to remove every % in the name.
echo ren "!file!" "!file:%%=!"
)
exit /b 0
In batch files as it was mentioned above you have to escape % sign with another % sign and file names are no exception. So instead of set file=C:\Users\XXX\Desktop\r%h.txt you must use set file=C:\Users\XXX\Desktop\r%%h.txt.
I wrote a separate batch file that accept two arguments: the first one is naming pattern and the second one is a symbol that needs to be doubled. So, for instance, if you want to double character "r" in all .txt files in the current directory you type
<nameOfTheBatchFile> *.txt r in the command line. Or in your case you can go with
<nameOfTheBatchFile> C:\Users\XXX\Desktop\r%h.txt %
#echo off
setlocal enabledelayedexpansion
set "namingPattern=%1"
set "charToDouble=%2"
for %%i in (%namingPattern%) do (
set "curName=%%i"
set newName=!curName:%charToDouble%=%charToDouble%%charToDouble%!
if "!curName!" neq "!newName!" (
echo Renaming !curName! -^> !newName!
ren "!curName!" "!newName!"
)
)
I've created the following config file which contains parameters to be used by a batch file:
File winscp.conf:
folder %appData%\winscp
version 5.7.4
visit http://sourceforge.net/projects/winscp/files/WinSCP/
download http://sourceforge.net/projects/winscp/files/WinSCP/5.7.4/winscp574.zip
Batch file (get.bat):
#echo off
setlocal
#if not exist "%1" (
echo Config file not found in "%1"
exit /B
)
#for /f "tokens=1,2 delims= " %%A in (%1) do (
set %%A=%%B
)
mkdir %folder%
When I call the batch file like this:
get.bat winscp.conf
I get a sub-folder %appData%\winscp created in the current folder, something like this:
c:\Temp\%appData%\winscp
While what I want is a winscp folder created in the Windows app data folder, something like this:
C:\Users\Caffe\AppData\Roaming\winscp
I think there's something wrong with the statement set %%A=%%B, since if I change it to set %%A=%appData%\winscp I do get the folder created the way I want.
I just summarize the answers of wOxxOm and JosefZ with adding some small improvements.
#echo off
setlocal EnableExtensions
set "ConfigFile=%~1"
if "%ConfigFile%" == "" (
echo %~nx0 must be called with name of configuration file
echo as first parameter, for example: %~nx0 winscp.conf
goto EndBatch
)
if not exist "%ConfigFile%" (
echo Config file "%ConfigFile%" not found.
goto EndBatch
)
set "folder="
for /F "usebackq tokens=1*" %%A in ("%ConfigFile%") do call set "%%A=%%B"
if "%folder%" == "" (
echo Option "folder" not defined in config file "%ConfigFile%".
goto EndBatch
)
mkdir "%folder%" 2>nul
:EndBatch
endlocal
For better reading what first argument passed to the batch file should be, the parameter is immediately assigned to an environment variable without the surrounding quotes which must be used if configuration file name with or without path contains anywhere at least 1 space character.
Next a check is made if batch file was called at all with a parameter.
After FOR loop it is verified if the configuration file really contained an entry for folder.
And creation of directory is made with using quotes because folder path can contain spaces, and with redirecting any error message to device nul, i.e. suppress the error message output if directory exists already.
The variables inside tokens should be expanded prior to the assignment by adding call:
call set %%A=%%B
With next changes in your code:
call set instead of set
for /f "tokens=1,* instead of for /f "tokens=1,2
then next code snippet should work:
#ECHO OFF
SETLOCAL enableextensions
if not exist "%1" (
echo Config file not found in "%1"
exit /B
)
for /f "tokens=1,* delims= " %%A in (%1) do (
call set "%%A=%%B"
)
mkdir %folder%
I am trying to create a windows batch file that will:
1st: List files in current directory and output to a filenamelist (Filelist_[YYY][MM][DD][hh][mm]ss])
2nd: From the given filenamelist, read each line as an input for 3rd step
3rd: Read the first line of the file and extract 7th, 9th, and and 14th string.
Delimiter can be "~", "*" and "^"
4th: Assign 7th as ISAsender variable, 9th as ISAreceiver, and 14th string as ISActrlnum.
5th: Redirect the echoed variables to a single logfile (EDI.log).
EDIT:
6th: Check ISAsender's CNtable.txt (Ex. AP_CNtable.txt for ISA's APPLESND, SS_CNtable for SAMSUNGSND) if ISActrlnum already exists or duplicate. Initially CNtable.txt contains dumped values of ISActrlnum (control numbers).
a. If there is duplicate control number found, exit with error message and output to Dupfile.txt as well as the ISActrlnum in issue. Move file to EXCEPT folder.
b. If not duplicate, insert ISActrlnum to CNtable.txt then proceed to 7th.
7th: Use IF condition for ISAsender and ISAreceiver to move the file to a correspoinding folder.
8th: Repeat 3rd-7th until 2nd step reaches EOF or finished reading each lines.
Example: From a given Filelist_20141022010101, FileName.txt contains the following having "~" as delimiter:
ISA~00~ ~00~ ~ZZ~APPLESND ~14~APPLERCV ~130214~2300~U~00401~000000001~0~T~>
GS~FA~APPLESND~APPLERCV~20130214~2300~810~X~004010
ST~997~131250001
AK1~SC~1809
AK9~A~1~1~1
SE~4~131250001
GE~1~810
IEA~1~000000001
while CNtable.txt contains:
000000004
000000002
000000003
000000005
Since ISActrlnum=000000001 does not exist in CNtable.txt, it will update CNtable.txt by appending ISActrlnum. Here only conditional statements for the ISAsender and ISAreceiver will be utilized.
So if ISAsender=APPLESND and ISAreceiver=APPLERCV move the curent file (FileName.txt) to specific directory such as C:\VP\APPLE.
Same logic for the next filename inside Filelist_20141022010101.
I was able to write code below but don't know how to get the filename from FORFILES and use it again as variable input in the FOR LOOP.
REM FileReceiver.bat
setlocal enabledelayedexpansion
for /f "delims=" %%a in ('wmic OS Get localdatetime ^| find "."') do set dt=%%a
set YYYY=%dt:~0,4%
set MM=%dt:~4,2%
set DD=%dt:~6,2%
set HH=%dt:~8,2%
set Min=%dt:~10,2%
set Sec=%dt:~12,2%
set TIMESTAMP=%YYYY%-%MM%-%DD%_%HH%-%Min%-%Sec%
C:
cd "C:\VP"
FORFILES /P "C:\VP" >"FLIST.%TIMESTAMP%"
set first=1
for /f "tokens=7 delims=~" %%a in (FileName.txt) do (
if %first%==1 (
set first=0
set ISAsender=%%a
echo %ISAsender%
)
)
set first=1
for /f "tokens=9 delims=~" %%b in (FileName.txt) do (
if %first%==1 (
set first=0
set ISAreceiver=%%b
echo %ISAreceiver%
)
)
set first=1
for /f "tokens=14 delims=~" %%c in (FileName.txt) do (
if %first%==1 (
set first=0
set ISActrlnum=%%c
echo %ISActrlnum%
)
)
if %ISAsender%=="APPLESND" if %ISAreceiver=="APPLERCV" (
move FileName.txt "C:\VP\APPLE"
)
if %ISAsender%=="SAMSUNGSND" if %ISAreceiver=="SAMSUNGRCV" (
move FileName.txt "C:\VP\SAMSUNG"
)
Can anyone help me to satisfy the requirement without having to call another batch file that checks the duplicate control numbers (ISActrlnum) ?
As much as possible I want the whole code in a single bat file. Please bear with my lengthy description.
I needed to setup the entire environment you have on your computer to recode your batch file, but I think I got it.
Read the comments - the lines starting with rem - to understand the batch file and adapt it to your environment if this necessary.
#echo off
REM FileReceiver.bat
setlocal
rem Get current date and time as local time independent on Windows region settings.
for /F "tokens=2 delims==." %%T in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "dt=%%T"
rem Reformat the date and time strong to wanted format.
set "YYYY=%dt:~0,4%"
set "MM=%dt:~4,2%"
set "DD=%dt:~6,2%"
set "HH=%dt:~8,2%"
set "Min=%dt:~10,2%"
set "Sec=%dt:~12,2%"
set "TimeStamp=%YYYY%-%MM%-%DD%_%HH%-%Min%-%Sec%"
rem Define name of the list file containing current date and time in name.
set "ListFile=FLIST_%TimeStamp%.lst"
rem Change directory (and drive).
cd /D "C:\VP"
rem Create the list file which is good here as the list of files changes
rem while running this batch file and therefore it is better to work with
rem a list file instead of running a FOR directly for each file in the
rem directory. The list file is not included in this list as it either does
rem not exist at all or it has wrong file extension as only *.txt files are
rem listed by command DIR. The log file EDI.log has also wrong file extension.
dir *.txt /A-D /B /ON >%ListFile%
rem It might be useful to delete the log file from a previous run.
if exist EDI.log del EDI.log
rem Process each file in the list file.
for /F "delims=" %%F in ( %ListFile% ) do call :ProcessFile "%%F"
rem Delete the list file as not needed anymore. It could be also kept.
del %ListFile%
rem Exit batch file.
endlocal
goto :EOF
:ProcessFile
rem The parameter passed from first FOR is the file name in double quotes.
set "FileName=%~1"
rem Ignore the files *_CNtable.txt and Dupfile.txt in same directory.
rem Command goto :EOF just exits here from subroutine ProcessFile.
if "%FileName%"=="AP_CNtable.txt" goto :EOF
if "%FileName%"=="SS_CNtable.txt" goto :EOF
if "%FileName%"=="Dupfile.txt" goto :EOF
rem Get 7th, 9th and 14th element from first line of current file.
for /f "usebackq tokens=7,9,14 delims=~*^" %%a in ( "%FileName%" ) do (
set "ISAsender=%%a"
set "ISAreceiver=%%b"
set "ISActrlnum=%%c"
goto WriteToLogFile
)
:WriteToLogFile
rem Remove all spaces as ISAsender and ISAreceiver have
rem usually spaces appended at end according to example
rem text. Then write file name and the 3 values to log file.
set "ISAsender=%ISAsender: =%"
set "ISAreceiver=%ISAreceiver: =%"
set "ISActrlnum=%ISActrlnum: =%"
echo %FileName%,%ISAsender%,%ISAreceiver%,%ISActrlnum%>>EDI.log
if "%ISAsender%"=="APPLESND" (
rem Check for ISA control number in file AP_CNtable.txt.
%SystemRoot%\System32\Findstr.exe /X /M /C:%ISActrlnum% AP_CNtable.txt >nul
if not errorlevel 1 goto Duplicate
echo %ISActrlnum%>>AP_CNtable.txt
if "%ISAreceiver%"=="APPLERCV" (
move /Y "%FileName%" "APPLE"
echo Moved %FileName% to directory APPLE.
)
)
if "%ISAsender%"=="SAMSUNGSND" (
rem Check for ISA control number in file SS_CNtable.txt.
%SystemRoot%\System32\Findstr.exe /X /M /C:%ISActrlnum% SS_CNtable.txt >nul
if not errorlevel 1 goto Duplicate
echo %ISActrlnum%>>SS_CNtable.txt
if "%ISAreceiver%"=="SAMSUNGRCV" (
move /Y "%FileName%" "SAMSUNG"
echo Moved %FileName% to directory SAMSUNG.
)
)
rem Exit the subroutine ProcessFile.
goto :EOF
:Duplicate
rem This ISA control number is already present in file *_CNtable.txt.
echo Duplicate control %ISActrlnum% found in file %FileName%.
echo %ISActrlnum%,%FileName%>>Dupfile.txt
rem Exit the subroutine ProcessFile.
goto :EOF
I'm not sure why the list file must have the current date and time in name, but perhaps you want to keep it for some unknown reason. In this case it would be necessary to delete 2 lines in code above - the line with command del %ListFile% and the comment line above.
Updated batch code on 2014-10-07 according to changes on point 6 in question.
our system is going to be migrated from Linux to Windows machine so I'm preparing a batch file equivalent to our existing script. I already have created the batch file but I need to unwrap first the file before processing its next line of codes.
Example. Here is a one-liner wherein the delimiter is "{".
Note: Delimiter can be any or variable character except element delimiter ("~" in this case).
ISA~00~ ~00~ ~ZZ~SAMSUNGSND ~14~181087842 ~130214~2300~U~00401~000000003~0~T~>{GS~FA~181087842TEST~SYNNTEST~20130214~2300~810~X~004010{ST~997~131250001{AK1~SC~1809{AK9~A~1~1~1{SE~4~131250001{GE~1~810{IEA~1~000000001
I need it to be unwrapped like this (equivalent to tr "{" "\n" < FileName.txt ):
ISA~00~ ~00~ ~ZZ~SAMSUNGSND ~14~181087842 ~130214~2300~U~00401~000000003~0~T~>
GS~FA~181087842TEST~SYNNTEST~20130214~2300~810~X~004010
ST~997~131250001
AK1~SC~1809
AK9~A~1~1~1
SE~4~131250001
GE~1~810
IEA~1~000000001
EDIT:
Once unwrapped, I need to search fixed values of third field if equal to "1145837" under GS segment (2nd line) and replace it with "1119283" (which is equivalent to sed '/^GS/ s/1145837/1119283/').
Below is my batch file. I need the code to be inserted somewhere inside :WriteToLogFile subroutine
#echo on
::This ensures the parameters are resolved prior to the internal variable
SetLocal EnableDelayedExpansion
rem Get current date and time as local time.
for /f "delims=" %%a in ('wmic OS Get localdatetime ^| %SystemRoot%\System32\Find.exe "."') do set dt=%%a
rem Reformat the date and time strong to wanted format.
set "YYYY=%dt:~0,4%"
set "MM=%dt:~4,2%"
set "DD=%dt:~6,2%"
set "HH=%dt:~8,2%"
set "Min=%dt:~10,2%"
set "Sec=%dt:~12,2%"
set "TimeStamp=%YYYY%-%MM%-%DD%_%HH%-%Min%-%Sec%"
rem Define name of the list file containing current date and time in name.
set "ListFile=FLIST_%TimeStamp%.lst"
rem Change directory (and drive).
cd /D "C:\VP"
rem Create the list file which is good here as the list of files changes
rem while running this batch file and therefore it is better to work with
rem a list file instead of running a FOR directly for each file in the
rem directory. The list file is not included in this list as it either does
rem not exist at all or it has wrong file extension as only *.txt files are
rem listed by command DIR. The log file EDI.log has also wrong file extension.
dir *.txt /A:-D /B /O:D >"C:\VP\TEST\%ListFile%"
rem It might be useful to delete the log file from a previous run.
if exist EDI.log del EDI.log
rem Process each file in the list file.
cd /D "C:\VP\TEST"
for /F "delims=" %%F in ( %ListFile% ) do call :ProcessFile "%%F"
cd /D "C:\VP"
::rem Delete the list file as not needed anymore. It could be also kept.
::del %ListFile%
rem Exit batch file.
endlocal
goto :EOF
:ProcessFile
rem The parameter passed from first FOR is the file name in double quotes.
set "FileName=%~1"
rem Ignore the files CNtable.txt and Dupfile.txt in same directory.
rem Command goto :EOF just exits here from subroutine ProcessFile.
if "%FileName%"=="CNtable.txt" goto :EOF
if "%FileName%"=="Dupfile.txt" goto :EOF
if "%FileName%"=="VanPointAS2in.bat" goto :EOF
if "%FileName%"=="VP.bat" goto :EOF
rem Get 7th, 9th and 14th element from first line of current file.
cd /D "C:\VP"
for /f "usebackq tokens=7,9,14 delims=~*^" %%a in ( "%FileName%" ) do (
set "ISAsender=%%a"
set "ISAreceiver=%%b"
set "ISActrlnum=%%c"
goto WriteToLogFile
)
:WriteToLogFile
rem Remove all spaces as ISAsender and ISAreceiver have
rem usually spaces appended at end according to example
rem text. Then write file name and the 3 values to log file.
set "ISAsender=%ISAsender: =%"
set "ISAreceiver=%ISAreceiver: =%"
set "ISActrlnum=%ISActrlnum: =%"
echo %FileName%,%ISAsender%,%ISAreceiver%,%ISActrlnum%>>"C:\VP\TEST\EDI.log"
set "FLAG=N"
if "%ISAsender%"=="APPLESND" (
if "%ISAreceiver%"=="MANGO" (
set "FLAG=Y"
set "VW=AP"
call :DupCheck
echo %errorlevel%>>"C:\VP\TEST\EDI.log"
if errorlevel 1 move /Y "%FileName%" "APPLE"
echo Moved %FileName% to directory APPLE.
)
)
if "%ISAsender%"=="APPLESND" (
if "%ISAreceiver%"=="MANGOES" (
set "FLAG=Y"
set "VW=AP"
call :DupCheck
echo %errorlevel%>>"C:\VP\TEST\EDI.log"
if errorlevel 1 move /Y "%FileName%" "APPLE"
echo Moved %FileName% to directory APPLE.
)
)
if "%ISAsender%"=="SAMSUNGSND" (
if "%ISAreceiver%"=="MANGO" (
set "FLAG=Y"
set "VW=SS"
call :DupCheck
echo %errorlevel%>>"C:\VP\TEST\EDI.log"
if errorlevel 1 move /Y "%FileName%" "SAMSUNG"
echo Moved %FileName% to directory SAMSUNG.
)
)
rem Move to directory BYPASS if all else not satisfied.
if "%FLAG%"=="N" (
move /Y "%FileName%" "BYPASS"
echo Moved %FileName% to directory BYPASS
)
rem Exit the subroutine WriteToLogFile.
goto :EOF
:DupCheck
rem Check for ISA control number in file %VW%_table.txt.
%SystemRoot%\System32\Findstr.exe /X /M /C:%ISActrlnum% "C:\VP\TEST\%VW%_table.txt" >nul
if errorlevel 1 goto NewControl
rem This ISA control number is already present in file %VW%_table.txt.
echo Duplicate control %ISActrlnum% found in file %FileName%.
echo %ISActrlnum%,%FileName%>>"C:\VP\TEST\Dupfile.txt"
move /Y "%FileName%" "DUPLICATES"
echo Moved %FileName% to directory DUPLICATES.
rem Exit the subroutine DupCheck.
goto :EOF
:NewControl
echo %ISActrlnum%>>"C:\VP\TEST\%VW%_table.txt"
Any help is appreciated.
Manipulating text files with native batch commands is rather tricky, and quite slow. Most tasks can be done, but it requires quite a few advanced batch techniques to make the solution robust.
You will probably be most happy with GnuWin32 - a free collection of unix utilities for Windows. You could then manipulate file content with familiar tools.
Another good alternative (my favorite - no surprise since I wrote it) is to use REPL.BAT - a hybrid JScript/batch utility that performs a regex search/replace operation on stdin and writes the result to stdout. It is pure script that will run natively on any Windows machine from XP forward. Full documentation is embedded within the script.
I recommend replacing your line delimiter with \r\n rather than \n, as that is the Windows standard for newlines.
Assuming REPL.BAT is in your current directory, or somewhere within your PATH, then the following will make your needed changes:
set "file=fileName.txt"
type "fileName.txt" | repl "{" "\r\n" lx >"%file%.new"
move /y "%file%.new" "%file%" >nul
:GS_replace
<"%file%" call repl "^(GS~.*)1145837" "$11119283" >"%file%.new"
set "rtn=%errorlevel%"
move /y "%file%.new" "%file%" >nul
if %rtn% equ 0 goto GS_replace
I'm concerned that your string of numeric digits could be embedded within a larger number, leading to an unwanted substitution. You might want to refine your search term to prevent this.
The following would only replace an entire field:
:GS_replace
<"%file%" call repl "^(GS~(?:.*~)*)1145837(~|$)" "$11119283$2" >"%file%.new"
set "rtn=%errorlevel%"
move /y "%file%.new" "%file%" >nul
if %rtn% equ 0 goto GS_replace
The following would only replace an entire number that may be embedded within a larger alpha-numeric string:
:GS_replace
<"%file%" call repl "^(GS~(?:.*\D)*)1145837(\D|$)" "$11119283$2" >"%file%.new"
set "rtn=%errorlevel%"
move /y "%file%.new" "%file%" >nul
if %rtn% equ 0 goto GS_replace
In your comment below, you say you want to restrict the number change to the 3rd field of GS lines. (This is quite different than what you stated in your original question.) This is much simpler - no loop is required:
type "%file%" | repl "^(GS~(.*?~){2})1145837(~|$)" "$11119283$2" >"%file%.new"
move /y "%file%.new" "%file%" >nul