I want to nest a for loop inside a batch file to delete carriage return.
I tried it like you can see below but it does not work.
#echo off
setLocal EnableDelayedExpansion
for /f "tokens=* delims= " %%a in (Listfile.txt) do (
set /a N+=1
set v!N!=%%a
)
for /l %%i in (1, 1, %N%) do (
echo !v%%i!
for /r "tokens=* delims=" %%i in (windows.cpp) do (
echo %%i >> Linux11.cpp
)
)
pause
Here I want to check with windows.cpp. If its working I like to change windows .cpp with !v%%i!
You cannot do this in a batch file. You have no way of addressing or writing arbitrary characters. Every tool on Windows normally makes sure to output Windows line breaks (i.e. CR+LF). Some can read Unix-style line breaks just fine, which is why you can easily convert from them. But to them isn't possible.
Also as a word of caution: Source code files often contain blank lines (at least mine do) that are for readability. for /f skips empty lines which is why you're mangling the files for your human readers there. Please don't do that.
As for your question: When nesting two loops you have to make sure that they don't use the same loop variable. Show me a language where code like you wrote actually works.
Something like
for /l %%i in (1, 1, %N%) do (
echo !v%%i!
for /f "tokens=* delims=" %%l in ("!v%%i!") do (
rem do whatever you want to do with the lines
)
)
should probably work better (you missed the final closing parenthesis as well). Thing to remember: If you want to use a certain variable instead of a fixed file name it surely helps replacing that fixed file name by that variable.
It would be probably easiest to use some unix2dos/dos2unix converter to do that or some win32 flavor of sed.
The intrinsic issue of your code is already addressed by another answer, hence I am going to focus on the main task you are trying to accomplish, namely converting DOS/Windows-style end-of-line markers (or line-breaks) to Unix-style ones.
Doing this is very tricky in a batch file, but give the following script a try. Supposing it is called convert.bat, and the original text file is named convert.txt, run the script using the following command line:
convert.bat "convert.txt" LF
The name of the returned file will get the original file name with _converted_EOL appended. The second argument LF specifies Unix-style line-breaks; omitting it will return DOS/Windows-style ones.
So here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem check whether or not an existing file is given as the first argument
>&2 (
if "%~1"=="" (
echo No file specified.
exit /B 2
) else if not exist "%~1" (
echo File "%~1" not found.
exit /B 1
)
)
rem get carriage-return character
for /F %%A in ('copy /Z "%~0" nul') do set "CR=%%A"
rem get line-feed character (the two empty lines afterwards are mandatory!)
(set ^"LF=^
%= blank line =%
^")
rem check which line-break is given by the second argument
rem (`CR` - carriage return (Mac); `LF` - line feed (Unix);
rem anything else or nothing - CR+LF (Windows, default))
setlocal EnableDelayedexpansion
set "BR=!CR!!LF!"
if /I "%~2"=="CR" set "BR=!CR!" & (>&2 echo CR not supported.) & exit /B 3
if /I "%~2"=="LF" set "BR=!LF!"
rem convert line-breaks; append `_converted_EOL` to file name
setlocal DisableDelayedExpansion
> "%~n1_converted_EOL%~x1" (
for /F delims^=^ eol^= %%L in ('
findstr /N /R "^" "%~1"
') do (
set "LINE=%%L"
rem firstly, precede every line with a dummy character (`:`) and
rem append the specified line-break in order to avoid the loss of
rem leading white-spaces or trouble with leading equal-to signs,
rem all caused by `set /P`, which is needed here to return the
rem line without a trailing DOS/Windows-style line-break (opposed
rem to `echo`); then, let `pause` strip off that character;
rem lastly, let `findstr` return the remainder;
rem (the `rem` suffix is just there to fix syntax highlighting)
cmd /V /C ^< nul set /P #="!LINE:*:=:!!BR!" | (> nul pause & findstr "^") & rem/ "^"
)
)
endlocal
endlocal
endlocal
exit /B
The following restrictions apply:
no line must be longer than about 8190 characters (this is a general limitation of batch files);
the file must not contain any null-bytes (well, a normal text file should not hold such, but Unicode-encoded do);
the last line of the returned file will always be terminated by a line-break, even if the respective original line is not;
And here is another solution for line-break conversions: Convert all CR to CRLF in text file using CMD
Related
I am using a batch file to rename all files in a folder into a numbered list. The code I use is as follow:
set /a Index=1
setlocal enabledelayedexpansion
for /f "tokens=*" %%f in ('dir /b') do (
echo %%f
echo !Index!
rename "%%f" "!Index!.*"
set /a Index+=1
pause
)
The result of the batch file is
G:\Directory A> (
echo
03.jpg
echo 1
rename "
03.jpg" "1.*"
set /a Index+=1
pause
)
03.jpg
1
The filename, directory name, or volume label syntax is incorrect.
Press any key to continue . . .
G:\Directory A> (
echo 04.jpg
echo 1
rename "04.jpg" "1.*"
set /a Index+=1
pause
)
04.jpg
1
The first result ALWAYS contains a line break at the beginning of the file name, which causes the RENAME command to fail. Can anyone tell me what is wrong with my code?
UPDATE
There is the folloging auto-run code set up in the Windows registry:
[HKEY_CURRENT_USER\Software\Microsoft\Command Processor]
"Autorun"="#chcp 65001>nul & prompt $d$s$s$t$_$p$g & cls"
After digging a bit with my batch file, I recognized that this is a problem with the Autorun option that I had set ages ago in Registry that look like this:
[HKEY_CURRENT_USER\Software\Microsoft\Command Processor]
"Autorun"="#chcp 65001>nul & prompt $d$s$s$t$_$p$g & cls"
Apparently the CLS command caused the weird line break to happen. After removing the CLS part from Autorun, the batch file works as it is intended to.
The finding described in the self-answer is very interesting albeit tremendously nasty!
The auto-run code contains the cls command, which sends a single form-feed character (FF, code0x0C) to the console that tells it to clear the screen (the character can be displayed by running cls > con in the console).
When executing cls within a for /F loop, the FF is captured: for /F %F in ('cls') do #echo/%F in the command prompt displays that character.
A for /F loop initiates a new cmd.exe instance to execute the given command line; this is done by preceding cmd /C to the command line.
With auto-run commands set up, they become executed within the for /F loop (due to the unfortunate lack of the /D option, which would prevent them to run).
The auto-run command line from the question has got cls involved, so for /F %F in ('rem') do #echo/%F captures a FF, even though the actual command rem does not return anything.
There are methods to suppress the FF character (note that the following code examples are intended to be placed in a batch-file rather than entered into the console):
Precede the desired command line by echo/& to append a line-break to a potential FF to have an extra line at the beginning and skip that line by for /F:
rem // Precede the desired command with `echo/` and skip the first line:
for /F "skip=1 delims=" %%I in ('echo/^& dir /B') do echo "%%I"
Retrieve a FF first and define it as the eol (end of line) character for for /F, then again prefix the desired command line with echo/& just like before:
rem // Retrieve the form-feed character and ignore lines beginning with a form-feed:
for /F delims^=^ eol^= %%F in ('echo/^& cls') do set "_FF=%%F"
rem // Precede the desired command with `echo/` and use form-feed as `eol` character:
for /F "delims= eol=%_FF%" %%I in ('echo/^& dir /B') do echo "%%I"
A more complex way needs to be gone when the auto-run code may write several arbitrary lines (that may even be empty). For this the number of said lines needs to be determined in order to become able to skip that number of lines later:
rem /* Determine the number of lines that the `AutoRun` code regurgitates, including empty ones;
rem the command line actually executed by `for /F` implicitly using `cmd /C` is as follows:
rem `chcp 437 > nul & echo/& cmd /C echo/| find /C /V ""`
rem this first returns a line-break, to have potential `AutoRun` text separated from the rest;
rem then another `AutoRun` text again with a terminal line-break is generated, but this time
rem piped into `find /C /V ""` in order to count the number of lines;
rem `chcp 437 > nul` is necessary to set the code page for the implicit `cmd.exe` instance,
rem because `find` (just like `more`) may have issues with some particulay Unicode code pages
rem (like 65001, which may be set by the `AutoRun` code): */
for /F %%C in ('chcp 437 ^> nul ^& echo/^& cmd /C echo/^| find /C /V ""') do set /A "SKIP=%%C" 2> nul
if %SKIP% gtr 0 (set "SKIP=skip=%SKIP%") else set "SKIP="
rem // Precede the desired command with `echo/` and skip as many lines as necessary:
for /F "%SKIP% delims=" %%I in ('echo/^& dir /B') do echo "%%I"
I've been trying to figure out how to replace an entire line in a text file that contains a certain string using a Batch Script. I've found this solution provided by another user on Stack Overflow, which does the job, however, it just stops iterating through the text file at some random point and in turn, the output file is left with a bunch of lines untransferred from the original file. I've looked character by character, and line by line of the script to figure out what each part exactly does, and can not seem to spot what is causing this bug.
The code provided, thanks to Ryan Bemrose on this question
copy nul output.txt
for /f "tokens=1* delims=:" %%a in ('findstr /n "^" file.txt') do call :do_line "%%b"
goto :eof
:do_line
set line=%1
if {%line:String =%}=={%line%} (
echo.%~1 >> output.txt
goto :eof
)
echo string >> output.txt
The lines it is stopping at always either contain < or > or both and lines with | will either cause it to stop, or sometimes it will delete the line and continue.
To do this robustly, Delayed expansion is necessary to prevent "poison" characters such as < > & | etc being interpreted as command tokens.
Note however that delayed expansion should not be enabled until after the variable containing the line value is defined so as to preserve any ! characters that may be present.
The following will robustly handle all Ascii printable characters *1, and preserve empty lines present in the source file:
#Echo off
Set "InFile=%~dp0input.txt"
Set "OutFile=%~dp0output.txt"
Set "Search=String "
Set "Replace="
>"%OutFile%" (
for /F "delims=" %%G in ('%SystemRoot%\System32\findstr.exe /N "^" "%InFile%"') do (
Set "line=%%G"
call :SearchReplace
)
)
Type "%OutFile%" | More
goto :eof
:SearchReplace
Setlocal EnableDelayedExpansion
Set "Line=!Line:*:=!"
If not defined Line (
(Echo()
Endlocal & goto :eof
)
(Echo(!Line:%Search%=%Replace%!)
Endlocal & goto :eof
*1 Note - Due to how substring modification operates, You cannot replace Search strings that:
contain the = Operator
Begin with ~
Using a .bat script, I want to find a line that says # Site 1 and replace the text in the next line with a variable. I found tutorials on StackOverflow for finding and replacing a line, but not finding a line and replacing the next line. Any help?
#echo off
set "the_file=C:\someFile"
set "search_for=somestring"
set "variable=http://site1"
for /f "tokens=1 delims=:" %%# in ('findstr /n /c:"%search_for%" "%the_file%"') do (
set "line=%%#"
goto :break
)
:break
set /a lineBefore=line-1
set /a nextLine=line+1
break>"%temp%\empty"&&fc "%temp%\empty" "%the_file%" /lb %lineBefore% /t |more +4 | findstr /B /E /V "*****" >newFile
echo %variable%>>newFile
more "%the_file%" +%nextLine% 1>>newFile
echo move /y newFile "%the_file%"
Check if newFile is ok and remove the echo at the front of the last line.
you need to set the three variables at the beginning by yourself.Have on mind that more command sets spaces instead of tabs
#ECHO OFF
SETLOCAL
SET "filename=q28567045.txt"
SET "afterme=# Site 1"
SET "putme=put this line after # Site 1"
SET "skip1="
(
FOR /f "usebackqdelims=" %%a IN ("%filename%") DO (
IF DEFINED skip1 (ECHO(%putme%) ELSE (ECHO(%%a)
SET "skip1="
IF /i "%%a"=="%afterme%" SET skip1=y
)
)>newfile.txt
GOTO :EOF
Produces newfile.txt
The flag `skip1 to skip the line is first reset, then the file is read line by line.
If the skip1 flag is set, then the replacement line is echoed in place of the line read; if not, the line read is echoed.
Then the skip1 flag is cleared
If the line read to %%a matches the string assigned to afterme then the flag skip1 is set (to y - but it doesn't matter what the value is)
Note that empty lines and those starting ; will be ignored and not reproduced - this is standard behaviour of for /f.
If you want to replce the starting file, then simply add
move /y newfile.txt "%filename%"
before the goto :eof line.
Even though I enjoy working with batch, I generally avoid using pure native batch to edit text files because a robust solution is usually complicated and slow.
This can be done easily and efficiently using JREPL.BAT - a hybrid JScript/batch utility that performs regular expression replacement. JREPL.BAT is pure script that runs natively on any Windows machine from XP onward.
#echo off
setlocal
set "newVal=Replacement value"
call jrepl "^.*" "%newValue%" /jbeg "skip=true" /jendln "skip=($txt!='# Site 1')" /f test.txt /o -
The /F option specifies the file to process
The /O option with value of - specifies to replace the original file with the result.
The /JBEG option initializes the command to skip (not replace) each line.
The /JENDLN option checks the value of each line just before it is written out, and sets SKIP off (false) if it matches # Site 1. The next line will be replaced only when SKIP is false.
The search string matches an entire line.
The replacement string is your value stored in a variable.
This problem is similar to this one and may use an equivalent solution. The pure Batch file solution below should be the fastest one of its kind.
#echo off
setlocal EnableDelayedExpansion
set "search=# Site 1"
set "nextLine=Text that replaces next line"
rem Get the line number of the search line
for /F "delims=:" %%a in ('findstr /N /C:"%search%" input.txt') do set /A "numLines=%%a-1"
rem Open a code block to read-input-file/create-output-file
< input.txt (
rem Read the first line
set /P "line="
rem Copy numLines-1 lines
for /L %%i in (1,1,%numLines%) do set /P "line=!line!" & echo/
rem Replace the next line
echo %nextLine%
rem Copy the rest of lines
findstr "^"
) > output.txt
rem Replace input file with created output file
move /Y output.txt input.txt > NUL
This method will fail if the input file have empty lines and also have other limitations.
For a further description of this method, see this post.
I'm trying to insert a line into a file using the following code (from Write batch variable into specific line in a text file)
#echo off
setlocal enableextensions enabledelayedexpansion
set inputfile=variables.txt
set tempfile=%random%-%random%.tmp
copy /y nul %tempfile%
set line=0
for /f "delims=" %%l in (%inputfile%) do (
set /a line+=1
if !line!==4 (
echo WORDS YOU REPLACE IT WITH>>%tempfile%
) else (
echo %%l>>%tempfile%
)
)
del %inputfile%
ren %tempfile% %inputfile%
endlocal
My problem is the file has comment lines (which start with semicolons) which need to be kept
; directory during network startup. This statement must indicate a local disc
; drive on your PC and not a network disc drive.
LOCALDRIVE=C:\TEMP;
; PANELISATION PART/NET NAMING CONVENTION
; When jobs are panelised, parts/nets are renamed for each panel step by
When I run the batch file, it ignores the semicolon lines, So I only get:
LOCALDRIVE=C:\TEMP;
What do I need to do to keep the semicolon lines?
The EOL option determines what lines are to be ignored. The default value is a semicolon. If you know a character that can never appear in the first position of a line, then you can simply set EOL to that character. For example, if you know a line can't start with |, then you could use
for /f "eol=| delims=" %%l in (%inputfile%) do ...
There is an awkward syntax that disables EOL completely, and also disables DELIMS:
for /f delims^=^ eol^= %%l in (%inputfil%) do ...
Note that FOR /F always discards empty lines, so either of the above would result in:
; directory during network startup. This statement must indicate a local disc
; drive on your PC and not a network disc drive.
LOCALDRIVE=C:\TEMP;
; PANELISATION PART/NET NAMING CONVENTION
; When jobs are panelised, parts/nets are renamed for each panel step by
A trick is used if you want to preserve empty lines. Use FIND or FINDSTR to insert the line number before each line, and then use expansion find/replace to remove the line number. Now you know the line never begins with ;, so you can ignore the EOL option.
for /f "delims=" %%L in ('findstr /n "^" "%inputfile%"') do (
set "ln=%%L"
set "ln=!ln:*:=!"
REM You now have the original line, do whatever needs to be done here
)
But all of the above have a potential problem in that you have delayed expansion enabled when you expand the FOR variable, which means that any content containing ! will be corrupted. To solve this you must toggle delayed expansion on and off within the loop:
setlocal disableDelayedExpansion
...
for /f "delims=" %%L in (findstr /n "^" "%inputfile%") do (
set "ln=%%L"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
REM You now have the original line with ! preserved, do whatever needs done here
endlocal
)
Also, when ECHOing an empty line, it will print out ECHO is off unless you do something like
echo(!ln!
It takes time to open and position the write cursor to the end every time you use >> within the loop. It is faster to enclose the entire operation in one set of parentheses and redirect once. Also, you can replace the DEL and REN with a single MOVE command.
Here is a final robust script:
#echo off
setlocal disableDelayedExpansion
set "inputfile=variables.txt"
set line=0
>"%inputfile%.new" (
for /f "delims=" %%L in (findstr /n "^" "%inputfile%") do (
set "txt=%%L"
set /a line+=1
setlocal enableDelayedExpansion
set "txt=!txt:*:=!"
if !line! equ 4 (
echo New line content here
) else (
echo(!txt!
)
endlocal
)
)
move /y "%inputfile%.new" "%inputfile%" >nul
endlocal
That is an awful lot of work for such a simple task, and it requires a lot of arcane knowledge.
There is a much quicker hack that works as long as
your first 4 lines do not exceed 1021 bytes
none of your first 3 lines have trailing control characters that need to be preserved
the remaining lines do not have <tab> characters that must be preserved (MORE converts <tab> into a string of spaces.
#echo off
setlocal enableDelayedExpansion
set "inputfile=variables.txt"
>"%inputfile%.new" (
<"%inputfile%" (
for /l %%N in (1 1 3) do (
set "ln="
set /p "ln="
echo(!ln!
)
)
echo New line content here
more +4 "%inputfile%"
)
move /y "%inputfile%.new" "%inputfile%"
That is still a lot of work and arcane knowledge.
I would use my JREPL.BAT utility
Batch is really a terrible tool for text processing. That is why I developed JREPL.BAT to manipulate text using regular expressions. It is a hybrid JScript/batch script that runs natively on any Windows machine from XP onward. It is extremely versatile, robust, and fast.
A minimal amount of code is required to solve your problem with JREPL. Your problem doesn't really require the regular expression capabilities.
jrepl "^" "" /jendln "if (ln==4) $txt='New content here'" /f "variables.txt" /o -
If used within a batch script, then you must use call jrepl ... because JREPL.BAT is also a batch script.
By default, the FOR command treats ; as the end-of-line character, so all those lines that start with ; are being ignored.
Add eol= to your FOR command, like this:
for /f "eol= delims=" %%l in (%inputfile%) do (
It looks like you're echoing just the line delimiter, not the whole line:
echo %%l>>%tempfile%
I'm rusty on ms-dos scripts, so I can't give you more than that.
I had a look at the previous questions of your db and I didn't try an answer, but I try.
I would like to write the following lines code:
echo Executing backup....
backup procedure
echo Ok
but the output should be:
Executing backup... Ok
That's possible?!
I suppose you are using dos/nt-batch.
It is possible with the set /p command, because set /p doesn't print a CrLf
set /p "=Executing backup...." <nul
echo OK
Also it's possible to erase the line with a CR character.
It's important to know that whitespace characters at the front of an set /p are ignored (in Vista, not in XP), so the !cr! has to placed later or at the end.
A CR can only be displayed with delayedExpansion, because %cr% works, but CR characters are removed in the percent expansion phase(or directly after this phase), but not in the delayed expansion phase.
Example of a counter which use only one line for displaying
#echo off
setlocal EnableDelayedExpansion EnableExtensions
call :CreateCR
for /l %%n in (1,1,10000) do (
set /P "=Count %%n!CR!" <nul
)
echo(
goto :eof
:CreateCR
rem setlocal EnableDelayedExpansion EnableExtensions
set "X=."
for /L %%c in (1,1,13) DO set X=!X:~0,4094!!X:~0,4094!
echo !X! > %temp%\cr.tmp
echo\>> %temp%\cr.tmp
for /f "tokens=2 usebackq" %%a in ("%temp%\cr.tmp") do (
endlocal
set cr=%%a
goto :eof
)
goto :eof
EDIT: Explanation, how the variable cr is created (Done with a trick)
After setting variable X to a single dot (the character itself is unimportant), it is repeated to become 8188 characters by way of for /L %%c in (1,1,13) DO set X=!X:~0,4094!!X:~0,4094!
Then the variable, two spaces and both a CR and LF are echoed into a file with echo !X! > %temp%\cr.tmp (Notice the two spaces between the !X! and the > and the natural line endings echo amends internally)
We now have 8192 characters, but the data buffer can only hold 8191 characters, so the last character (the linefeed) will be dropped!
In the next line echo\>> %temp%\cr.tmp, another CR/LF set is appended to the file (the \ in the command is just to output nothing bar the carriage return and line feed, as echo by it's self will output ECHO is ON/OFF), that's important, as a single CR can't be read at the end of a line (More later).
So the file now contains <8188 .'s><SPACE><SPACE><CR><CR><LF>
The for /f "tokens=2 usebackq" %%a in ("%temp%\cr.tmp") do reads the second token, the delimters are standard space and tab, so the second token is only a single CR, as the following CR/LF is removed as standard line ending.
Finally the endlocal is used to return to an environment without the temporary variables X, c and a existing (As with the endlocal in brackets, it allows the setting of cr before the endlocal actually takes affect at the end of the brackets (could also be written as for /f "tokens=2 usebackq" %%a in ("%temp%\cr.tmp") do endlocal&set cr=%%a&goto :eof)
Additionally
This was my first way to create a CR character, but it needs some time and a temporary file.
Later I saw a simpler method of retrieving the CR from a copy /z command.
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
Try this on Posix system (Linux)
echo -n "Executing backup.... "
echo -n "backup procedure "
echo "Ok"
It is much harder on Windows. You will need to use something like this:
#echo off
echo|set /p ="Executing backup...."
echo|set /p =" backup procedure"
Check this post: http://www.pcreview.co.uk/forums/showpost.php?s=1a20b16775d915998b30bd76a0ec5d35&p=4432915&postcount=7.
It's a bit of a hack, but here is an article describing how to do it for Windows.
From the article, the final result (edited for your setup) looks like this:
SET /P var=Backing up
%Result%....<NUL
Backup_process %Result% >NUL 2>&1
IF ERRORLEVEL 1
ECHO FAIL
ELSE
ECHO OK
I've done something similar using a VBScript.
Put this code in EchoNoNewline.vbs:
If WScript.Arguments.Named.Exists("TEXT") Then
WScript.StdOut.Write WScript.Arguments.Named.Item("TEXT")
End If
From your batch file, use the script like this:
CSCRIPT EchoNoNewLine.vbs //NOLOGO /TEXT:"Executing backup...."
backup procedure
CSCRIPT EchoNoNewLine.vbs //NOLOGO /TEXT:"Ok"
at What does a forward slash before a pipe in cmd do to remove the line ending of an echo?
the best suggestion is:
to echo text without a linefeed is very inefficient, as a pipe creates two new instances of cmd.exe.
It's much simpler and faster to use
<nul set /p "=My Text"
The redirect from NUL will also stop the waiting for user input.