Reading a specific column from a row using a batch file - batch-file

I have a notepad which looks like this.
Job Name Last Start Last End ST Run/Ntry Pri/Xit
________________________________________________________________ ____________________ ____________________ __ ________ _______
DEV_xxx_xxx_xxx_xxxx_b 11/20/2012 22:05:00 ----- RU 9229277/1
This value "22:05:00" is present on the 77th column of row 3. Is there a way using a batch script that i can extract only this value and assign it to a variable. The above notepad is the redicrected output of an autosys command (if it helps)
I've been breaking my head for the past 3 days but to no avail.

Untested
#echo off
for /f "skip=2 delims=" %%a in (file.txt) do set "var=%%a"&goto :done
:done
set "var=%var:~76,8%"
echo "%var%"
pause

Another way (faster) to read the 3rd line.
#echo off
<file.txt (
set/p=
set/p=
set /p var=
)
set "var=%var:~76,8%"
Use a FOR /L loop if you want to skip a larger number of lines
#echo off
<file.txt (
for /l %%N in (1 1 2) do set/p=
set /p var=
)
set "var=%var:~76,8%"
If the job name never contains spaces, then it looks like you could also use FOR /F in the traditional way.
#echo off
for /f "skip=2 tokens=3" %%A in (file.txt) do set "var=%%A" & goto :break
:break

search it with GNU sed (very fast!)
for /f %%i in ('sed -n "/DEV/ {s/.*\([0-2][0-4]:[0-5][0-9]:[0-5][0-9]\).*/\1/p;q}" file.txt') do set "var=%%i"
echo %var%

Related

Find current console size in batch

