How to insert line breaks on existing text file? - batch-file

I have an existing text file that is one long string. I would like to create a .bat script to insert a carriage return and line-feed after it finds ~.
For example, the original text file is:
This is a long string~which should be many lines~and yet it is not
The wanted output is:
This is a long string~
which should be many lines~
and yet it is not

I am not really sure how to do tilde replacement within a batch file because the tilde is a special character within the SET command for substrings.
But this should get you headed in the right direction.
#echo off
set "longline=This is a long string~which should be many lines~and yet it is not"
set count=1
:loop
FOR /F "tokens=1* delims=~" %%G IN ("%longline%") DO (
SET "line%count%=%%G"
set "longline=%%H"
IF DEFINED longline (set /a count+=1 &goto loop)
)
FOR /L %%I IN (1,1,%count%) DO call echo %%line%%I%%
pause

I suppose you could also utilise PowerShell from your batch file too:
#If "%~1"=="" (Exit/B) Else If Not Exist "%~1" Exit/B
#Powershell -C "(GC '%~1') -Replace '~',\"`r`n\"|SC '%~1'"
The above accepts your input file as its argument, which means it could be as simple as a drag and drop job. The output file will be ASCII encoded by default.

First let me explain the three different types of line break/newline/line ending/line termination types.
There is carriage return with the escape sequence \r with hexadecimal code value 0D abbreviated with CR and line-feed with the escape sequence \n with hexadecimal code value 0A abbreviated with LF.
Text files on MS-DOS/Windows use CR+LF as newline.
Text files on Unix/Linux/MAC (since OS X) use just LF as newline.
Text files on MAC before OS X use just CR as newline.
So I suppose in real the task is to insert after tilde not just a carriage return, but a carriage return + line-feed.
The answers on How can you find and replace text in a file using the Windows command-line environment? offer many solutions for replacing strings in text files using Windows command line.
The first suggested solution is with using JREPL.BAT written by Dave Benham.
jrepl.bat "~" "~\r\n" /X /F "FileToModify.txt" /O -
This solution works for a text file containing the posted line and produce the expected output.

Related

Execute WMIC command in nested for-loop

