Windows cmd: echo without new line but with CR - windows

I would like to write on the same line inside a loop in a windows batch file.
For example:
setlocal EnableDelayedExpansion
set file_number=0
for %%f in (*) do (
set /a file_number+=1
echo working on file number !file_number!
something.exe %%f
)
setlocal DisableDelayedExpansion
This will result in:
echo working on file number 1
echo working on file number 2
echo working on file number 3
.
.
.
I would like all of them to be on the same line.
I found a hack to remove the new line (e.g. here: Windows batch: echo without new line), but this will produce one long line.
Thanks!

#echo off
setlocal enableextensions enabledelayedexpansion
for /f %%a in ('copy "%~f0" nul /z') do set "CR=%%a"
set "count=0"
for %%a in (*) do (
set /a "count+=1"
<nul set /p ".=working on file !count! !CR!"
)
The first for command executes a copy operation that leaves a carriage return character inside the variable.
Now, in the file loop, each line is echoed using a <nul set /p that will output the prompt string without a line feed and without waiting for the input (we are reading from nul). But inside the data echoed, we include the carriage return previously obtained.
BUT for it to work, the CR variable needs to be echoed with delayed expansion. Otherwise it will not work.
If for some reason you need to disable delayed expansion, this can be done without the CR variable using the for command replaceable parameter
#echo off
setlocal enableextensions disabledelayedexpansion
for /f %%a in ('copy "%~f0" nul /z') do (
for /l %%b in (0 1 1000) do (
<nul set /p ".=This is the line %%b%%a"
)
)

Thanks to the answer of MC ND I have a created a subroutine, echocr, that you can call without
delayed expansion, that will echo a string with only a carriage return,
and no newline. (The spaces after %input% are adjusted to cover all previous messages).
You can use it to overwrite a line as shown in the modified
code:
#echo off
call :echocr "good morning"
PING -n 2 127.0.0.1>nul
call :echocr "good afternoon"
PING -n 2 127.0.0.1>nul
call :echocr "bye now"
PING -n 2 127.0.0.1>nul
pause
:echocr
:: (echo string with carriage return, no line feed)
for /F "tokens=1 delims=# " %%a in (
'"prompt #$H# & echo on & for %%b in (1) do rem"'
) do set "backspace=%%a"
set input=%~1
set "spaces40= "
set "spaces120=%spaces40%%spaces40%%spaces40%
for /f %%a in ('copy "%~f0" nul /z') do (
set /p ".=*%backspace%%spaces120%%%a" <nul
set /p ".=*%backspace%%input%%%a" <nul
)
exit /b

The above no longer works in Windows 7 and later.
I found the reason is delayed expansion that should be set after ASCII_13 assignment, maybe someone smart could explain why exactly.
Anyway, the code below works both on Windows 7 and Windows 10.
#set licz=0
#setlocal
#for /f %%a in ('copy /Z "%~dpf0" nul') do #set "ASCII_13=%%a"
#setlocal enabledelayedexpansion
:loop
#set /a licz=licz+1
#set /p "=Waiting time: %licz% seconds!ASCII_13!" <NUL
#Timeout /T 1 /Nobreak > NUL
#GOTO loop
If you do not like the cursor blinking at beginning of the line, transfer ASCII_13 to the beginning to execute CR before text.
Needs to be preceeded by any ASCII character, though, to avoid getting stripped. And this will be visible as the last char on the line, so be wary here :)
#set /p "=.!ASCII_13!Waiting time: %licz% seconds" <NUL

