Batch file doesn't insert text from another file - windows

I am working in Windows 10 and I have text files resulting from another process that I want to edit using a batch file. I've shortened the example, File-1 (shown below with line numbers) for this post. They are all formatted this way, but much longer and with more sections:
1 Album 1 - 1982,,,
2 ('Song 1'),
3 ('Song 2'),
4 |===================|
5 Album 2 - 1978,,,
6 ('Song 1'),
7 ('Song 2'),
After each line with a string of 3 commas (lines 1 and 5) I want to insert two new lines of text from File-2 before the existing lines in File-1 (lines 2 and 6).
The two lines in File-2 to insert are:
NEW LINE 1
NEW LINE 2
The desired output would then be:
1 Album 1 - 1982,,,
2 NEW LINE 1
3 NEW LINE 2
4 ('Song 1'),
5 ('Song 2'),
6 |===================|
7 Album 2 - 1978,,,
8 NEW LINE 1
9 NEW LINE 2
10 ('Song 1'),
11 ('Song 2'),
The closest I've come so far as an experiment is a batch file (that I found and adapted) that finds the 3 commas and replaces them with 3 dots.
#echo off &setlocal
set "search=,,,"
set "replace=..."
(for /f "delims=" %%i in (a.txt) do (
set "word=%%i"
setlocal enabledelayedexpansion
set "word=!word:%search%=%replace%!"
echo(!word!
endlocal
)
)
Output of the adapted batch file to the screen:
1 Album 1 - 1982...
2 ('Song 1'),
3 ('Song 2'),
4 |===================|
5 Album 2 - 1978...
6 ('Song 1'),
7 ('Song 2'),
I don't need to change the commas to dots (that was just a test to see if I could make the batch file stop and do something at each instance of a unique string).
My dilemma is that I have searched for hours and can't find how to insert File-2 text in the two proper places of File-1. I've found many explanations on how to insert another file's text at one point or at the end of a first file (using the "type" command), but not at more than one specific point found from the search. Also, File-2 text might be more than two lines in the future, so that is why I want to use an external file to hold it.
All I really want to do right now is:
Find a known string (3 commas).
Read boilerplate text from a text file and insert it below the lines with the found strings, into the the file sitting in the batch file memory.
For testing, I am echoing this to the screen, but once I am able to get the right output, I can edit the batch file to redirect output to a file. I know there are easier ways of doing this using python or C, but it seems it should be easy to do this way with a small correction.
UPDATE
I corrected my batch file per #aschipfl, and made new input files for easier debugging.
a.txt:
A LINE 1,,,
A LINE 2
b.txt:
B LINE 1
B LINE 2
I methodically tried three versions of the FIND statement starting with the #aschipfl original but there is no interaction with b.txt.
if not "!word!"=="!word:%search%=!" find /V ",,," < "!infile2!"
if not "!word!"=="!word:%search%=!" < "!infile2!" find /V ",,,"
if not "!word!"=="!word:%search%=!" find /V ",,," type "!infile2!"
The fourth combination does interact with b.txt as shown below it.
if not "!word!"=="!word:%search%=!" type "!infile2!" find /V ",,,"
C:\Users\Pete\Documents\test>test.bat
A LINE 1,,,
b.txt
B LINE 1
B LINE 2The system cannot find the file specified.
Error occurred while processing: find.
The system cannot find the file specified.
Error occurred while processing: /V.
The system cannot find the file specified.
Error occurred while processing: ,,,.
A LINE 2
C:\Users\Pete\Documents\test>
Now, the batch file reads both lines of both input files but with errors as shown (complete with blank lines, copied directly from screen).

You should not try to replace ,,, by ,,, + line-break + multi-line text from another file, rather should you, after echo(!word!, check whether the current line contains ,,, by if not "!word!"=="!word:,,,=!", and if so, just type out the other text file by type "File-2.txt", or by find /V "" < "File-2.txt" in case its contents may not be terminated with a final line-break:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "infile1=%~dp0File-1.txt"
set "infile2=%~dp0File-2.txt"
set "outfile=con"
set "search=,,,"
> "%outfile%" (
rem // The strange unquoted option string syntax disables the `eol` character:
for /F usebackq^ delims^=^ eol^= %%i in ("%infile1%") do (
set "word=%%i"
setlocal EnableDelayedExpansion
echo(!word!
rem // Try to remove search string; if result differs, search string occurred:
if not "!word!"=="!word:%search%=!" find /V "" < "!infile2!"
endlocal
)
)
endlocal
exit /B

One way to do this in a cmd batch-file would be to use PowerShell. If you are on a supported Windows platform, PowerShell is available. This uses a regex to insert the content of another file.
powershell -NoLogo -NoProfile -Command ^
(Get-Content -Path .\afile1.txt) -replace ',,,', ^
\",,,$([Environment]::NewLine)$(Get-Content -Raw -Path .\afile2.txt)\"
Example:
C:>type afile1.txt
1 Album 1 - 1982,,,
2 ('Song 1'),
3 ('Song 2'),
4 |===================|
5 Album 2 - 1978,,,
6 ('Song 1'),
7 ('Song 2'),
C:>type afile2.txt
NEW LINE 1
NEW LINE 2
C:>powershell -NoLogo -NoProfile -Command ^
More? (Get-Content -Path .\afile1.txt) -replace ',,,', ^
More? \",,,$([Environment]::NewLine)$(Get-Content -Raw -Path .\afile2.txt)\"
1 Album 1 - 1982,,,
NEW LINE 1
NEW LINE 2
2 ('Song 1'),
3 ('Song 2'),
4 |===================|
5 Album 2 - 1978,,,
NEW LINE 1
NEW LINE 2
6 ('Song 1'),
7 ('Song 2'),

Related

Dynamic variable syntax with delayed expansion

In an attempt to reuse code I am trying to use dynamic variables to test a condition but am unable to get the result I need. I'm using delayed expansion.
1 Outside the for loop:
2 set "H_HEADER=FALSE"
3 set "SUB_TRANSTYPE=#"
4
5 Inside the for loop:
6 set "SUB_TRANSTYPE=!FULL_LINE:~0,1!"
7 if !SUB_TRANSTYPE!==H (
8 echo sub_transtype_header is !!SUB_TRANSTYPE!_HEADER!
9 )
Line 6 sets SUB_TRANSTYPE to H
Line 8 prints H_HEADER to console but I want it to print FALSE (the value of H_HEADER)
I've messed around with escape characters but can't get this to work. Help!
Line 6 should be:
set "SUB_TRANSTYPE=!FULL_LINE:~0,1!"
Line 7 should be:
if !SUB_TRANSTYPE!==H (
Line 8 should be:
for /F %%A in ("!SUB_TRANSTYPE!") do echo sub_transtype_header is !%%A_HEADER!
This type of management is fully described at this answer.
if !SUB_TRANSTYPE!==H (
CALL echo sub_transtype_header is %%!SUB_TRANSTYPE!_HEADER%%
)
note that you have a = missing from the == operator.
This executes the echo in a subshell.
To interpret the value in an if statement, use
call set "someothervariable=%%!SUB_TRANSTYPE!_HEADER%%"
if "!someothervariable!"=="value" (

How to remove comments from text file

My text file contains one line comments that all being with "// ". Two forward slashes and a space. These may either take up the whole line or just the last part of a line. Each comment does not extend beyond the line that it's on. So no /* */ type comments crossing multiple lines.
In simple terms, all comments start with "//space" anywhere on the line. Anything starting with "//space" should be removed and trailing spaces on that line should also be removed. Leading spaces should stay. Any blank lines should be removed.
Sample file:
// This is a comment
x = 1 // This is also a comment after the double slash
x = 2
x = 3 // The above is a blank line
// Comment on this record but nothing precedes it, so should be deleted.
y = 4 // A line with leading spaces that should be kept.
z = "//path"; // The first double slashes are not a comment since the space is missing after the "//"
// Last comment line.
Result file (no trailing spaces, but keep leading spaces.:
x = 1
x = 2
x = 3
y = 4
z = "//path";
I can remove the blank lines using gc file.txt | Where-Object { $_ -ne ''} > result.txt. However I'm having trouble with reading just the beginning part of a line up to the "//" comment part.
I also tried findstr but haven't found how to read each line up to the "//" and then trim spaces out.
I could write a script program to loop throught the file and do this, but it seems like there should be a way to accomplish it using a simple one or two line powershell or bat file command.
What is the easiest way (shortest amount of code) to remove these comments while keeping the uncommented contents of the file?
Since you seem to equate "easy" with "short", here's a fairly simple solution:
gc .\samplefile.txt|%{$_-replace"(.*)(// .*)",'$1'}|?{$_}
if it's really that important to you :-)
A bit more verbose version (still using regex):
Get-Content .\samplefile.txt | Where-Object {
-not ([String]::IsNullOrEmpty($_.Trim()) -or $_-match"^\s*// ")
} |ForEach-Object { $_ -replace "(.*)(// .*)",'$1' }
That being said, I would (personally) go for a more verbose and easier-to-read/maintain solution:
To remove everything after //, the easiest way is to find the first occurrence of // with String.IndexOf() and then grab the first part with String.Substring():
PS C:\> $CommentedString = "Content // this is a comment"
PS C:\> $CommentIndex = $CommentedString.IndexOf('// ')
PS C:\> $CommentedString.Substring(0,$CommentIndex)
Content
For the indented comments you can also use String.Trim() to remove whitespace from the beginning and end of the string:
PS C:\> " // Indented comment" -match '^//'
True
You can use the ForEach-Object cmdlet to go through every line and apply the above:
function Remove-Comments {
param(
[string]$Path,
[string]$OutFile
)
# Read file, remove comments and blank lines
$CleanLines = Get-Content $Path |ForEach-Object {
$Line = $_
# Trim() removes whitespace from both ends of string
$TrimmedLine = $Line.Trim()
# Check if what's left is either nothing or a comment
if([string]::IsNullOrEmpty($TrimmedLine) -or $TrimmedLine -match "^// ") {
# if so, return nothing (inside foreach-object "return" acts like "coninue")
return
}
# See if non-empty line contains comment
$CommentIndex = $Line.IndexOf("// ")
if($CommentIndex -ge 0) {
# if so, remove the comment
$Line = $Line.Substring(0,$CommentIndex)
}
# return $Line to $CleanLines
return $Line
}
if($OutFile -and (Test-Path $OutFile)){
[System.IO.File]::WriteAllLines($OutFile, $CleanLines)
} else {
# No OutFile was specified, write lines to pipeline
Write-Output $CleanLines
}
}
Applied to your sample:
PS C:\> Remove-Comments D:\samplefile.txt
x = 1
x = 2
x = 3
Like a great many text processing problems, there is a simple solution using JREPL.BAT - a powerful regex text processing utility for the Windows command line. It is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward. Full documentation is embedded within the script.
jrepl "^(.*?)\s*// " "$1!=''?$1:false" /jmatch /f test.txt /o out.txt
You can overwrite the original file by specifying - as the output file:
jrepl "^(.*?)\s*// " "$1!=''?$1:false" /jmatch /f test.txt /o -
I've tested, and it gives the exact output you are looking for.
If you put the command within a batch script, then you must use call jrepl
Tha Batch file below do what you want. Sorry, but there is not an "easy short code" way to do this...
#echo off
setlocal EnableDelayedExpansion
rem Set the maximum number of trailing spaces as a power_of_2-1 value. For example, for 15 spaces:
set spcPow2=4
set "spaces= "
for /L %%i in (1,1,%spcPow2%) do set "spaces=!spaces!!spaces!"
set /A spcPow2-=1
rem Process all lines, excepting empty ones and lines that start with "/"
setlocal DisableDelayedExpansion
for /F "eol=/ delims=" %%a in (test.txt) do (
set "line=%%a"
rem Split line at "// " and get the first part
setlocal EnableDelayedExpansion
for /F "delims=¡" %%b in ("!line:// =¡!") do (
endlocal
set "line=%%b"
)
rem Eliminate trailing spaces
setlocal EnableDelayedExpansion
set spc=0
for /L %%b in (%spcPow2%,-1,0) do (
set /A "newSpc=spc+(1<<%%b)"
for %%n in (!newSpc!) do if "!line:~-%%n!" equ "!spaces:~-%%n!" set "spc=%%n"
)
if !spc! gtr 0 for %%n in (!spc!) do set "line=!line:~0,-%%n!"
rem Show resulting line
if defined line echo !line!
endlocal
)
EDIT: New solution added
#set #x=1 // & CScript //nologo //E:JScript "%~F0" < samplefile.txt & goto :EOF
WScript.Stdout.Write(WScript.Stdin.ReadAll().replace(/(.*)\/\/ .*/g,"$1"))
Copy previous code into a file with .BAT extension, that is, it is a Batch file!

How to merge files horizontally in Windows 7 command prompt?

I have three files in a directory
File 1:
a b
c d
File 2:
1 2
3 4
File 3:
e f
g h
I know that in windows command prompt, when I type "copy * new.txt", I get a file called new.txt which looks like the following.
a b
c d
1 2
3 4
e f
g h
In command prompt, how would I combine the files horizontally, so I get the following for my combined file?
a b 1 2 e f
c d 3 4 g h
You can install some proper (Unix/Linux) tools from here and do it like this:
paste -d" " file1 file2 file3
a b 1 2 e f
c d 3 4 g h
#echo off
setlocal EnableDelayedExpansion
3< File2.txt 4< File3.txt (
for /F "delims=" %%a in (File1.txt) do (
set "line1=%%a"
set /P "line2=" <&3
set /P "line3=" <&4
echo !line1! !line2! !line3!
)
)
Further details at this site.
You should use the batch-file tag for any "command prompt" related question.
You should upvote and select answers that had been useful to you, otherwise the people may refuse to answer your future questions.

windows batch file change blank field to value

I am writing a windows script to read in a txt file with 9 columns of data
I want to write this data out to a csv file to be loaded into a database.
I have found that one column is sometimes blank, so I would like to enter code 'rdp'
another column has mostly numbers, but for reason some values are a '.' (presumably indicating value less than 1. I would like to change these values to '0'
my code
for /F %%b in (c:\ts_users\newfiles_list.txt) do (
for /F "tokens=1,2,3,4,5,6,7,8,9" %%i in (%%b) do (
echo %%i,%%j,%%k,%%l,%%m,!idle!,%%o %%p,%%q >>%%~nb.csv
)
)
this manages to read in the txt file, then write it out as a csv.
column k is sometimes empty, column n sometimes has a '.'
I have tried variations of
set var=rdp
if %%k="" then set !k:=!var!
which doesnt work, so I a little stumped (after googling internet for days)
current input
201401241611 USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME eu1ptsw002
201401241611 julie.noble rdp-tcp#9 3 Active 18 24/01/2014 14:24 eu1ptsw002
201401241611 svc.perfmon18 7 Disc 3 24/01/2014 14:15 eu1ptsw002
201401241611 reto.hofstetter 10 Disc 40 24/01/2014 10:57 eu1ptsw002
201401241611 lester.valentin 14 Disc 1:16 24/01/2014 11:53 eu1ptsw002
201401241611 philippe.bachmann 15 Disc 2 24/01/2014 12:45 eu1ptsw002
201401241611 patrik.soderlund rdp-tcp#2 21 Active 24 24/01/2014 07:42 eu1ptsw002
current output
201401241611,USERNAME,SESSIONNAME,ID,STATE,,TIME LOGON,TIME
201401241611,julie.noble,rdp-tcp#9,3,Active,,24/01/2014 14:24,eu1ptsw002
201401241611,svc.perfmon18,7,Disc,3,,14:15 eu1ptsw002,
201401241611,reto.hofstetter,10,Disc,40,,10:57 eu1ptsw002,
201401241611,lester.valentin,14,Disc,1:16,,11:53 eu1ptsw002,
201401241611,philippe.bachmann,15,Disc,2,,12:45 eu1ptsw002,
201401241611,patrik.soderlund,rdp-tcp#2,21,Active,,24/01/2014 07:42,eu1ptsw002
required output
201401241611,USERNAME,SESSIONNAME,ID,STATE,,TIME LOGON,TIME
201401241611,julie.noble,rdp-tcp#9,3,Active,18,24/01/2014 14:24,eu1ptsw002
201401241611,svc.perfmon18,rdp,7,Disc,3,24/01/2014 14:15,eu1ptsw002
201401241611,reto.hofstetter,rdp,10,Disc,40,24/01/2014 10:57,eu1ptsw002,
201401241611,lester.valentin,rdp,14,Disc,1:16,24/01/2014 11:53,eu1ptsw002
201401241611,philippe.bachmann,rdp,15,Disc,2,24/01/2014 12:45,eu1ptsw002
201401241611,patrik.soderlund,rdp-tcp#2,21,Active,24,24/01/2014 07:42,eu1ptsw002
#echo off &setlocal disableDelayedExpansion
for /f "delims=" %%a in (file) do (
set "line=%%~a"
setlocal enabledelayedexpansion
set "line=!line:,.,=,0,!"
set "line=!line:,,=,rdp,!"
echo(!line!
endlocal
)

windows batch file script to sort URLs

How do I write a windows batch script to sort URLs by grouping those with unique file names together in a text file? I don't know how to describe further what I want to achieve but I hope the example below explains everything:
I want this text
http://example.com/5235/Guava.jpg
http://example.com/2725/Guava.jpg
http://example.com/4627/Guava.jpg
http://example.com/8385/Guava.jpg
http://example.com/3886/Lemon.jpg
http://example.com/5896/Lemon.jpg
http://example.com/2788/Lemon.jpg
http://example.com/1758/Lemon.jpg
http://example.com/1788/Apple.jpg
http://example.com/1567/Apple.jpg
http://example.com/8065/Apple.jpg
http://example.com/6467/Apple.jpg
http://example.com/1464/Banana.jpg
http://example.com/6581/Banana.jpg
http://example.com/4642/Banana.jpg
http://example.com/8635/Banana.jpg
http://example.com/2578/Pineapple.jpg
http://example.com/1452/Pineapple.jpg
http://example.com/8652/Pineapple.jpg
http://example.com/9463/Pineapple.jpg
http://example.com/9765/Peach.jpg
http://example.com/3578/Peach.jpg
http://example.com/3583/Peach.jpg
http://example.com/9467/Peach.jpg
http://example.com/3683/Mango.jpg
http://example.com/3479/Mango.jpg
http://example.com/1795/Mango.jpg
http://example.com/7345/Mango.jpg
sorted this way
http://example.com/5235/Guava.jpg
http://example.com/3886/Lemon.jpg
http://example.com/1788/Apple.jpg
http://example.com/1464/Banana.jpg
http://example.com/2578/Pineapple.jpg
http://example.com/9765/Peach.jpg
http://example.com/3683/Mango.jpg
http://example.com/2725/Guava.jpg
http://example.com/5896/Lemon.jpg
http://example.com/1567/Apple.jpg
http://example.com/6581/Banana.jpg
http://example.com/1452/Pineapple.jpg
http://example.com/3578/Peach.jpg
http://example.com/3479/Mango.jpg
http://example.com/4627/Guava.jpg
http://example.com/2788/Lemon.jpg
http://example.com/8065/Apple.jpg
http://example.com/4642/Banana.jpg
http://example.com/8652/Pineapple.jpg
http://example.com/3583/Peach.jpg
http://example.com/1795/Mango.jpg
http://example.com/8385/Guava.jpg
http://example.com/1758/Lemon.jpg
http://example.com/6467/Apple.jpg
http://example.com/8635/Banana.jpg
http://example.com/9463/Pineapple.jpg
http://example.com/9467/Peach.jpg
http://example.com/7345/Mango.jpg
In other words, for this particular example (withe four of each fruit jpeg) I want to sort lines according to this manner: 1, 5, 9, 13, 17, 21, 25, 2, 6, 10, 14, 18, 22, 26, and so on. I hope you get what I mean.
The text file always contains urls with the same number of every "fruit" picture. There can't be six lemon jpg files and four guava jpg files. I hope you get what I what I mean.
Maybe something like this:
#ECHO OFF
SET origfile=urls.txt
SET c=1
SET skip=4
FOR /L %%c IN (1,1,%skip%) DO IF EXIST %origfile%.%%c DEL %origfile%.%%c
FOR /F "tokens=*" %%L IN (%origfile%) DO CALL :process "%%L"
DEL %origfile%
FOR /L %%c IN (1,1,%skip%) DO (
TYPE %origfile%.%%c >> %origfile%
DEL %origfile%.%%c
)
GOTO :EOF
:process
ECHO %~1>>%origfile%.%c%
SET /A c=c%%skip+1
The idea is to output subsequent lines to different files, repeating the sequence every 4 lines (and 4 is parametrised here actually, so you can easily change it), then concatenate those files under the original name.
Run this on your file. Algorithm as described in my comment above.
#!/bin/bash
FILE=$1
FIRST=$(head -1 $FILE)
COUNT=$(grep $FIRST $FILE | wc -l)
LINES=$(uniq $FILE)
for i in $(seq 1 $COUNT); do
echo $LINES | tr " " "\n"
done
You can tell sort where to start comparing:
/+n Specifies the character number, n, to
begin each comparison. /+3 indicates that
each comparison should begin at the 3rd
character in each line. Lines with fewer
than n characters collate before other lines.
By default comparisons start at the first
So if your URI prefix is always the same (which your comments indicated) you can just run the file through
sort /+25 list.txt /O:list_new.txt
which should sort it by file name, then.

Resources