this is my first time creating a batch file and I'm relatively new to this coding language. I wish to create a batch file that scans through all DLL files within a folder to retrieve and write the version info into a new text file. The nested for-loop functioned well but I couldn't get the WMIC command to execute, can you help?
Upon double-clicking to run the batch file, this is what I will get:
Description = Invalid query
'WMIC DataFile Where "Name='mydirecotry\myfile.dll'" Get Version'
Node - myPCname
Here are the codes I wrote:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%I in (D:\mydirecotry\*.dll*) DO (
SET Location='WMIC DataFile Where "Name='%%I'" Get Version'
ECHO !Location!
For /F "Tokens=1* Delims=" %%A In (!Location!) Do ( <---Where i think it went wrong
For /F "Tokens=*" %%B In ("%%~A") Do Set "pver=%%B"
ECHO %%I - %pver% >> test.txt
)
)
pause
Thanks in advance.
As the majority of my earlier comment is already within a supplied answer, I have decided to provide the alternative method I used within that comment, as an answer.
For the task you've laid out in your question, you should be able to have a single line batch-file, with no set or for commands, just containing:
#"%__AppDir__%wbem\WMIC.exe" /Output:"test.txt" DataFile Where "Drive='D:' And Path='\\mydirecotry\\' And Extension='dll'" Get Name, Version
The DLL version information collection task can be done with following batch file:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
(for %%I in ("D:\V4-x64\Exe\Bin\*.dll*") do (
set "FileName=%%I"
set "FileName=!FileName:\=\\!"
for /F "skip=1" %%J In ('%SystemRoot%\System32\wbem\wmic.exe DATAFILE where Name^="!FileName!" GET Version 2^>nul') do if defined FileName set "FileName=" & echo %%I - %%J
)) >test.txt
endlocal
Each backslash in full qualified file name must be escaped with one more backslash. That is a special WMIC requirement on using DATAFILE on which file name must be always specified with full path.
The command line specified within ' is executed by FOR with running in background %ComSpec% /c and the command line in ' appended as additional arguments. So executed is in background with Windows installed to C:\Windows the command line:
C:\Windows\System32\cmd.exe /c C:\Windows\System32\wbem\wmic.exe DATAFILE where Name="D:\\V4-x64\\Exe\\Bin\\FileName.dll" GET Version 2>nul
The equal sign must be escaped with ^ to be interpreted as literal character and not as argument separator as otherwise = would be replaced by a space character before running cmd.exe in background which would make the arguments invalid for wmic.exe.
The redirection operator > must be escaped also with ^ to be interpreted as literal character on parsing the FOR command line before executing FOR at all.
WMIC outputs the data Unicode encoded with UTF-16 Little Endian encoding with byte order mark (BOM). FOR has problems to parse the Unicode output correct as expecting an ASCII/ANSI/OEM character encoding, i.e. one byte per character without null bytes. The byte sequence with the hexadecimal values 0D 00 0A 00 being UTF-16 LE encoded the line ending carriage return + line-feed is interpreted as 0D 0D 0A by FOR (respectively cmd.exe). So there is an erroneous carriage return at end of the real line ending removed by FOR before processing further the remaining string.
For that reason the WMIC output is interpreted with skipping the first line containing just the string Version and so processed is first the second line with the version string by removing leading and trailing spaces.
The full qualified file name of the current DLL file and its version is output with command ECHO. Additionally the environment variable FileName is deleted to make sure that no further line output by WMIC including an empty line interpreted by FOR as a line with just a carriage return results in running once again command ECHO with loop variable J having assigned a carriage return.
The batch file does not work for DLL files with an exclamation mark in file name, but that should not matter here as I have never seen a DLL with ! in file name.
Another solution provided by Compo in a comment is much faster and works also for DLL file names with ! in file name.
#echo off
%SystemRoot%\System32\wbem\wmic.exe DATAFILE Where "Drive='D:' And Path='\\V4-x64\\Exe\\Bin\\' And Extension='dll'" GET Name,Version >"%TEMP%\%~n0.tmp"
%SystemRoot%\System32\more.com +1 "%TEMP%\%~n0.tmp" >test.txt
del "%TEMP%\%~n0.tmp"
WMIC searches itself for *.dll files in the directory D:\V4-x64\Exe\Bin and outputs full name of the DLL file and its version written into a temporary file being UTF-16 LE encoded with BOM. This temporary file is next processed with MORE with skipping the header line and the empty lines at bottom and the output is written into test.txt being a non-Unicode file. The temporary file is deleted finally.
For completeness:
#%SystemRoot%\System32\wbem\wmic.exe DATAFILE Where "Drive='D:' And Path='\\V4-x64\\Exe\\Bin\\' And Extension='dll'" GET Name,Version | %SystemRoot%\System32\more.com +1 >test.txt
This command line produces a non-Unicode encoded text file test.txt on which every line with a DLL file name and version is terminated with two carriage returns and one line-feed and with two empty lines at bottom with just one carriage return plus line-feed. So it is not recommended to use this single line variant.
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 /? ... explains %~n0 ... file name of argument 0, i.e. the batch file name without file extension.
del /?
echo /?
endlocal /?
for /?
if /?
more /?
set /?
setlocal /?
wmic /?
wmic datafile /?
wmic datafile get /?
See also single line with multiple commands using Windows batch file for an explanation of the operator & and the Microsoft documentation about Using command redirection operators.

Write HEX values to file in Windows batch