#echo off
setlocal enableextensions enabledelayedexpansion
Rem Get a carriage return character
set "CR=" & for /f %%a in ('copy /Z "%~f0" nul') do if not defined CR set "CR=%%a"
rem The progress bar
set "fill=[###################]"
echo(
rem For each character in the fill
for /l %%a in (2 3 21) do (
rem Calculate the right part of the bar
set "spaces=!fill:~%%a!"
rem Output the left and right parts of the bar and carriage return
<nul set/p ".=:: Please Connect Device : !fill:~0,%%a!!spaces:#= !!CR!"
rem Pause for a second
ping -n 2 "" > nul
)
echo(

Related

How to extract the last word from the last line of a TXT file through a batch script

I am trying to extract the last word from the last line of a txt file.
The result I want is just Cup$2!.
This is what I tried:
#echo off
SetLocal EnableDelayedExpansion
set L=1
for /F "tokens=2 delims=" %%a in (corner.txt) do (
set line=%%a
if !L!==7 set Line7=%%a
set /a L=!L!+1
)
echo The word is %Line7%
pause
The result I'm getting is The word is.
What should I edit to get the above result?
Get line count.
for /f "tokens=3*" %%i in ('find /c /v /n /i"" corner.txt') do set /a v=%%i-1
Then get the last values from 7-th word of the last line:
for /f "tokens=7*" %%a in ('more corner.txt +%v%') do set "String="%%b""
Variable %String% keeps the values framed by double quotes: Cup&2!/Cup$2!
If you use / as delimiter you can get last value:
for /f "delims=/ tokens=2*" %%a in (%String%) do #echo %%a
Here's a quick example of how you could capture the substring you require, from reading your code, i.e. the last substring on the seventh line:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "var="
For /F UseBackQ^ Skip^=7^ Delims^=^ EOL^= %%G In ("corner.txt") Do Set "var=%%~nxG" & GoTo Next
If Not Defined var GoTo EndIt
:Next
SetLocal EnableDelayedExpansion
Echo The word is !var!
EndLocal
:EndIt
Echo Press any key to close . . .
Pause 1>NUL
EndLocal
Exit /B
If however, as your question title and body asks, you want the last substring of the last line, it's a little bit simpler:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "var="
For /F UseBackQ^ Delims^=^ EOL^= %%G In ("corner.txt") Do Set "var=%%~nxG"
If Not Defined var GoTo EndIt
SetLocal EnableDelayedExpansion
Echo The word is !var!
EndLocal
:EndIt
Echo Press any key to close . . .
Pause 1>NUL
EndLocal
Exit /B
I will not however be explaining any of it, please use the search facility at the top of the page, and the output from the built in help for each of the commands I have used.
Parsing strings in batch files is never ideal but this seems to work:
#echo off
setlocal ENABLEEXTENSIONS DISABLEDELAYEDEXPANSION
echo.line 1 > "%temp%\SO_Test.txt"
echo. >> "%temp%\SO_Test.txt"
echo.foo^&bar!baz hello/world Cup^&2!/Cup$2!>> "%temp%\SO_Test.txt"
goto start
:start
set "line="
for /F "usebackq delims=" %%a in ("%temp%\SO_Test.txt") do #set line=%%a
:word
set "b="
rem Delims is slash, tab, space and the order is important
for /F "tokens=1,* delims=/ " %%A in ("%line%") do (
set "line=%%A"
set "b=%%B"
)
if not "%b%" == "" (
set "line=%b%"
goto word
)
echo.Last word is "%line%"
#ECHO OFF
SETLOCAL
rem The following settings for the source directory, destination directory, target directory,
rem batch directory, filenames, output filename and temporary filename [if shown] are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "filename1=%sourcedir%\q70761955.txt"
SET "amper=&"
FOR /f "usebackq delims=" %%b IN ("%filename1%") DO SET "lastword=%%b"
:: Replace ampersands with spaces, then slashes with spaces
CALL SET "lastword=%%lastword:%amper%= %%"
SET "lastword=%lastword:/= %"
FOR %%b IN (%lastword%) DO SET "lastword=%%b"
ECHO last "word" is -^>%lastword%^<-
GOTO :EOF
Read each line of the file, retaining the last-read line in lastword.
Replace each "delimiter" character (& and / are implied in the question. Others - no information.)
Select the last "word" in the last line.
You would need to change the value assigned to sourcedir to suit your circumstances. The listing uses a setting that suits my system.
I deliberately include spaces in names to ensure that the spaces are processed correctly.
I used a file named q70761955.txt containing your data for my testing.
You could use this in a batch-file run by cmd on windows.
FOR /F "delims=" %%A IN ('powershell -NoLogo -NoProfile -Command ^
"((Get-Content -Path 'corner.txt' -Last 1) -split '[\s*/]')[-1]"') DO (SET "LINE7=%%A")
ECHO %LINE7%
If the script were written in PowerShell instead of cmd language, it could be a one-liner.
$Line7 = ((Get-Content -Path 'corner.txt' -Last 1) -split '[\s*/]')[-1]

Update information without creating new line in batch

The script I had been working on reads all folders on the CD-ROM drive "i" and searches for mp3 files, copying them onto the destination "e:\MP3\new".
#ECHO off
setlocal enabledelayedexpansion
cd /d "i:\"
set count=1
for /r %%d in (*.mp3) do (
set /a count+=1
)
echo There were %count% files found
set countb=1
for /r %%g in (*.mp3) do (
set /a countb+=1
echo|set /p = File: !countb!/%count%
copy "%%g" "e:\MP3\new" > nul
)
endlocal
Let´s suppose in this example that 115 files were found.
What I get from the above code:
There were 115 files found
File: 1/115 File: 2/115 File: 3/115 File: 4/115 File: 5/115 (...) File: 115/115
What I want:
There were 115 files found
File: X/115 where X will be constantly updated on the screen each time an mp3 file is successfully copied
Any help to fix that?
Firstly, you are initialising your counters wrongly, they should be set to zero (like set count=0) rather than one to get the correct numbers.
Secondly, you should replace echo|set /p = by < nul set /P =, because the pipe (|) is slower than simple (input) redirection (<) since it creates new cmd instances for either side.
To move the cursor in the Command Prompt window back to the beginning of the current line, you need to write the carriage-return character first. However, you cannot use this character as the first one with set /P, because it is going to be removed, together with other leading white-space characters. So you need another invisible character preceding the carriage-return that is not going to be removed; let us choose the back-space character.
Therefore, the fixed code may look like this, for instance:
#echo off
setlocal EnableDelayedExpansion
rem // Gather the back-space character:
for /F %%B in ('prompt $H ^& for %%Z in ^(.^) do rem') do set "BS=%%B"
rem // Gather the carriage-return character:
for /F %%C in ('copy /Z "%~f0" nul') do set "CR=%%C"
cd /D "I:\"
set /A "count=0"
for /R %%d in ("*.mp3") do (
set /A "count+=1"
)
echo There were %count% files found.
set /A "index=0"
for /R %%g in ("*.mp3") do (
set /A "index+=1"
< nul set /P ="%BS%!CR!File: !index!/%count%"
copy "%%~g" "E:\MP3\new\" > nul
)
endlocal

Reading line by line from one file and write to another file using batch script

In below code i am tring to fetch the line no of string "AXX0000XXXA" from file data.txt,then fetching line by line and printing target.txt file,in between if the line reach the find line no i am adding one more line from file temp.txt.The code is working fine with the less nos of records(tested with 150 lines-File Size 100 kb),but when i am processing with 50K records(File Size 25MB) it is taking more then 25 minutes to process.could you please help me how i will process same in less time.
#echo off
setlocal enabledelayedexpansion
for /f "delims=:" %%a in ('findstr /n "AXX0000XXXA" "C:\Users\23456\Desktop\data.txt"') do (set find_line=%%a)
set /a counter=0
for /f "usebackq delims=" %%b in (`"findstr /n ^^ C:\Users\23456\Desktop\data.txt"`) do (
set curr_line=%%b
set /a counter=!counter!+1
if !counter! equ !find_line! (
type temp.txt >> target.txt
)
call :print_line curr_line
)
endlocal
:print_line
setlocal enabledelayedexpansion
set line=!%1!
set line=!line:*:=!
echo !line!>>target.txt
endlocal
Your code uses three Batch file constructs that are inherently slow: call command, >> append redirection and setlocal/endlocal, and these constructs are executed once per each file line! It would be faster to include the subroutine into the original code to avoid the call and setlocal commands, and an echo !line!>>target.txt command imply open the file, search for the end, append the data and close the file, so it is faster to use this construct: (for ...) > target.txt that just open the file once. An example of a code with such changes is in Compo's answer.
This is another method to solve this problem that may run faster when the search line is placed towards the beginning of the file:
#echo off
setlocal enabledelayedexpansion
for /f "delims=:" %%a in ('findstr /n "AXX0000XXXA" "C:\Users\23456\Desktop\data.txt"') do (set /A find_line=%%a-1)
call :processFile < "C:\Users\23456\Desktop\data.txt" > target.txt
goto :EOF
:processFile
rem Duplicate the first %find_line%-1 lines
for /L %%i in (1,1,%find_line%) do (
set /P "line="
echo !line!
)
rem Insert the additional line
type temp.txt
rem Copy the rest of lines
findstr ^^
exit /B
This should create target.txt with content matching data.txt except for an inserted line taken from tmp.txt immediately above the line matching the search string, AXX0000XXXA.
#Echo Off
Set "fSrc=C:\Users\23456\Desktop\data.txt"
Set "iSrc=temp.txt"
Set "sStr=AXX0000XXXA"
Set "fDst=target.txt"
Set "iStr="
Set/P "iStr="<"%iSrc%" 2>Nul
If Not Defined iStr Exit/B
Set "nStr="
For /F "Delims=:" %%A In ('FindStr/N "%sStr%" "%fSrc%" 2^>Nul') Do Set "nStr=%%A"
If Not Defined nStr Exit/B
( For /F "Tokens=1*Delims=:" %%A In ('FindStr/N "^" "%fSrc%"') Do (
If "%%A"=="%nStr%" Echo %iStr%
Echo %%B))>"%fDst%"
I have made it easy for you to change your variable data, you only need to alter lines 3-6.
I have assumed that this was your intention, your question was not clear, please accept my apologies if I have assumed incorrectly.

Redirecting contents of a file to a variable in command prompt

I have a file "file.txt" which contains the output of "dir /s /b *.c"
I want to write this whole content of file.txt in a single variable .
Any ideas?
The usual way to treat questions like this one is to reply: "What do you want this for?". However, your question is pure and simple, so here is the answer. The Batch file below not just store the contents of file.txt in a single variable, but it also later process the variable value as individual lines.
EDIT: I added the method to extract individual lines from the variable value as substrings.
#echo off
setlocal EnableDelayedExpansion
rem Create variables with LF and CR values:
set LF=^
%empty line 1/2, don't remove%
%empty line 2/2, don't remove%
for /F %%a in ('copy /Z "%~F0" NUL') do set CR=%%a
rem Store the contents of file.txt in a single variable,
rem end each line with <CR><LF> bytes
set "variable="
for /F "delims=" %%a in (file.txt) do (
set "variable=!variable!%%a!CR!!LF!"
)
rem 1- Show the contents of the variable:
echo !variable!
rem 2- Process the contents of the variable line by line
echo/
set i=0
for /F "delims=" %%a in ("!variable!") do (
set /A i+=1
echo Line !i!- %%a
)
rem Get the starting position and length of each line inside the variable
set /A i=0, lastStart=0
for /F "delims=:" %%a in (
'(cmd /V:ON /C set /P "=^!variable^!" ^& echo/^) ^<NUL ^| findstr /O "^^"'
) do (
set /A len[!i!]=%%a-lastStart-2, i+=1
set /A start[!i!]=%%a, lastStart=%%a
)
set "len[0]="
set "start[%i%]="
set /A lastLine=i-1
rem 3- Extract individual lines from the variable contents as substrings
:getNumber
echo/
set "num="
set /P "num=Enter line number (nothing to end): "
if not defined num goto end
if %num% gtr %lastLine% echo Invalid number & goto getNumber
for /F "tokens=1,2" %%i in ("!start[%num%]! !len[%num%]!") do (
echo Line %num%- !variable:~%%i,%%j!
)
goto getNumber
:end
You must note that Batch variables can only store a maximum of 8K characters.
No. But you can go through the lines one by one.
for /f "delims=" %A in (file.txt) do echo %A
Maybe say what you are trying to achieve. Knowledge of C won't help you in batch because it's contary for historical reasons.
You can use set /p:
set /p foo=<file.txt
See also this question.
Batch-Script is a very limited tool with a primitive support for multi-line variables (with specific hacks that will not work as expected for this sceneario but if you are interested see this).
The only reasonable solution is to move to a capable language, which is every else less Batch-Script.

Windows batch: delayed expansion in a for loop

I want to modify a few specific lines of numbers of text files, and I wrote a batch file as follows:
#echo off
set n=0
set n1=10
set n2=40
cd.>output.txt
for /f "delims=" %%i in ('findstr /n .* test.txt') do (
set "var=%%i"
setlocal enabledelayedexpansion
set /a n=!n!+1
echo.!n!
set var=!var:*:=!
rem if !n!=%n1% ...
rem if !n!=%n2% ...
(echo.!var!)>>output.txt
endlocal
)
start output.txt
However, this doesn't work as expected.
After some tests, I think the !n! expansion is not normally delayed. That is very strange, because !var! expansion is normally delayed.
By the way, the setlocal enabledelayedexpansion and endlocal commands are put in the for loop, because otherwise the special character ! will be abandoned.
I suppose the problem what you see is that n will never increase.
But that isn't a problem of the delayed expansion, it's an effefct of the setlocal/endlocal block inside the loop.
As #panda-34 mentioned you should use the extended syntax of set/a and move the statement outside the setlocal/endlocal block.
#echo off
set n=0
set n1=10
set n2=40
(
for /f "delims=" %%i in ('findstr /n .* test.txt') do (
set "var=%%i"
set /a n+=1
setlocal enabledelayedexpansion
echo !n!
set var=!var:*:=!
rem if !n!=%n1% ...
rem if !n!=%n2% ...
(echo(!var!)
endlocal
)
) >output.txt
start output.txt

Resources