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'),
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!
In CMD the following variable will give you the name of the computer: %COMPUTERNAME%
I need a variable that takes a part of the computername.
I need a if statement that checks if the computername contains "KM" at the start and 00 at the end. It should not look at the number between KM and -00
KM100-00
KM200-00
This works here:
echo %computername%| findstr "^KM.*00$" >nul && echo found the right format
You can do this with substring commands, as per the following transcript:
pax> set xyzzy=KM100-00 KM200-00
pax> echo %xyzzy%
KM100-00 KM200-00
pax> echo %xyzzy:~0,2%
KM
pax> echo %xyzzy:~-2,2%
00
pax> if %xyzzy:~0,2%==KM if %xyzzy:~-2,2%==00 echo yes
yes
That final (chained) if statement is the one you're looking for to see if your variable starts with KM and ends with 00.
The expression %X:~Y,Z% will give you the Z characters starting at position Y (zero-based) of the variable X. You can provide a negative value of Y to make it relative to the end of the string.
echo %computername%| findstr /I /b "KM" | findstr /i /e "00" && echo computer name is like KM-XX-00
You can try also with hostname instead of echo %computername%
I recommend you to read this page, which is about substring usage in command prompt.
And why dont you try this;
set str=KM2000-00
echo.%str%
set pre=%str:~0,2%
echo.%pre%
set pst=%str:~-2%
echo.%pst%
IF %pre% == KM( IF %pst% == 00( echo.true ) )
pause
Is anyone aware of a library suitable for writing an image in .TMB format?
The .TMB format is suitable for printing logos from a Epson thermal receipt printer.
After about an hour or so of looking at binary data, I came to the following conclusion:
A *.TMB image is really just a serialized ESC/POS command to print a raster image.
Using the following command:
od -t a -v [YOUR_TMB_FILE] | head
we can view the binary data, as ASCII character data, in the beginning of the TMB file.
I had a file that looked something like this:
0000000 gs v 0 nul 5 nul P nul del del del del del del del del
0000020 del del del del del del del del del del del del del del del del
... snipped for brevity ...
According to the ESC/POS Programming Guide, the ASCII command to print a raster image is:
GS V 0
Hmm.. Interesting!
On a whim, I decided to convert 5 and P to their decimal equivalents, which are 53 and 80 respectively, the exact dimensions of my .TMB image (actually, its 80x53)!
Everything fell into place after this. The remainder of a .TMB file is just the binary image data.
Here is a one-off Python script I wrote to test my theory:
1 out = open('test.TMB', 'wb')
2
3 width = 80
4 height = 53
5
6 NUL = chr(0)
7 GS = chr(29)
8 V = chr(118)
9 ZERO = chr(48)
10
11 W = chr(width)
12 H = chr(height)
13
14 out.write(GS)
15 out.write(V)
16 out.write(ZERO)
17 out.write(NUL)
18
19 out.write(H)
20 out.write(NUL)
21 out.write(W)
22 out.write(NUL)
23
24 for y in range(0, height):
25 for x in range(0, width):
26 out.write(chr(127)) # looks like `del` in ASCII mode
27
28 out.close()
In the windows shell:
echo "foo bar" | find "foo"
succeeds (i.e. errorlevel = 0). However, I want a script that fails (i.e. errorlevel <> 0) when it finds a particular word in some input text. Any ideas?
A lttle trickery can emulate what you desire. Only the first three lines are required, the rest are just a test.
c:> echo "foo bar" | find "foo"
c:> if x%errorlevel%==x0 echo 1 | find "2"
c:> if not x%errorlevel%==x0 echo 1 | find "1" >nul 2>nul
c:> echo %errorlevel%
1