I'm trying to read the output of a command (which outputs into multiple lines), and use an arbitrary number of those lines. Because I know neither the number of total lines, nor the number of lines that will be used, I need to analyse and possibly use each line in a loop, which is why I have setlocal enabledelayedexpansion.
Below is a snippet of the code that shows the process of taking the command and reading each line (not using it yet, just reading it to make sure this works (which it doesn't)):
#echo off
setlocal enabledelayedexpansion
for /f "tokens=*" %%i in ('svn status') do (
echo %%i
set file=%%i
echo *!file!
)
The problem that I'm running into is that the %%i values that are being read in are not correct in the for line. The first character is missing from the first line of the input (which is important because I use the first line to decide whether or not to use that line).
The output I get from my code looks like this:
Dir0\TestDoc7.txt
? StatusFile.txt
Whereas if I run this code:
copy /y NUL StatusFile.txt >NUL
>StatusFile.txt (
svn status
)
(Which is just another way of me seeing what the real output of svn status is) I get a proper output into the text file:
! Dir0\TestDoc7.txt
? StatusFile.txt
I'm probably making a fairly clear mistake as I'm rather new to batch scripting.
Thanks in advance.
The cause is EnableDelayedExpansion which will eat the exclamation marks,
Your choice of tokens=* will also strip all leading spaces from the lines.
#echo off
for /f "tokens=1*" %%A in ('svn status') do (
if "%%A" equ "!" (
Rem do whatever
) else If "%%A" equ "?" (
Rem do something else rest of the line is in %%B
) else (
Rem no ! or ? first space sep. content is in %%A rest of the line is in %%B
)
)
Related
I have a batch file with more command to skip the first few lines of the file and print the rest. I am using more +6 file_name. I see that it opens some percentage of the file and asks the user to enter the prompt so that it can load the next portion. I tried redirecting the output of the more command to a file using the > operation to another file and still have the same problem.
https://ss64.com/nt/more.html
When MORE is used without any redirection symbols it will display the percent complete e.g.
MORE /E myfile.txt
--More (17%) --
Thanks,
Pavan.
The more command prompts for user input even when you redirect its output to a file, as soon as 64K lines have been encountered. In addition, more expands TABs to SPACEs.
A work-around is to use a for /F loop to skip the first 6 lines:
> "outfile.txt" (
for /F "usebackq skip=6 delims=" %%L in ("infile.txt") do #(
echo(%%L
)
)
This however limits the line length to 8K characters/bytes. Furthermore, this skips empty lines.
To keep empty lines, you could do this:
> "outfile.txt" (
for /F "skip=6 delims=" %%L in ('findstr /N "^" "infile.txt"') do #(
set "LINE=%%L"
setlocal EnableDelayedExpansion
echo(!LINE:*:=!
endlocal
)
)
This precedes every line by a line number and a colon using findstr intermittently, so no line appears empty to for /F; the prefix is removed when writing out the lines then. The line length is still limited though.
You may use this simpler method:
< myfile.txt (
rem Skip the first 6 lines
for /L %%i in (1,1,6) do set /P "="
rem Show the rest
findstr "^"
)
This method may fail if the skipped lines are longer than 1023 characters, that is the limit of set /P command.
I want to make a program that takes the content of the second line of a text file and puts it on the first. (It doesn't matter if the second doesn't get edited)
for /f "tokens=1" %%t in (file.txt) do set string1=%%t
for /f "tokens=2" %%t in (file.txt) do set string2=%%t
echo %string1%%string2%>file.txt
I have two issues hat I can't seem to be able to fix.
One: the loops only include the first word of each line in the variables.
Two: Echo doesn't replace the first line of the file with the variables given and instead writes ECHO command deactivated (I have the French version of Windows 10 and simply translated what got written in the file, the text in English Windows version might be slightly different, but you get the idea)
If you have any suggestions, I would appreciate if you explain what the code you provide does (I always like to learn)
Your question is not clear and can be understood in several different ways. Anyway, this management is simpler with no for command:
#echo off
setlocal EnableDelayedExpansion
< file.txt (
rem Takes the content of the first line
set /P "line1="
rem Takes the content of the second line and puts it on the first
set /P "line2="
echo !line1!!line2!
rem It doesn't matter if the second line doesn't get edited
echo !line2!
rem Copy the rest of lines
findstr "^"
) > output.txt
move /Y output.txt file.txt
The FOR command uses a space as a delimiter by default. So you have to tell it to not use any delimiters with the DELIMS option. Also, you should be able to do this with a single FOR /F command. Just hold the previous line in a variable.
#ECHO OFF
setlocal enabledelayedexpansion
set "line1="
(for /f "delims=" %%G in (file.txt) do (
IF NOT DEFINED line1 (
set "line1=%%G"
) else (
echo !line1!%%G
set "line1="
)
)
REM If there are an odd amount of lines, line1 will still be defined.
IF DEFINED line1 echo !line1!
)>File2.txt
EDIT: I think I completely misunderstood your question. Once you clarify your question I will repost a code solution if needed.
Use skip to omit the first line and write the 2nd line twice. In general an edit of a file implies a rewrite to a new file and possibly a rename to retain the old file name.
:: Q:\Test\2018\07\25\SO_51508268.cmd
#Echo off
Set "flag="
( for /f "usebackq skip=1 delims=" %%A in ("file1.txt") Do (
If not defined flag (
Echo=%%A
Set flag=true
)
Echo=%%A
)
) >file2.txt
Del file1.txt
Ren file2.txt file1.txt
After running the batch a file1.txt with initially numbered lines 1..5 looks like this:
> type file1.txt
2
2
3
4
5
I need to parse a text file.
I want to find the firstline in the text file
: the first line to find
set firstLine=------------------------------------------------------------------------------------------------------------------
and find the last line
:: the last line to find
set lastLine=*******************************************************************************************************************
Then I need to export to a new file everything between those two line.
echo >> M:\TESTING\Output.txt
I'm a beginner with this and I've searched for days, but am not finding how to do this.
I looked at for loops and if statements, but I'm still puzzled.
for /f "tokens=1 delims= " %%f in (M:\TESTING\*.txt) do (
:: sets then the line variable to the line just read
set line=%%f
:: the first line to find
set firstLine=------------------------------------------------------------------------------------------------------------------
:: the last line to find
set lastLine=*******************************************************************************************************************
Then if %line% = %fistLine% start the export.....
Any direction will be appreciated. thanks.
#DennisvanGils' approach is a good start and will do well in many cases.
However, it will not produce an exact copy of the text file content between the given lines:
leading whitespaces (SPACE and TAB) will be removed (due to tokens=* option),
lines starting with ; will be skipped (due to the default option eol=; of for /F), and
empty lines will be skipped as well (as for /F always skips such).
To get an exact copy of the text file portion, you could use the following code snippet:
#echo off
set "INFILE=M:\TESTING\input.txt"
set "OUTFILE=M:\TESTING\output.txt"
set "FIRSTLINE=------------------------------------------------------------------------------------------------------------------"
set "LASTLINE=*******************************************************************************************************************"
setlocal EnableExtensions DisableDelayedExpansion
set "FLAG="
> "%OUTFILE%" (
for /F "delims=" %%L in ('findstr /N "^" "%INFILE%"') do (
set "LINE=%%L"
setlocal EnableDelayedExpansion
set "LINE=!LINE:*:=!"
if "!LINE!"=="%FIRSTLINE%" (
endlocal
set "FLAG=TRUE"
) else if "!LINE!"=="%LASTLINE%" (
endlocal
goto :CONTINUE
) else if defined FLAG (
echo(!LINE!
endlocal
) else (
endlocal
)
)
)
:CONTINUE
endlocal
Core function here is findstr, which is configured so that every line in the file is returned, prefixed with a line number and a colon :. Its output is then parsed by a for /F loop. Because of the prefix, no line appears to be empty and therefore every one is iterated by the loop. In the body of the loop, the prefix is removed by the set "LINE=!LINE:*:=!" command for each line.
The variable FLAG is used to decide whether or not the current line is to be output; if it is defined, that is, a value is assigned, the command echo !LINE! is executed; otherwise it is not. FLAG is set if the current line matches the string in %FIRSTLINE%; if the line matches the string in %LASTLINE%, a goto command is executed which breaks the loop. This means also that only the first block between %FIRSTLINE% and %LASTLINE% matches is output.
If there might occur multiple %FIRSTLINE% and %LASTLINE% matches within the text file and you want to output every block, replace the goto command line by set "FLAG=".
Note that this approach does not check whether %FIRSTLINE% occurs before %LASTLINE%, nor does it even check for existence of %LASTLINE% (all remaining lines to the end of file are output in case). If all this is important, the logic need to be improved and even a second loop will be required most likely.
What you should do in this case is use a variable like a boolean to know if you encountered the startline and endline yet, and to know if you have to output the lines.
Also, you should use setlocal ENABLEDELAYEDEXPANSION with ! instead of % so you can change variables in loops and ifs (for more information about that, see this. The usage of () after if is not needed in this case, since the if is on one line, but they make things easier to read in my opinion. If you want to output the start and endline too, switch the checks for the start and endlines.
#echo off & setlocal ENABLEDELAYEDEXPANSION
set start=0
:: the first line to find
set firstLine=------------------------------------------------------------------------------------------------------------------
:: the last line to find
set lastLine=*******************************************************************************************************************
for /F "tokens=*" %%A in (TEST.txt) do (
:: sets then the line variable to the line just read
set line=%%A
if "!line!"=="!lastLine!" (set start=0)
if !start! equ 1 (echo !line!>>TESTOUTPUT.txt)
if "!line!"=="!firstLine!" (set start=1)
)
This should do what you want. Note that when you encounter a startline a second time it starts reading again.
I am relearning DOS/batch & seems different.
Script should create a text file (it does, verified) then read it line by line looking for a beginning word and discarding lines thru that line as it builds TMP.txt.
It should then create a file of remaining lines up to and including a finishing word and ignore the remainder.
Then it will look for a line containing a specific word.
Problem: I get ("inFile.txt") was unexpected. The reams of text I have pulled down into my reference directory has not been helpful as it appears (does't it always) correct from what I expect.
Here is the script to and a bit after that point:
REM Append a final line to input text
echo IDSS >> inFile.txt
REM Bookend the node's text with BEGwd and FINwd.
REM Read inFile.txt file- Ignore %%A if BEGwd not found,
REM else write remaining lines to TMP.
for /F "tokens=*" %%A ("inFile.txt") DO ( <<---- Problem line
if "*%BEGwd%*" == "*%%A*" echo "%%A" >> TMP.txt
)
REM Read TMP look for FINwd- Write non-%%B lines to TMP2
REM until FINwd
for /F "tokens=*" %%B ("TMP.txt") DO (
if "*%FINwd%*" == "*%%B*" goto SEARCH
echo "%%B" >> TMP2.txt
)
Am also unsure if batch will accept my IF command compare with *'s?
Appreciating any assistance there too.
for /F "tokens=*" %%A ("inFile.txt") DO ( <<---- Problem line
Correct syntax is for /F "USEBACKQtokens=*" %%AIN("inFile.txt") DO (
the usebackq option is required because you have "quoted your filename".
I am trying to find and replace. I am using a for loop. I am learning but I am almost there. I have tried to find the answer but the sources have been a bit too confusing for me.
The delim is a blank space and as you can tell I am skipping 4 lines and doing the 2nd token.
I need what is found there at that spot to be replaced by var5a. I have it backwards, as I need %%F to equal var5a, not the other way around (as I have it written now). But don't know how to write it. Please explain how one can do this. I've tried using <<= but with no luck.
for /f "skip=4 tokens=2 delims= " %%F in (script.vbs) do (
set var5a=!var5a!%%F
)
I'm learning so please be kind.
#ECHO OFF
SETLOCAL
:: Replace token 2 (space-separated) in line 5 of a file with REPLACEMENT
:: Assumed the file exists, etc. and no line begins ":"
SET replacement=THIS IS THE REPLACEMENT TEXT
DEL newfile.txt 2>nul
FOR /f "tokens=1*delims=:" %%i IN ('findstr /n /r "$" ^<oldfile.txt') DO (
IF %%i==5 (
FOR /f "tokens=1,2* delims= " %%L IN ("%%j") DO >>newfile.txt ECHO %%L %replacement% %%N
) ELSE (>>newfile.txt ECHO.%%j)
)
TYPE oldfile.txt
ECHO ==== separator =======
FC oldfile.txt newfile.txt
Results:
Line one should not be changed
Line two should not be changed
Line three should not be changed
Line four should not be changed
changeme iwillbereplaced but only on this line
and notbereplaced on subsequent lines
including the previous line which was empty
==== separator =======
Comparing files oldfile.txt and NEWFILE.TXT
***** oldfile.txt
Line four should not be changed
changeme iwillbereplaced but only on this line
and notbereplaced on subsequent lines
***** NEWFILE.TXT
Line four should not be changed
changeme THIS IS THE REPLACEMENT TEXT but only on this line
and notbereplaced on subsequent lines
*****
There are difficulties - especially if the lines in your file start with a colon or contain quoted-strings or any of the usual batch gotchas like %
So - at long last I've worked out what you're doing. You omitted the critical information that the string in %%F is not only quoted, it also contains spaces.
Had you said that in the first place, you'd be hours further into your project, and I'd be richer by a handful of aspirin.
In order to load %%F with a quoted string, I've read the string from a file.
#ECHO OFF
SETLOCAL enabledelayedexpansion
:: Replace token 2 (space-separated) in line 5 of a file with REPLACEMENT
:: Assumed the file exists, etc. and no line begins ":"
SET replacement="THIS IS THE REPLACEMENT TEXT"
DEL newfile.txt 2>NUL
:: iwbr.txt just contains
:: "i will be replaced"
:: on a single line for loading into %%F as that is the target to be replaced
FOR /f "delims=" %%F IN (iwbr.txt) DO (
FOR /f "tokens=1*delims=:" %%i IN (
'findstr /n /r "$" ^<oldfile.txt'
) DO >>newfile.txt (
IF %%i==5 (
SET newline=%%j
CALL SET newline=%%newline:%%F=%replacement%%%
ECHO.!newline!
) ELSE (ECHO.%%j)
)
)
TYPE oldfile.txt
ECHO ==== separator =======
FC oldfile.txt newfile.txt
Results:
Line one should not be changed
Line two should not be changed
Line three "should" not be changed
Line four should not be changed
changeme "i will bereplaced" but only on this line
and notbereplaced on subsequent lines
including the "previous" line which was empty
including this "unbalanced-quote line...
==== separator =======
Comparing files oldfile.txt and NEWFILE.TXT
***** oldfile.txt
Line four should not be changed
changeme "i will bereplaced" but only on this line
and notbereplaced on subsequent lines
***** NEWFILE.TXT
Line four should not be changed
changeme "THIS IS THE REPLACEMENT TEXT" but only on this line
and notbereplaced on subsequent lines
*****
If you have Henri in %%F and you want to set the value of Henri to the current value of var5a then
set %%F=!var5a!
(!var5a! if you want the RUN-TIME value of var5a (provided you have ENABLEDELAYEDEXPANSION mode invoked) or %var5a% if you want the PARSE-TIME value)
Program to demonstrate that the answer does work:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
::: WARNING! This will overwrite SCRIPT.VBS
DEL script.vbs 2>nul
FOR /l %%i IN (1,1,4) DO >>script.vbs ECHO ignore this line %%i
>>script.vbs ECHO Oh Henri - it does work
>>script.vbs ECHO Now try again
ECHO ===== here is script.vbs
TYPE script.vbs
ECHO ===== script.vbs ends
SET var5a=this is the value
SET henri
SET var5a
ECHO ===== now run the FOR loop...
for /f "skip=4 tokens=2 delims= " %%F in (script.vbs) do (
set %%F=!var5a!
)
SET henri
GOTO :eof
Results:
===== here is script.vbs
ignore this line 1
ignore this line 2
ignore this line 3
ignore this line 4
Oh Henri - it does work
Now try again
===== script.vbs ends
Environment variable henri not defined
var5a=this is the value
===== now run the FOR loop...
Henri=this is the value
Now - what do you mean by "it doesn't work"? It certainly does. If it's not doing what you want it to do, then demonstrate WHY you make the claim, otherwise we're left guessing.
For instance, the above will also set try to the value because Henri isn't the ONLY token that's number 2, space-delimited after 4 lines of the source file. That's easy to fix (but I'm not a mindreader - just a humble programmer)
(set notsetyet=Y)
for /f "skip=4 tokens=2 delims= " %%F in (script.vbs) do (
IF DEFINED NOTSETYET set %%F=!var5a!&(set notsetyet=)
)
But maybe "it didn't work" because it Henri was set to the wrong value. Or perhaps it crashed. We can't tell from your response.