In Linux we can do
echo -n -e '\x66\x6f\x6f' > test.txt
to write HEX values to a file.
How can this be done simply in Windows batch?
I am assuming you want the ability to write all possible binary bytes in the range \x00 through \xFF. Unfortunately, pure batch does not provide a simple mechanism to do this.
But there are a number of options that are not too difficult.
Undocumented !=ExitCodeASCII! dynamic variable
The !=ExitCodeASCII! dynamic variable reports the ASCII value of the return code of the most recently run external command. But it is limited to ASCII codes from \x20 through \x7E.
I use delayed expansion so that I don't have to worry about poison characters.
The simplest mechanism to return a specific error code is to use cmd /c exit N, where N must be a decimal value. If you want to pass in a hex value, then the value must first be converted to decimal.
Windows batch uses 0xNN notation to specify a hex value. As others have noted, you can use set /a val=0x66 to do the conversion. Another option is to use for /l %%N in (0x66 1 0x66) do ..., the advantage being you don't need to define an intermediate environment variable to hold the value.
#echo off
setlocal enableDelayedExpansion
set "str="
for %%H in (0x66 0x6f 0x6f) do (
for /l %%N in (%%H 1 %%H) do cmd /c exit %%N
set "str=!str!!=ExitCodeASCII!"
)
>test.txt echo(!str!
Advantages:
Pure batch
Disadvantages:
Must build string one character at a time
Range limited to 0x20 - 0x7E
FORFILES
The FORFILES command supports 0xNN syntax, so it can generate most characters. But the string must pass through CMD /C, so it cannot be used to generate \0x00, \0x0A, or \0x0D. (I haven't tested, but I believe all other values work, provided poison characters are appropriately quoted or escaped)
#echo off
forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo(0x660x6f0x6f"
Advantages:
Pure batch
Can process an entire string in one pass
Disadvantages:
No native XP support
No support for 0x00, 0x0A, or 0x0D
Escaping / quoting of poison characters can be tricky
CERTUTIL
The CERTUTIL supports a -decodeHex verb that can read hex values and write directly to a file
#echo off
>temp.txt echo(66 6f 6f 0d 0a
certutil -f -decodehex temp.txt test.txt >nul
del temp.txt
Advantages:
Pure batch
All possible bytes codes supported
Absolute control of newlines and carriage returns
Can process an entire string (or file!) in one pass
Fast
Disadvantages:
Requires a temp file
Hybrid JScript / batch - simple solution
It is very easy to embed and execute JScript within a batch script. And JScript has native ability to interpret many escape sequences, including \xNN. However, the \xNN escape sequences actually map to Unicode code points, so some of the high order byte codes do not map to the correct character values. And the results for high order bytes can vary depending on your machines default character set.
Below I define a :jWrite subroutine that can write lines with embedded escape sequences. Simply change the WriteLine to Write in the JScript code if you want to write strings without the newline characters.
#if (#X)==(#Y) #end /* Harmless hybrid line that begins a JScript comment
:: -------- Batch code --------------
#echo off
call :jWrite "\x66\x6f\x6f" >test.txt
exit /b
:jWrite
cscript.exe //E:JScript //nologo "%~f0" %1
exit /b
:: --------- JScript code -----------*/
WScript.StdOut.WriteLine(eval('"'+WScript.Arguments.Unnamed(0)+'"'));
Advantages:
Pure script that runs natively on all Windows machines from XP onward
Very simple JScript code
Can process entire strings in one pass
Many other escape sequences available, such as \\, \t, \r, \n, etc.
Easy to mix ordinary text with escape sequences, but double quote must be \x22
Disadvantages:
Not all high order bytes give the correct result
Results are dependent on your machine's default character set. Best if Windows 1252
Hybrid JScript / batch - more robust, but complex solution
It is not too difficult to write some JScript code to properly interpret all \xNN codes to give the correct byte as long as your machine defaults to Windows-1252. And if your command session's active code page also matches Windows 1252, then you can freely mix in normal text.
#if (#X)==(#Y) #end /* Harmless hybrid line that begins a JScript comment
:: -------- Batch code --------------
#echo off
call :jWrite "\x66\x6f\x6f"
call :jWrite "Hello\nworld\x80"
exit /b
:jWrite
cscript.exe //E:JScript //nologo "%~f0" %1
exit /b
:: --------- JScript code -----------*/
WScript.StdOut.WriteLine(WScript.Arguments.Unnamed(0).replace(
/\\(\\|b|f|n|r|t|v|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})/g,
function($0,$1) {
switch ($1.toLowerCase()) {
case 'x80': return '\u20AC';
case 'x82': return '\u201A';
case 'x83': return '\u0192';
case 'x84': return '\u201E';
case 'x85': return '\u2026';
case 'x86': return '\u2020';
case 'x87': return '\u2021';
case 'x88': return '\u02C6';
case 'x89': return '\u2030';
case 'x8a': return '\u0160';
case 'x8b': return '\u2039';
case 'x8c': return '\u0152';
case 'x8e': return '\u017D';
case 'x91': return '\u2018';
case 'x92': return '\u2019';
case 'x93': return '\u201C';
case 'x94': return '\u201D';
case 'x95': return '\u2022';
case 'x96': return '\u2013';
case 'x97': return '\u2014';
case 'x98': return '\u02DC';
case 'x99': return '\u2122';
case 'x9a': return '\u0161';
case 'x9b': return '\u203A';
case 'x9c': return '\u0153';
case 'x9d': return '\u009D';
case 'x9e': return '\u017E';
case 'x9f': return '\u0178';
default: return eval('"'+$0+'"');
}
}
));
Advantages:
Pure script that runs natively on all Windows machines from XP onward
Can process entire strings in one pass
All bytes codes from \x00 through \xFF are supported
Many other escape sequences available, such as \\, \t, \r, \n, etc.
Easy to mix ordinary text with escape sequences, but double quote must be \x22
Disadvantages:
Only gives correct results for all bytes if your machine defaults to Windows-1252
Moderately complex JScript code
JREPL.BAT - Hybrid JScript/batch regular expression text processing utility
My JREPL.BAT utility was originally designed to perform regular expression search and replace operations on text files. But it has options that allow it to easily write strings with embedded escape sequences, and it can give the correct result for all byte codes no matter what default character set your machine uses.
If your machine defaults to any single byte character set, then you can safely use the following with all possible escape sequences from \x00 through \xFF, and you can freely mix in normal text along with escape sequences.
call jrepl $ "\x66\x6f\x6f" /s "=" /x /o test.txt
The /s "=" option specifies an undefined environment variable as the input source, which is interpreted as an empty string. The first $ argument matches the end of the empty string. The second "\x66\x6f\x6f" argument specifies the replacement value. The /x option enables escape sequences within the replacement string, and the /o test.txt option specifies the output file.
If you want to append to test.txt, then add the /APP option.
If you want \n end-of-line instead of \r\n, (Unix style instead of Windows) then add the /U option
If you don't want any new line terminators, then add the /M option.
Lastly, if your machine does not default to a single byte character set, you can still force the correct result for all escape sequences by specifying a single byte character set like Windows-1252 for the output format. However, if the specified character set does not match your command session's active code page, then only escape sequences are guaranteed to work - some normal text characters may give the wrong result.
call jrepl $ "\x66\x6f\x6f" /s "=" /x /o "test.txt|Windows-1252"
Advantages:
Pure script that runs on any Windows machine from XP onward
Can process entire strings in one pass
All bytes codes from \x00 through \xFF are supported
Many other escape sequences available, such as \\, \t, \r, \n, etc.
Easy to mix ordinary text with escape sequences, but double quote must be \x22 or \q
Default character set agnostic - can always give the correct result
Once you have JREPL in your arsenal of tools, you will find many uses for it. It is a powerful tool.
Disadvantages:
Requires download of a 3rd party script
To write "HEX values to file":
set a=.
for /l %a in (0,1,255) do (
set /a dec=%a >nul
call cmd /c exit /b !dec!
set hex=!=exitcode!
set a=!a! !hex:~-2!
)
echo %a:~2% > file.txt
To create a binary file from batch with all the values, see here:
https://www.dostips.com/forum/viewtopic.php?f=3&t=5326
certutil can do both: create a hex file from a binary, and create a binary file from a hex coded file:
certutil -f -decodeHex hex.txt out.bin >nul
certutil -f -encodeHex out.bin out.txt >nul
To write a binary file with given (decoded) hex value bytes, from CMD:
set data="66 6F 6F"
echo Dim File : Set File = CreateObject("Scripting.FileSystemObject").CreateTextFile("test.txt") > tmp.vbs
echo data = %data% >> tmp.vbs
echo data = Split (data) >> tmp.vbs
echo for each x in data >> tmp.vbs
echo File.write chr("&H" ^& x) >> tmp.vbs
echo next >> tmp.vbs
echo File.Close >> tmp.vbs
cscript //nologo tmp.vbs
If you're just after writing printable (from 0x20 through 0x7E) characters, then !=ExitCodeASCII! is a good/faster (batch) solution.
In addition to #dbenham's excellent and comprehensive answer, using PowerShell Core 6.x+ (available on all supported Windows systems, MacOS and Linux at http://github.com/PowerShell/Powershell) would be another way to do it in a batch-file run by cmd.
pwsh.exe -NoLogo -NoProfile -Command Write-Host "`u{64}`u{65}`u{66}" >test.txt
Please note that this works with PowerShell Core. I have yet to find a way to make it work with Windows PowerShell 5.x that is the most recent version as of this date that is delivered as part of Microsoft Windows systems.

Batch Remove Trailing Whitespace in a Text File

Remove trailing spaces from a file using Windows batch? and How to remove trailing and leading whitespace for user-provided input in a batch file? seem to be similar questions, but I can't get any of the answers to work.
I have a text file, say C:\Users\%username%\Desktop\sometext.txt that has a long list of numbers, some with trailing whitespace. How can I loop through and remove the whitespace at the end of each line? I'd like to do this all in batch. It'll be in a file with some other batch commands in it.
Regrettably, you don't show us an example from your file, so we're left to assume from your desription.
Assuming your file is something like
1
22
3
64
where some of the lines have trailing spaces, then
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
(
FOR /f "usebackq" %%a IN (q30594509.txt) DO (
SET /a num=%%a
ECHO(!num!
)
)>u:\newfile.txt
GOTO :EOF
I used a file named q30594509.txt containing the above data for my testing.
Produces u:\newfile.txt

Batch trim characters at the EOL

i would like to create a batch file that goes through txt files in a directory and every line that has #EXTINF in it, remove the last 15 characters..
basically if it is possible, i would like to trim the last 15-20 characters from any lines that is longer than a certain number. for some reason i have a software that if the line is too long, it will screw up the data.
thanks
See How can you find and replace text in a file using the Windows command-line environment? for various options to modify text files using Windows batch.
The following simple code uses REPL.BAT to truncate long lines that begin with #extinf (case insensitive) at 600 characters. Lines that are less than 600 characters are preserved in their entirety.
type test.txt|repl.bat "^(#extinf.{593}).*$" "$1" I>test.txt.new
move /y test.txt.new test.txt >nul
User231429 wrote: "i need the script to go through all files in a directory, and each line that starts with #extinf, remove XX number of characters from the end."
The Batch file below do precisely that:
#echo off
setlocal EnableDelayedExpansion
for %%f in (*.txt) do (
(for /F "usebackq delims=" %%a in ("%%f") do (
set "line=%%a"
if "!line:~0,7!" equ "#extinf" set "line=!line:~0,-XX!"
echo !line!
)) > "%%~Nf.new"
)
REM del *.txt
REM ren *.new *.txt
Note that you must replace XX by a number in the long if command.
Test this program and check the result in *.NEW files. If the result is correct, remove REM part from two last lines.
This program remove exclamation marks from the file. This detail may be fixed, if required.

In batch, how do I create spaces at the beginning of a input prompt string?

Let's say I have a batch file that has "padding" in it, and I want to indent the beginning of the prompt string for the user to type in. If I use spaces, it will not show up when run, it just ignores the spaces. This script for an example:
#echo off
echo.
echo.
echo Hi.
echo Please input something.
echo.
set /P input=
After the = there is three spaces, and what I expect is that the marker for input is away from the edge of the command box, however these spaces are ignored.
How can I fix this problem? I am using Windows 7 SP1.
You need to add a dot after the echo
The following example will output "Test" with three leading spaces:
echo. Test
Same works for tabulator. The following example will output "Test" with one leading tab:
echo. Test
As the comments above state, Vista and beyond strip leading spaces in a SET /P prompt.
The way to get around the problem is to define and use a backspace character in the prompt.
::define a variable containing a single backspace character
for /f %%A in ('"prompt $H &echo on &for %%B in (1) do rem"') do set BS=%%A
set /p var=%BS% Leading spaces will not show properly.
Normally the prompt will be at the beginning of a line, so the above works just fine.
But if the prompt is issued from the middle of a line (very unusual), then a leading character should be included prior to the <BS>, since the backspace will erase whatever was before it.
<nul set/p=Leave the cursor at the end of this line:
set /p var=.%BS% The dot (any char) is necessary to prevent the <BS> from erasing the :
Highly inspired by in dbenham's answer, I propose a similar but simpler variation based in the fact that the backspace character can be inserted in its raw form (only in batch files, attempting this in the console directly won't work as expected):
set /p var=.'BS' Leading spaces will now show properly.
The 'BS' character can be inserted by typing Alt + Numpad 008 (8 is backspace's ASCII code, won't work using the alphanumeric keys typically found above the letters), using a good text editor (such as Notepad++, Windows Notepad simply performs the backspace action).
If unable to insert the character, Notepad++ has a useful feature for this: in TextFX menu, choose TextFX Tools followed by Insert Ascii Chart or Character:
The desired character is the BS (white letters on black background on the screenshot) found in line 9 (ASCII character 8 - as stated above - as it's a zero-indexed table).
If still the result is not the one described, try changing the file encoding to ASCII. Using Notepad++:
Make a backup copy of the script or perform an experiment in a separate file, as non-ASCII characters (accented character, non-Latin etc.) are lost in this conversion.
In Encoding menu, choose Convert to ANSI
Save and check the result again...
Tested with Windows 7 Professional SP1.
Credits also go to:
#DavidCallanan for questioning the obtained result.
Wadelau for writing about Notepad++ at ufqi.com (where the screenshot was extracted from).
This works in every Windows OS from W2K + I've tried, if it suits you.
You could just use a : in the string.
set /p "var=Please input something: "
echo.%var%
dbenhams answer works good when you only want to display the text, but not if you create a file, as it inputs also the backspaces.
But for files(and for displaying) you can use copy /a to remove a CR/LF with the help of a SUB(EOF) character.
The trick is to append the SUB character directly after the text, so it's just before the CR/LF of the ECHO output.
And then using the /a switch of the copy command will only copy the content to the SUB character, so the SUB and also the CR/LF are removed
#echo off
setlocal EnableDelayedExpansion
call :createSub
call :echoWithoutLinefeed "=hello"
call :echoWithoutLinefeed " world"
exit /b
:echoWithoutLinefeed
> txt.tmp (echo(%~1!sub!)
copy txt.tmp /a txt2.tmp /b > nul
type txt2.tmp
del txt.tmp txt2.tmp
exit /b
:createSub
copy nul sub.tmp /a > nul
for /F %%a in (sub.tmp) DO (
set "sub=%%a"
)
del sub.tmp
exit /b
You can use double quotes (" ... ") for your purpose
set /p OPTION="Your choice: "
As you can see here, the cursor for input is 3 units away.

Resources