Separate first and last names with for loop - batch-file

I'm working on a batch that creates a .txt-file from a record. The file will be processed later by another program.
The record looks like this at the moment:
11111;Lastname Firstname SecondFirstname;1234567;SomeText
22222;Lastname Firstname;1254557;SomeText
33333;Lastname Firstname;1234567;SomeText
I would like to have a semicolon between the last name and the first name. The problem is the sentences with two first names. Here should be no semicolon between the names.
In the end it should look like this:
11111;Lastname;Firstname SecondFirstname;1234567;SomeText
22222;Lastname;Firstname;1254557;SomeText
33333;Lastname;Firstname;1234567;SomeText
Does anyone have an idea here?
I have tried the following but that does not solve the problem with the two first names:
type nul>tmp.txt
for /f "delims=: tokens=1*" %%i in ('findstr /n "^" "test_output.txt"') do set "Zeile=%%j" &call :sub
move /y "tmp.txt" "test_output.txt"
goto :eof
:sub
if not defined Zeile (
>>tmp.txt echo.
goto :eof
)
>>tmp.txt echo %Zeile: =;%
goto :eof

Parsing the first time by the ; delimiter and parsing the second token (the third, if you take the line numbers into account) a second time by a <SPACE> delimiter (with tokens=1,*) solves your problem:
#echo off
(for /f "tokens=1-3,* delims=;:" %%A in ('findstr /n "^" "test_output.txt"') do (
if "%%B"=="" echo/
for /f "tokens=1,*" %%M in ("%%C") do (
echo/%%B;%%M;%%N;%%D
)
))>tmp.txt
goto :eof
(redirecting the whole output at once is a lot faster than redirecting single lines - especially with big files)

You need two nested for /F the first to split at the semicolon, the 2nd to split at the space limited to 2 splits.
Using uppercase %A and lowercase %a for the meta variables, in cmd line:
for /F "tokens=1-3* delims=;" %A in (test_output.txt) do #for /F "tokens=1*" %a in ("%B") do #Echo %A;%a;%b;%C;%D
In a batch file saving to new_output.txt:
:: Q:\Test\2019\06\04\SO_56443069.cmd
#Echo off&SetLocal EnableDelayedExpansion
(for /F "tokens=1-3* delims=;" %%A in (test_output.txt
) do for /F "tokens=1*" %%a in ("%%B"
) do Echo %%A;%%a;%%b;%%C;%%D
) >new_output.txt
Sample output:
> type new_output.txt
11111;Lastname;Firstname SecondFirstname;1234567;SomeText
22222;Lastname;Firstname;1254557;SomeText
33333;Lastname;Firstname;1234567;SomeText

Related

Batch script to read a specific line from a log file

I am trying to write a batch script that reads 18th line from a .log file and outputs the same. The .log file name varies each time. abc_XXXX.log where xxxx are process IDs. Below is the code I am trying to run to achieve this.
:Test1
set "xprvar=" for /F "skip=17 delims=" %%p in (abc*.log) do (echo %%p& goto
break)
:break
pause
goto END
set var=anyCommand doesn't work. It just sets the var to the literal string.
The usage of afor /f is the right way, just the variable assignment works different:
for /F "skip=17 delims=" %%p in ('dir /b abc*.log') do ( set "xprvar=%%p"& goto break )
There is also an option using FindStr
#Echo Off
For /F "Tokens=1-2* Delims=:" %%A In ('FindStr/N "^" "abc_*.log" 2^>Nul'
) Do If %%B Equ 18 Echo %%A:%%C
Pause
The above example Echoes the <filename>:<18th line content>, but there's no reason in the appropriate situation why you couldn't change that to read:
#Echo Off
For /F "Tokens=1-2* Delims=:" %%A In ('FindStr/N "^" "abc_*.log" 2^>Nul'
) Do If %%B Equ 18 Set "xprvar=%%C"
If there is more than one matching filename in the directory, the variable would be set to the content in the last file parsed.
#ECHO Off
SETLOCAL
FOR %%f IN (abc*.log) DO (
SET "reported="
FOR /f "skip=17delims=" %%p IN (%%f) DO IF NOT DEFINED reported (
ECHO %%p
SET "reported=Y"
)
)
Assign each filename in turn to %%f.
For each filename found, clear the reported flag then read the file, skipping the first 17 lines. echo the 18th line and set the reported flag so that the remainder of the lines are not echoed.