I am working on a batch file and i need to print hyphens ( - ) across the screen as a separator. Is there a fast (under two seconds) command that can do this?
I have done multiple search queries and could not find the answer on various websites.
(code to find screen size)
for /l %%a in (1,1,%screen size var%) do (set "line=%line%-")
echo %line%
The output should show a line of hyphens across the console.
This is untested, but based upon the output from Mode CON as used in gjpio's answer:
#Echo Off
For /F "Skip=4Tokens=1*Delims=:" %%A In ('Mode CON')Do (For /L %%C In (1,1,%%B)Do #Set/P "=-"<Nul)&Echo( &GoTo :Draw
:Draw
Pause
If you intend to use the separator multiple times within your script you could save it to a variable:
#Echo Off
For /F "Skip=4Tokens=1*Delims=:" %%A In ('Mode CON')Do (For /L %%C In (1,1,%%B)Do (Call Set "separator=%%separator%%-"))&GoTo :Next
:Next
Echo Welcome to %~nx0
Echo %separator%
Pause
As a final afterthought, and just in case you feel that it would perform quicker, I thought I'd better provide a version using delayed expansion too:
#Echo Off&SetLocal EnableDelayedExpansion&Set "separator="
For /F "Skip=4Tokens=1*Delims=:" %%A In ('Mode CON')Do (For /L %%C In (1,1,%%B)Do Set "separator=!separator!-")&GoTo :Next
:Next
Rem Uncomment the next line if you don't want to use delayed expansion in the rest of the script
::EndLocal&Set "separator=%separator%"
Rem Your code goes here
Echo Welcome to %~nx0
Echo %separator%
Pause
As an addition to all of the above, you could also leverage powershell to do this too:
#Echo Off
For /F %%A In ('Powershell -NoP "Write-Host('-' * $(Get-Host).UI.RawUI.WindowSize.Width)"')Do Set "separator=%%A"
Echo Welcome to %~nx0
Echo %separator%
Pause
If you can work out the batch commands retrieve the columns value you could use the output from the MODE command;
MODE CON
C:\Users\gjp>mode con
Status for device CON:
----------------------
Lines: 9001
Columns: 120
Keyboard rate: 31
Keyboard delay: 1
Code page: 850
I would suggest you to use the following code:
#echo off
setlocal EnableDelayedExpansion
for /F "skip=4 tokens=2" %%A IN ('mode CON') do (
for /L %%B IN (1 1 %%A) do set "hyphen=!hyphen!-"
echo !hyphen!
goto :subroutine
)
:subroutine
echo You may continue here
pause
which is a bit complicated, but should do what you want.
The code searches for the columns in mode CON's command output and adds to hyphen variable these accordingly.

Batch File: Assign random line of text file as variable for later use

I'm trying to write a very simple batch file for personal use...It's complete except for one thing I'm stumped on. Hopefully this is an easy fix (I'm effectively illiterate when it comes to code).
Basically what I'm trying to do is have the script choose a random line from a text file, do this a couple times with a couple different text files, then I wish to assign the output from each text file to a variable so that I can easily use them in various combinations...then repeat the process.
Here is what I have right now...
#ECHO OFF
:START
SETLOCAL
SETLOCAL EnableDelayedExpansion EnableExtensions
SET "list1=list1.txt"
FOR /f %%a IN ('type "%list1%"^|find /c /v ""') DO SET /a numlines=%%a
SET /A list1random=(%RANDOM% %% %NumLines%)
IF "%list1random%"=="0" (SET "list1random=") ELSE (SET "list1random=skip=%list1random%")
FOR /F "usebackq tokens=* %list1random% delims=" %%A IN (`TYPE %list1%`) DO (
>> output.txt ECHO %%A
)
:Finish
ENDLOCAL
GOTO START`
This procures the random line, and spits it to a text file. All is well, next step, take that random result and assign it to a variable...
#ECHO OFF
:START
SETLOCAL
SETLOCAL EnableDelayedExpansion EnableExtensions
SET "list1=list1.txt"
FOR /f %%a IN ('type "%list1%"^|find /c /v ""') DO SET /a numlines=%%a
SET /A list1random=(%RANDOM% %% %NumLines%)
IF "%list1random%"=="0" (SET "list1random=") ELSE (SET "list1random=skip=%list1random%")
FOR /F "usebackq tokens=* %list1random% delims=" %%A IN (`TYPE %list1%`) DO (
SET output1=%%A
)
>> output.txt ECHO %output1%
:Finish
ENDLOCAL
GOTO START
Now the output ceases to be random...instead it is always the last line of the referenced text file.
EDIT: The site suggested another question that was similar to mine. However, that person was having trouble getting the script to choose a valid line. I get a valid line every time, and a random one too (when I check it via echo), but a non-random line when proceeding on, assigning the output to a variable. I don't understand because it seems like a post-facto derandomization. I.E. the difference between the two scripts has nothing to do with procuring the random result, only what to do with that result AFTER it has it, right?
I appreciate any help in advance, this is the last step before I know everything I need to finish this, I'm excited!
Sorry, you're right...anyways, I figured out a simple workaround, probably not the quickest in terms of processing time, but whatever. Basically allow the initial part of the script to spit out the random result to a text file (as seemed to work just fine) then reference the text file as a variable.
#ECHO OFF
:START
SET "list1=list1.txt"
FOR /f %%a IN ('type "%list1%"^|find /c /v ""') DO SET /a numlines=%%a
SET /A listchoice=(%RANDOM% %% %NumLines%)
IF "%listchoice%"=="0" (SET "listchoice=") ELSE (SET "listchoice=skip=%listchoice%")
FOR /F "usebackq tokens=* %listchoice% delims=" %%A IN (`TYPE %list1%`) DO (
>> listoutput.txt ECHO %%A
)
Set /p list=<listoutput.txt
>> result.txt ECHO %list%
:Finish
DEL listoutput.txt
GOTO START
This is easy to do in PowerShell using the built-in Get-Random cmdlet.
$line = (Get-Content file.txt | where { $_ } | Get-Random)
Which makes it also easy in batch.
set filename=file.txt
for /f "tokens=*" %%a in ('powershell -ex bypass -c "gc %filename% | ? { $_ } | Get-Random"') do (
set "var=%%a"
)
The where { $_ } clause is only necessary to filter out any blank lines. You can omit it if you know your file has none.

Batch output one occurrence instead of several [duplicate]

Is it possible to remove duplicate rows from a text file? If yes, how?
Sure can, but like most text file processing with batch, it is not pretty, and it is not particularly fast.
This solution ignores case when looking for duplicates, and it sorts the lines. The name of the file is passed in as the 1st and only argument to the batch script.
#echo off
setlocal disableDelayedExpansion
set "file=%~1"
set "sorted=%file%.sorted"
set "deduped=%file%.deduped"
::Define a variable containing a linefeed character
set LF=^
::The 2 blank lines above are critical, do not remove
sort "%file%" >"%sorted%"
>"%deduped%" (
set "prev="
for /f usebackq^ eol^=^%LF%%LF%^ delims^= %%A in ("%sorted%") do (
set "ln=%%A"
setlocal enableDelayedExpansion
if /i "!ln!" neq "!prev!" (
endlocal
(echo %%A)
set "prev=%%A"
) else endlocal
)
)
>nul move /y "%deduped%" "%file%"
del "%sorted%"
This solution is case sensitive and it leaves the lines in the original order (except for duplicates of course). Again the name of the file is passed in as the 1st and only argument.
#echo off
setlocal disableDelayedExpansion
set "file=%~1"
set "line=%file%.line"
set "deduped=%file%.deduped"
::Define a variable containing a linefeed character
set LF=^
::The 2 blank lines above are critical, do not remove
>"%deduped%" (
for /f usebackq^ eol^=^%LF%%LF%^ delims^= %%A in ("%file%") do (
set "ln=%%A"
setlocal enableDelayedExpansion
>"%line%" (echo !ln:\=\\!)
>nul findstr /xlg:"%line%" "%deduped%" || (echo !ln!)
endlocal
)
)
>nul move /y "%deduped%" "%file%"
2>nul del "%line%"
EDIT
Both solutions above strip blank lines. I didn't think blank lines were worth preserving when talking about distinct values.
I've modified both solutions to disable the FOR /F "EOL" option so that all non-blank lines are preserved, regardless what the 1st character is. The modified code sets the EOL option to a linefeed character.
New solution 2016-04-13: JSORT.BAT
You can use my JSORT.BAT hybrid JScript/batch utility to efficiently sort and remove duplicate lines with a simple one liner (plus a MOVE to overwrite the original file with the final result). JSORT is pure script that runs natively on any Windows machine from XP onward.
#jsort file.txt /u >file.txt.new
#move /y file.txt.new file.txt >nul
you may use uniq http://en.wikipedia.org/wiki/Uniq from UnxUtils http://sourceforge.net/projects/unxutils/
Some time ago I found an unexpectly simple solution, but this unfortunately only works on Windows 10: the sort command features some undocumented options that can be adopted:
/UNIQ[UE] to output only unique lines;
/C[ASE_SENSITIVE] to sort case-sensitively;
So use the following line of code to remove duplicate lines (remove /C to do that in a case-insensitive manner):
sort /C /UNIQUE "incoming.txt" /O "outgoing.txt"
This removes duplicate lines from the text in incoming.txt and provides the result in outgoing.txt. Regard that the original order is of course not going to be preserved (because, well, this is the main purpose of sort).
However, you sould use these options with care as there might be some (un)known issues with them, because there is possibly a good reason for them not to be documented (so far).
The Batch file below do what you want:
#echo off
setlocal EnableDelayedExpansion
set "prevLine="
for /F "delims=" %%a in (theFile.txt) do (
if "%%a" neq "!prevLine!" (
echo %%a
set "prevLine=%%a"
)
)
If you need a more efficient method, try this Batch-JScript hybrid script that is developed as a filter, that is, similar to Unix uniq program. Save it with .bat extension, like uniq.bat:
#if (#CodeSection == #Batch) #then
#CScript //nologo //E:JScript "%~F0" & goto :EOF
#end
var line, prevLine = "";
while ( ! WScript.Stdin.AtEndOfStream ) {
line = WScript.Stdin.ReadLine();
if ( line != prevLine ) {
WScript.Stdout.WriteLine(line);
prevLine = line;
}
}
Both programs were copied from this post.
set "file=%CD%\%1"
sort "%file%">"%file%.sorted"
del /q "%file%"
FOR /F "tokens=*" %%A IN (%file%.sorted) DO (
SETLOCAL EnableDelayedExpansion
if not [%%A]==[!LN!] (
set "ln=%%A"
echo %%A>>"%file%"
)
)
ENDLOCAL
del /q "%file%.sorted"
This should work exactly the same. That dbenham example seemed way too hardcore for me, so, tested my own solution. usage ex.: filedup.cmd filename.ext
Pure batch - 3 effective lines.
#ECHO OFF
SETLOCAL
:: remove variables starting $
FOR /F "delims==" %%a In ('set $ 2^>Nul') DO SET "%%a="
FOR /f "delims=" %%a IN (q34223624.txt) DO SET $%%a=Y
(FOR /F "delims=$=" %%a In ('set $ 2^>Nul') DO ECHO %%a)>u:\resultfile.txt
GOTO :EOF
Works happily if the data does not contain characters to which batch has a sensitivity.
"q34223624.txt" because question 34223624 contained this data
1.1.1.1
1.1.1.1
1.1.1.1
1.2.1.2
1.2.1.2
1.2.1.2
1.3.1.3
1.3.1.3
1.3.1.3
on which it works perfectly.
Did come across this issue and had to resolve it myself because the use was particulate to my need.
I needed to find duplicate URL's and order of lines was relevant so it needed to be preserved. The lines of text should not contain any double quotes, should not be very long and sorting cannot be used.
Thus I did this:
setlocal enabledelayedexpansion
type nul>unique.txt
for /F "tokens=*" %%i in (list.txt) do (
find "%%i" unique.txt 1>nul
if !errorlevel! NEQ 0 (
echo %%i>>unique.txt
)
)
Auxiliary: if the text does contain double quotes then the FIND needs to use a filtered set variable as described in this post: Escape double quotes in parameter
So instead of:
find "%%i" unique.txt 1>nul
it would be more like:
set test=%%i
set test=!test:"=""!
find "!test!" unique.txt 1>nul
Thus find will look like find """what""" file and %%i will be unchanged.
I have used a fake "array" to accomplish this
#echo off
:: filter out all duplicate ip addresses
REM you file would take place of %1
set file=%1%
if [%1]==[] goto :EOF
setlocal EnableDelayedExpansion
set size=0
set cond=false
set max=0
for /F %%a IN ('type %file%') do (
if [!size!]==[0] (
set cond=true
set /a size="size+1"
set arr[!size!]=%%a
) ELSE (
call :inner
if [!cond!]==[true] (
set /a size="size+1"
set arr[!size!]=%%a&& ECHO > NUL
)
)
)
break> %file%
:: destroys old output
for /L %%b in (1,1,!size!) do echo !arr[%%b]!>> %file%
endlocal
goto :eof
:inner
for /L %%b in (1,1,!size!) do (
if "%%a" neq "!arr[%%b]!" (set cond=true) ELSE (set cond=false&&goto :break)
)
:break
the use of the label for the inner loop is something specific to cmd.exe and is the only way I have been successful nesting for loops within each other. Basically this compares each new value that is being passed as a delimiter and if there is no match then the program will add the value into memory. When it is done it will destroy the target files contents and replace them with the unique strings

Remove a number of delimiters from each line of file

I have a file which I need to load into a database. It has a delimiter of pipe (|) however each line contains different number of pipes. Using a batch script, how can I remove pipes from each line so the same number of pipes are on each line?
Example of file:
1|2|3||||||
4|5|6|||
7|8||||||
Let's say I'd like 5 pipes on each line only so it looks like:
1|2|3|||
4|5|6|||
7|8||||
Update See second solution and limitation updates.
Example file.txt contents
A|B|C|D|E|F|G
1|2|3|4|5|6|7
!|#|#|$|%|^|&
]1|]2|]3|]4|]5|]6|]7
|Two||Four||||Eight
!#$%^&%^*(){}|[]';/.,<>/|
Lonely||||||||||||||||||
Sep|er|ate| From| Th|e |W||orld | |
First Solution
Here is a simple way to do what you want. It should not have any problems with special characters.
Limitations
It only supports up to 24 25 columns as it is currently written. %%A to %%Y
The first value may not begin with ]. Replaced for /F "tokens=1,* delims=]" %%Y in ('type file.txt ^| find /v /n ""') do ( with for /F "delims=" %%Z in ('type file.txt') do (.
"Empty" fields may only appear at the end of every line. See second solution.
Does not preserve blank lines in the file. (This can be fixed if desired.)
Just specify how many and which columns you want to keep. For example tokens=3-5,12,48-50 will select only columns 3,4,5,12,48,49,50. Make sure you add on or remove the variables to match the output you want. echo %%A^|%%B^|%%D^|%%C^|%%G^|%%E^|%%F. Note that the columns can be reordered as well in the echo statement.
#echo off
setlocal DisableDelayedExpansion
for /F "delims=" %%Z in ('type file.txt') do (
for /F "tokens=1-5 delims=|" %%A in ("%%Z") do (
echo %%A^|%%B^|%%C^|%%D^|%%E
)
)
endlocal
pause >nul
You can either redirect the output of the .bat file into a new file Script.bat>output.txt or output the echo command to a file by appending >>output.txt to the echo line.
Example Output:
A|B|C|D|E
1|2|3|4|5
!|#|#|$|%
]1|]2|]3|]4|]5
Two|Four|Eight|| <-- Note that this line exhibits limit 3.
!#$%^&%^*(){}|[]';/.,<>/|||
Lonely||||
Sep|er|ate| From| Th
Second Solution
Shares only limitations 1 and 4. Currently adds spaces into existing blank columns to preserve all columns. They can be removed with a further code change, but will not add unless desired by the OP.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "delims=" %%Z in ('type file.txt') do (
set "xLine=|%%Z"
call :Parse xLine
)
endlocal
pause >nul
goto :eof
:Parse
call set "xLine=%%%~1:||=| |%%"
for /F "tokens=1-5 delims=|" %%A in ("%xLine%") do (
echo %%A^|%%B^|%%C^|%%D^|%%E
)
goto :eof
Example Output:
A|B|C|D|E
1|2|3|4|5
!|#|#|$|%
]1|]2|]3|]4|]5
|Two| |Four|
!#$%^&%^*(){}|[]';/.,<>/|||
Lonely| | | |
Sep|er|ate| From| Th
There is no direct way to achieve this process, so each character must be revised in order to count the number of pipes in each line. It works, but it is somewhat slow.
#echo off
setlocal EnableDelayedExpansion
rem Number of desired pipes
set limit=5
for /F "delims=" %%a in (input.txt) do (
set "line=%%a"
rem Get position of last character
set last=0
for /L %%b in (12,-1,0) do (
set /A "last|=1<<%%b"
for %%c in (!last!) do if "!line:~%%c,1!" equ "" set /A "last&=~1<<%%b"
)
rem Copy each character to result, but just %limit% number of pipes
set pipes=0
set result=
for /L %%c in (0,1,!last!) do (
if "!line:~%%c,1!" neq "|" (
set "result=!result!!line:~%%c,1!"
) else (
set /A pipes+=1
if !pipes! leq %limit% set "result=!result!|"
)
)
echo !result!
)
Previous program will fail if the input line contain exclamation marks.
Output:
1|2|3|||
4|5|6|||
7|8||||
Antonio

Batch script to merge lines from two files into a third file

Contents of file A1:
AA
VV
BB
Contents of file A2:
DD
EE
FF
I want to merge the contents of A1 and A2 as below into A3, so that the expected data in A3 is:
AADD
VVEE
BBFF
Alternatively, the expected output in A3 may be:
AA is from DD
VV is from EE
BB is from FF
Thanks for the help. I did try and search before I posted and could not find someone that has already posted something similar...
We can load the contents of the files into Batch variable arrays so each of its lines can be directly accessed in any way you wish:
#echo off
setlocal EnableDelayedExpansion
rem Load first file into A1 array:
set i=0
for /F "delims=" %%a in (A1.txt) do (
set /A i+=1
set A1[!i!]=%%a
)
rem Load second file into A2 array:
set i=0
for /F "delims=" %%a in (A2.txt) do (
set /A i+=1
set A2[!i!]=%%a
)
rem At this point, the number of lines is in %i% variable
rem Merge data from both files and create the third one:
for /L %%i in (1,1,%i%) do echo !A1[%%i]! is from !A2[%%i]!>> A3.txt
EDIT Alternative solution
There is another way to do it that don't use Batch variables so it can be used on files of any size, although it is slower. I borrowed the method used by Andy Morris in its solution: 1- Insert line numbers in both files, 2- Combine both files in one, 3- Sort the combined file, and 4- Merge groups of lines into one same line. The program below is basically Andy's one with several small modifications that made it faster (with a subtle error fixed).
#echo off
setlocal EnableDelayedExpansion
call :AddLineNumbers A1.txt A > Both.txt
call :AddLineNumbers A2.txt B >> Both.txt
sort Both.txt /O Sorted.txt
echo EOF: >> Sorted.txt
call :creatNewLines < Sorted.txt > Result.txt
goto :eof
:AddLineNumbers
findstr /n ^^ %1 > tem.tmp
for /f "tokens=1* delims=:" %%a in (tem.tmp) do (
set /a lineNo=1000000+%%a
echo !lineNo!%2:%%b
)
goto :eof
:creatNewLines
set /p lineA1=
for /f "tokens=1* delims=:" %%a in ("%lineA1%") do (
if %%a == EOF goto :eof
set /p dummy=%%b< nul
)
set /p lineA2=
for /f "tokens=1* delims=:" %%a in ("%lineA2%") do echo is from %%b
goto creatNewLines
SORT command order lines based on its contents. Andy's original method may fail because after the line number the lines are ordered based on line contents, so the lines of each file may be misplaced. In this method an additional character (A or B) is added after the line number, so the lines of each file are always placed in the right place.
If your original data is in Data1.txt and Data2.txt this should do:
#echo off
call :AddLineNumbers data1.txt Tem1.txt
call :AddLineNumbers data2.txt Tem2.txt
copy tem1.txt + tem2.txt tem3.txt
sort < tem3.txt > tem4.txt
call :GetDataOut tem4.txt > tem5.txt
set OddData=
for /f %%a in (tem5.txt) do call :creatNewLines %%a
goto :eof
:AddLineNumbers
find /v /n "xx!!xx" < %1 > tem.txt
call :ProcessLines > %2
goto :eof
:ProcessLines
for /f "tokens=1,2 delims=[]" %%a in (tem.txt) do call :EachLine %%a %%b
goto :eof
:eachLine
set LineNo=00000%1
set data=%2
set LineNo=%LineNo:~-6%
echo %LineNo% %data%
goto :eof
:GetDataOut
for /f "tokens=2" %%a in (%1) do #echo %%a
goto :eof
:creatNewLines
if "%oddData%"=="" (
set oddData=%1
) else (
echo %oddData% %1
set oddData=
)
goto :eof
If using linux, I would recommend using cut and paste (command line). See the man pages.
Alternatively, if you don't need the automation, you could use vim block mode cut and paste. Enter block mode visual mode with control-v.

Resources