I am writing a .bat program to find and replace text in a file without changing its position

I am writing a .bat program that will find and replace text in a file. The problem that I am having is that it is removing blank lines and left justifying the other lines. I need the blank lines to remain and the new text to remain in the same location. Here is what I have wrote, and also the result. Can anybody please help.
program:
#ECHO OFF
cls
cd\
c:
setLocal EnableDelayedExpansion
For /f "tokens=* delims= " %%a in (samplefile.tx) do (
Set str=%%a
set str=!str:day=night!
set str=!str:winter=summer!
echo !str!>>samplefile2.txt)
ENDLOCAL
cls
exit
samle File:
this line is the first line in my file that I am using as an example.This is made up text
the cat in the hat
day
winter
below is the result:
this line is the first line in my file that I am using as an example.This is made up text
the cat in the hat
night
summer
I need the lines, spaces and new text to remain in the same position while making the text replacement. Please help
Your use of "tokens=* delims= " will trim leading spaces. Instead, use "delims=" to preserve leading spaces.
FOR /F always skips empty lines. The trick is to insert something before each line. Typically FIND or FINDSTR is used to insert the line number at the front of each line.
You can use !var:*:=! to delete the the line number prefix from FINDSTR.
Use echo(!str! to prevent ECHO is off message when line is empty
It is more efficient (faster) to redirect only once.
#echo off
setlocal enableDelayedExpansion
>samplefile2.txt (
for /f "delims=" %%A in ('findstr /n "^" samplefile.txt') do (
set "str=%%A"
set "str=!str:*:=!"
set "str=!str:day=night!"
set "str=!str:winter=summer!"
echo(!str!
)
)
This still has a potential problem. It will corrupt lines that contain ! when %%A is expanded because of the delayed expansion. The trick is to toggle delayed expansion on and off within the loop.
#echo off
setlocal disableDelayedExpansion
>samplefile2.txt (
for /f "delims=" %%A in ('findstr /n "^" samplefile.txt') do (
set "str=%%A"
setlocal enableDelayedExpansion
set "str=!str:*:=!"
set "str=!str:day=night!"
set "str=!str:winter=summer!"
echo(!str!
endlocal
)
)
Or you could forget custom batch entirely and get a much simpler and faster solution using my JREPL.BAT utility that performs regular expression search and replace on text. There are options to specify multiple literal search/replace pairs.
jrepl "day winter" "night summer" /t " " /l /i /f sampleFile.txt /o sampleFile2.txt
I used the /I option to make the search case insensitive. But you can drop that option to make it case sensitive if you prefer. That cannot be done easily using pure batch.
#ECHO Off
SETLOCAL
(
FOR /f "tokens=1*delims=]" %%a IN ('find /n /v "" q27459813.txt') DO (
SET "line=%%b"
IF DEFINED line (CALL :subs) ELSE (ECHO()
)
)>newfile.txt
GOTO :EOF
:subs
SET "line=%line:day=night%"
SET "line=%line:winter=summer%"
ECHO(%line%
GOTO :eof
Thi should work for you. I used a file named q27459813.txt containing your data for my testing.
Produces newfile.txt
Will not work correctly if the datafile lines start ].
Revised to allow leading ]
#ECHO Off
SETLOCAL
(
FOR /f "delims=" %%a IN ('type q27459813.txt^|find /n /v "" ') DO (
SET "line=%%a"
CALL :subs
)
)>newfile.txt
GOTO :EOF
:subs
SET "line=%line:*]=%"
IF NOT DEFINED line ECHO(&GOTO :EOF
SET "line=%line:day=night%"
SET "line=%line:winter=summer%"
ECHO(%line%
GOTO :eof

Batch FOR /F loop won't read relative directory

I have a simply FOR /F loop which strips out all but one line of a text file:
for /f "skip=12 tokens=* delims= " %%f in (.\NonProcessed\*.txt) do (
> newfile.txt echo.%%f
goto :eof
)
But when I run, I get the result:
The system cannot find the file .\NonProcessed\*.txt
The for loop works fine if I enter a fully qualified path to the text file within the brackets, but it can't handle the relative link I have in there. I've been able to use the exact same relative link in another standard for loop in a different batch file running in the same directory without any issues. I can't understand why it won't work! Please help.
EDIT: For comments, code I'm using now is
for %%f in (.\NonProcessed\*.txt) do (
echo f is %%f
for /f "usebackq skip=12 tokens=* delims= " %%a in (%%f) do (
echo a is %%a
> %%f echo.%%a
goto :continue
)
:continue
sqlcmd stuff here
)
Sorry but for /f does not allow you to do that. And no, the problem is not the relative path to files but the wildcard.
According to documentation, you have the syntax case
for /F ["ParsingKeywords"] {%% | %}variable in (filenameset) do command [CommandLineOptions]
For this case, documentation states The Set argument specifies one or more file names. You can do
for /f %%a in (file1.txt file2.txt file3.txt) do ...
but wildcards are not allowed.
If you don't know the name of the file you want to process, your best option is to add an additional for command to first select the file
for %%a in (".\NonProcessed\*.txt"
) do for /f "usebackq skip=12 tokens=* delims= " %%f in ("%%~fa"
) do (
> newfile.txt echo(%%f
goto :eof
)
When executed, the goto command will cancel both for loops so you end with the same behaviour you expected from your original code.
edited to adapt code to comments
#echo off
set "folder=.\NonProcessed"
pushd "%folder%"
for /f "tokens=1,2,* delims=:" %%a in (
' findstr /n "^" *.txt ^| findstr /r /b /c:"[^:]*:13:" '
) do (
echo Overwrite file "%%a" with content "%%c"
>"%%a" echo(%%c
)
popd
Read all the files in the folder, numbering the lines. The output for the first findstr command will be
filename.txt:99:lineContents
This output is parsed to find the line 13, the resulting data is splitted using the colon as a separator, so we will end with the file name in %%a, the line number in %%b and the line content in %%c.
SET FILES_LIST=files_list.config
DIR /b .\NonProcessed\*.txt>!FILES_LIST!
for /f "skip=12 tokens=* delims= " %%f in (!FILES_LIST!) do (
> newfile.txt echo.%%f
goto :eof
)
IF EXIST "!FILES_LIST!" DEL "!FILES_LIST!"
I did not check how your's FOR works, just added my additions/corrections to it.... Hope it will work for you.
Best regards!

Batch script to extract same line from each log file

I have a number of text files that are always the same number of lines - 42 and always the same type of information on each line. Also each line starts with a header.
What I would like is a batch script to keep line 30 of the text file remove the others and save the file.
I have tried to look find the line based on the information between two line. In this case the heading on line 30 (Job Notes) and the heading on line 31 (Job Number) and then write the information to a new file.
Line 30 begins with
Job Notes= (information specifically about the job)
Line 31 begins with
Job Number=
This is the code I used (which i found elsewhere on this site) and i am getting no output at all. Have tried other ways as well so don't really have to use this method if you can see a better one, basically i just want line 30 to be the only information in the file.
#ECHO OFF
SETLOCAL
SET "sourcedir=C\Batch"
SET "destdir=C:\Batch\Extract"
for /f "tokens=1 delims=[]" %%a in ('find /n "Job Notes"^<"%sourcedir%\7099.txt" ') do set /a start=%%a
for /f "tokens=1 delims=[]" %%a in ('find /n "Job Number"^<"%sourcedir%\7099.txt" ') do set /a end=%%a
(
for /f "tokens=1* delims=[]" %%a in ('find /n /v ""^<"%sourcedir%\00007099.txt" ') do (
IF %%a geq %start% IF %%a leq %end% ECHO(%%b
)
)>"%destdir%\newfile.txt"
GOTO :EOF
Any help would be greatly appreciated.
David
Option 1, using findstr string numeration
for %%a in (*.log) do (
for /f "tokens=1,* delims=:" %%b in (
'findstr /n "^" "%%a" ^| findstr /b "30:"'
) do (
echo(%%c>"%%a"
)
)
Option 2, using for command skip lines
setlocal disabledelayedexpansion
for %%a in (*.log) do (
set "line="
for /f "usebackq skip=29 delims=" %%b in ("%%a") do if not defined line set "line=%%b"
setlocal enabledelayedexpansion
echo(!line!>"%%a"
endlocal
)
You want you the /N flag with FINDSTR:
FINDSTR /N /R ".*" *.txt | FINDSTR /R "\<30:" > out.txt
Explanation:
FINDSTR /N /R ".*" *.txt
finds every line (".*" with the regex flag /R) in every text file (*.txt) and appends its line number to the front (/N). This is then piped to
FINDSTR /R "\<30:"
which only grabs lines starting with (that's the \< bit) 30:.
#ECHO OFF
SETLOCAL
SET "sourcedir=C\Batch"
SET "destdir=C:\Batch\Extract"
for /f "skip=29tokens=1* delims==" %%a in ('type "%sourcedir%\7099.txt" ') do ECHO(%%b >"%destdir%\newfile.txt"&goto nextstep
:nextstep
GOTO :EOF
should do the job, finding line 30 (by skipping 29) then tokenising on the separating = and having output exactly one, going to the next step in your batch.
Having said that, however - you do realise that you are setting start and end using 7099.txt and then picking the data from 00007099.txt don't you? Does 00007099.txt exist?
And c:\batch seems a mighty strange place to store data files to me. Personally, I'd put batch files there (and include c:\batch into your PATH) but it's your system...

How do I only assign FOR /F token from one line only in a test file?

How do I make this grab the token from the first line ONLY in .txt file instead of looping through every line. I want %%m to be assigned to the 3rd token on line one only then stop.
#echo off
FOR %%A IN (.\xdrive\*.txt) DO (
FOR /F "usebackq tokens=3 delims=," %%m IN ("%%A") DO (
IF "%%m" == "F01" (xcopy /Y "%%A" .\Outbound)
pause
)
)
pause
set /p can be used to read the first line, and then you can use a FOR /F loop to get the third token
setlocal EnableDelayedExpansion
FOR %%A IN (%1) DO (
<%%A set /p firstline=
FOR /F "tokens=3 delims=," %%m IN ("!firstline!") DO (
echo %%m
)
)
Without see the eg files and knowing exactly what you're trying to do I can't test this, but here's the listing of firstline.bat which should do what you're asking for :) At first I thought this needed to be more complicated than it is... after your first if simply use a goto to exit the for structure after it's first call - problem solved?
#echo off
::: firstline.bat - Retrieve the first line from a series of files
::: usage: firstline $filespec
::: filespace - files to process (eg .\xdrive\*.txt)
if "%~1"=="" findstr "^:::" "%~f0"&GOTO:EOF
FOR %%A IN (%1) DO (
call :testfirst "%%A"
)
goto :eof
:testfirst
FOR /F "usebackq tokens=3 delims=," %%m IN (%1) DO (
IF "%%m" == "F01" (xcopy /Y %1 .\Outbound)
goto:eof
)
See this post, which shows how to mimic the gnu head utility using a dos batch file:
Windows batch command(s) to read first line from text file
untested
read first line tokens3
for /f "tokens=3 delims=," %%a in ('"findstr /n . %1|findstr /b 1:"') do set fltok3=%%a
echo(%fltok3%
Hackish =
#echo off
FOR %%A IN (.\xdrive\*.txt) DO (
FOR /F "usebackq tokens=3 delims=," %%m IN ("%%A") DO (
IF "%%m" == "F01" (xcopy /Y "%%A" .\Outbound)
GOTO:EOF
)
)
So all you're doing is escaping the loop after the first pass instead of continuing onto the next line.

Resources