I am trying to replicate a file without the 1st line.
But I can't seem to copy it's contents properly.
This is how I tried:
for /f "tokens=* skip=1" %%i in (input.txt) do (
echo %%i >> "output.txt"
)
When my input.txt have this:
test1
1. test item1
2. test item2
3. test item3
It gives me this:
1. test item1
2. test item2
3. test item3
Expected Output:
1. test item1
2. test item2
3. test item3
How do I achieve this?
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
SET "filename1=%sourcedir%\q47067655.txt"
SET "outfile=%destdir%\outfile.txt"
(FOR /f "tokens=1*skip=1delims=:" %%a IN ('findstr /n /r ".*" "%filename1%"') DO ECHO(%%b)>"%outfile%1"
(FOR /f "tokens=1*skip=1delims=]" %%a IN ('find /n /v "" ^<"%filename1%"') DO ECHO(%%b)>"%outfile%2"
(more +1 "%filename1%">"%outfile%3")
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
You would need to change the settings of sourcedir and destdir to suit your circumstances.
I used a file named q47067655.txt containing your data for my testing.
Produces the files defined as %outfile%*
for /f will skip empty lines, so ensure the lines are not empty by numbering them.
Note that echo(%%a will produce an empty line with empty %%a
Your example output omits the blank line between the skipped line and the next.
There is no way to do this with for /f, the documentation from for /? is quite adamant:
Blank lines are skipped.
For a simple need like this, it can be met with the Windows more program, which can start at an arbitrary line and will not actually page the file if it's redirected (up to a certain size anyway). That means your entire script can be replaced with a single command:
c:\users\paxdiablo> more +2 input.txt >output.txt
c:\users\paxdiablo> type output.txt
1. test item1
2. test item2
3. test item3
For more complex tasks or if your files are bigger than what more can handle without paging, you should probably stop using (the frankly brain-dead) older Windows tools and switch instead to PowerShell.
Or get yourself some decent text processing tools for Windows such as sed or awk (see GnuWin32), after which you can just do something like (for your case, though the script can now be arbitrarily complex):
c:\users\paxdiablo> awk "NR>2{print}" input.txt >output.txt
Related
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've got some *.Xml files in a directory and its sub-directories. I need to loop through the XML files which have a specific constant at the end of their file name, and then echo/print their names without the constant part nor the extension (.Xml).
For example: these are the file names I have:
FileAAA_Constant.Xml
FileBBB.Xml
FileCCC.Xml
FileDDD_Constant.Xml
And this is the output I need:
FileAAA
FileDDD
I've tried this command:
For /R %%X in (*_Constant.Xml) do echo %%~nX
Which outputs this:
FileAAA_Constant
FileDDD_Constant
As you can see, it has removed the extension only, while I need to remove "_Constant.Xml" as well.
This works if the file names contains only one underscore, as indicated in your example:
for /F "delims=_" %%X in ('dir /S /B *_Constant.Xml') do echo %%X
If the desired file names may contain more than one underscore, use Pokechu22's answer.
Fairly easy if the exact length of the phrase is known; you just need to use the %var:~0,-3% syntax. Since "_Constant" is 9 chars long, you would want %var:~0,-9%, which takes text from the start (0) to 9 chars from the end (-9). Aditionally, delayed variable expansion also must be enabled with setlocal ENABLEDELAYEDEXPANSION for this to be run inside of your For loop.
Here's a full example:
#Echo off
setlocal ENABLEDELAYEDEXPANSION
for /R %%X in (*_Constant.Xml) do (
set FileNameTemp=%%~nX
echo !FileNameTemp:~0,-9!
)
Note that if you have a file named just _Constant.xml, this will produce "ECHO is off." rather than "" (no output). This can be solved by changing echo !FileNameTemp:~0,-9! to echo. !FileNameTemp:~0,-9!, but that puts a space before each output.
Here's the loop alone:
for /R %%X in (*_Constant.Xml) do (
set FileNameTemp=%%~nX
echo !FileNameTemp:~0,-9!
)
This question already has answers here:
Set output of a command as a variable (with pipes) [duplicate]
(6 answers)
Closed 7 years ago.
I need to run a simple find command and redirect the output to a variable in a Windows Batch File.
I have tried this:
set file=ls|find ".txt"
echo %file%
But it does not work.
If I run this command it works without problems:
set file=test.txt
echo %file%
So obviously my command output is not being set to my variable. Can anyone help? Thanks
I just find out how to use commands with pipes in it, here's my command (that extracts the head revision of an svn repo) :
SET SVN_INFO_CMD=svn info http://mySvnRepo/MyProjects
FOR /f "tokens=1 delims=" %%i IN ('%SVN_INFO_CMD% ^| find "Revision"') DO echo %%i
First of all, what you seem to expect from your question isn't even possible in UNIX shells. How should the shell know that ls|find foo is a command and test.txt is not? What to execute here? That's why UNIX shells have the backtick for such things. Anyway, I digress.
You can't set environment variables to multi-line strings from the shell. So we now have a problem because the output of ls wouldn't quite fit.
What you really want here, though, is a list of all text files, right? Depending on what you need it's very easy to do. The main part in all of these examples is the for loop, iterating over a set of files.
If you just need to do an action for every text file:
for %%i in (*.txt) do echo Doing something with "%%i"
This even works for file names with spaces and it won't erroneously catch files that just have a .txt in the middle of their name, such as foo.txt.bar. Just to point out that your approach isn't as pretty as you'd like it to be.
Anyway, if you want a list of files you can use a little trick to create arrays, or something like that:
setlocal enabledelayedexpansion
set N=0
for %%i in (*.txt) do (
set Files[!N!]=%%i
set /a N+=1
)
After this you will have a number of environment variables, named Files[0], Files[1], etc. each one containing a single file name. You can loop over that with
for /l %%x in (1,1,%N%) do echo.!Files[%%x]!
(Note that we output a superfluous new line here, we could remove that but takes one more line of code :-))
Then you can build a really long line of file names, if you wish. You might recognize the pattern:
setlocal enabledelayedexpansion
set Files=
for %%i in (*.txt) do set Files=!Files! "%%i"
Now we have a really long line with file names. Use it for whatever you wish. This is sometimes handy for passing a bunch of files to another program.
Keep in mind though, that the maximum line length for batch files is around 8190 characters. So that puts a limit on the number of things you can have in a single line. And yes, enumerating a whole bunch of files in a single line might overflow here.
Back to the original point, that batch files have no way of capturing a command output. Others have noted it before. You can use for /f for this purpose:
for /f %%i in ('dir /b') do ...
This will iterate over the lines returned by the command, tokenizing them along the way. Not quite as handy maybe as backticks but close enough and sufficient for most puposes.
By default the tokens are broken up at whitespace, so if you got a file name "Foo bar" then suddenly you would have only "Foo" in %%i and "bar" in %%j. It can be confusing and such things are the main reason why you don't ever want to use for /f just to get a file listing.
You can also use backticks instead of apostrophes if that clashes with some program arguments:
for /f "usebackq" %%i in (`echo I can write 'apostrophes'`) do ...
Note that this also tokenizes. There are some more options you can give. They are detailed in the help for command.
set command has /p option that tells it to read a value from standard input. Unfortunately, it does not support piping into it, but it supports reading a value from a first line of existing file.
So, to set your variable to the name of a first *.txt file, you could do the following:
dir /b *.txt > filename.tmp
set /p file=< filename.tmp
del /q filename.tmp
It is important not to add a space before or even after =.
P. S. No fors, no tokens.
Here's a batch file which will return the last item output by find:
#echo off
ls | find ".txt" > %temp%\temp.txt
for /f %%i in (%temp%\temp.txt) do set file=%%i
del %temp%\temp.txt
echo %file%
for has a syntax for parsing command output, for /f "usebackq", but it cannot handle pipes in the command, so I've redirected output to a temporary location.
I strongly recommend, given that you have access to ls, that you consider using a better batch language, such as bash or even an scripting language like python or ruby. Even bash would be a 20x improvement over cmd scripting.
The short answer is: Don't!
A windows shell env var can hold a max of 32 Kb and it isn't safe to save output from programs in them.
That's why you can't. In batch script you must adopt another programming style. If you need all of the output
from the program then save it to file. If you only need to check for certain properties then pipe the output into
a program that does the checking and use the errorlevel mechanism:
#echo off
type somefile.txt | find "somestring" >nul
if %errorlevel% EQU 1 echo Sorry, not found!
REM Alternatively:
if errorlevel 1 echo Sorry, not found!
However, it's more elegant to use the logical operators Perl style:
#echo off
(type somefile.txt | find "somestring" >nul) || echo Sorry, not found!
It's not available in DOS, but in the Windows console, there is the for command. Just type 'help for' at a command prompt to see all of the options. To set a single variable you can use this:
for /f %%i in ('find .txt') do set file=%%i
Note this will only work for the first line returned from 'find .txt' because windows only expands variable once by default. You'll have to enable delayed expansion as shown here.
what you are essentially doing is listing out .txt files. With that, you can use a for loop to over dir cmd
eg
for /f "tokens=*" %%i in ('dir /b *.txt') do set file=%%i
or if you prefer using your ls, there's no need to pipe to find.
for /f "tokens=*" %%i in ('ls *.txt') do set file=%%i
Example of setting a variable from command output:
FOR /F "usebackq" %%Z IN ( `C:\cygwin\bin\cygpath "C:\scripts\sample.sh"` ) DO SET BASH_SCRIPT=%%Z
c:\cygwin\bin\bash -c '. ~/.bashrc ; %BASH_SCRIPT%'
Also, note that if you want to test out the FOR command in a DOS shell, then you need only use %Z instead of %%Z, otherwise it will complain with the following error:
%%Z was unexpected at this time.
I have a list of websites. I want a batch file which will open only the first website in the list when I execute the bat file for first time, and when I execute the batch file for the second time it will open only the second website in the list, and when I execute it for third time it will open only the third website in the list and so on.
Someone suggested me the following solution:
#echo off & setLocal EnableDELAYedExpansion
if not exist %TEMP%\runnum echo 1> %TEMP%\runnum
set /p R=<%TEMP%\runnum
set N=
for /f "tokens=* delims= " %%a in (myfile) do (
set /a N+=1
if !N! equ !R! echo firefox %%a blabla
)
set /a R+=1
echo !R!> %TEMP%\runnum
But I don't know what he meant by (myfile) and blabla, because I don't know anything about scripting or programming.
How can I do this?
Assuming my list of websites are:
http://meebo.com
http://yahoo.com
http://google.com
http://orkut.com
http://facebook.com
http://msn.com
http://cnn.com
http://myspace.com
http://twitter.com
This is actually not that hard. myfile becomes the file name in which you store your list of URLs and blabla are simply additional parameters passed to Firefox.
Though there are a few points one could improve:
Relying on Firefox isn't the best thing. I'd suggest using
start "" "%%a"
instead, since that spawns the default browser instead of hardcoding a specific one.
Your batch will fail when the number of websites in your file is reached and probably just spawn a new Firefox window. Below I have created a batch which eliminates both problems:
#echo off
setlocal enableextensions
rem fetch the first URL
set /p URL=<list.txt
rem Open the browser with that URL
start "" "%URL%"
rem Remove the URL from the front of the file ...
more +1 list.txt | findstr /r /v "^$" > tmp_list.txt
rem ... and put it to the end
echo.%URL%>>tmp_list.txt
del list.txt
ren tmp_list.txt list.txt
endlocal
This version doesn't rely on a specific browser. It will simply roll the file itself, by removing the first URL and sticking it to the end again. So as long as you have your URL file (which is list.txt in this case) it's pretty much self-contained. Code may be found in my SVN repository as well.
ETA: Explaining some parts of that batch:
set /p URL=<list.txt
This will the first line in list.txt to be stored in the environment variable URL. Usually set /p prompts for user input. By redirecting the file into this command we are basically pretending the file's contents were user input.
start "" "%URL%"
will open a web page, document, folder, whatever. start does The Right Thing™ automagically (mostly :)). If we give it a URL it will open the default browser with it, which is what we're using here. The two quotation marks around the URL will ensure that characters like & in URLs will get passed correctly to the browser, they have a special meaning otherwise. The two quotation marks directly following start are necessary when using quotation marks with start at all, unfortunately. Otherwise start would interpret the URL as the window title for a new console window which may not be exactly what we want here.
more +1 list.txt | findstr /r /v "^$" > tmp_list.txt
This has several parts. First of all more +1 causes a file to be output, skipping the first line. As we remember, the first line is the first URL we wanted to open (which should have happened already). What we want to do is to remove that URL from the start of the file and put it to the end. So the first step is to remove it from the start, which is what more +1 list.txt does here.
Then, whatever more prints gets passed into findstr. findstr is a handy utility to search for strings usually. What we do here is enable regular expressions with /r (sort of programmers' dream tool for handling text – if they could, they would write complete programs in regular expressions, but I digress). Then /v causes findstr to print every line not matching what we specify after that. The pattern we are searching for here is "^$" which is just reg-ex speak for "empty line".
So in one line we remove the first line from the file and remove any empty lines from it. (Those empty lines would cause the batch file to do weird things. Remember that start does mostly the right thing? This is one such case. An empty line in your file would cause an Explorer window with your current folder to appear, instead of a browser with the next web page. So we need to get rid of those.)
Finally we write everything those commands print into a new file, called tmp_list.txt. Don't worry, it won't linger around for too long.
echo.%URL%>>tmp_list.txt
This appends the URL just opened to our temporary list. Nothing fancy going on here.
del list.txt
ren tmp_list.txt list.txt
Finally we delete the old list and rename the temporary one to the old name again.
ETA: Since you requested a version which can open multiple pages in one go, what follows is a quick and dirty hack which enables just that:
#echo off
setlocal enableextensions
set num=3
for /l %%i in (1,1,%num%) do call :start
endlocal
goto :eof
:start
set /p URL=<list.txt
start "" "%URL%"
more +1 list.txt | findstr /r /v "^$" > tmp_list.txt
echo.%URL%>>tmp_list.txt
del list.txt
ren tmp_list.txt list.txt
goto :eof
No lengthy explanation this time, though. This post is already long enough. You can control the number of pages opening by changing the num variable near the top of the file.
set num=5
will cause five pages to open instead of three.
I expanded on Ventero's "Dirty hack" to get the lines in the text file to loop through the entire text file opening them up then ending. It works like a charm.
#echo off
setlocal enableextensions
setlocal EnableDelayedExpansion
set "cmd=findstr /R /N "^^" Give.txt | find /C ":""
for /f %%a in ('!cmd!') do set number=%%a
set num=%number%
for /l %%i in (1,1,%num%) do call :start
endlocal
goto :eof
:start
set /p URL=<Give.txt
start "" "%URL%"
more +1 Give.txt | findstr /r /v "^$" > tmp_list.txt
echo.%URL%>>tmp_list.txt
del Give.txt
ren tmp_list.txt Give.txt
goto :eof
I had a URL with a space (%20) in it and really needed:
start "" "(URL with space)"
Also, I found that if Internet Explorer wasn't loaded it would ignore the hardcoded list in my batch file while loading, resulting in just one URL from the list actually working. A delay after the first start works well:
rem Allows Internet Explorer to load, otherwise it will only show one URL
rem N=Nothing shown D=default (Y for Y/N) T=timeout (7 sec)
#choice /N /D Y /T 7 > nul:
I just found an old CHOICE.COM that didn't support /D and /T in the same way:
rem Older versions of choice may need: choice /N /Ty,7 > nul:
Just use choice /? if in doubt :)
Any ideas how to echo or type the last 10 lines of a txt file?
I'm running a server change log script to prompt admins to state what they're doing, so we can track changes. I'm trying to get the script to show the last 10 entries or so to give an idea of what's been happening recently. I've found a script that deals with the last line, as shown below, but can't figure out what to change in it to display the last 10 lines.
Script:
#echo off
setLocal EnableDelayedExpansion
for /f "tokens=* delims= " %%a in (c:\log09.txt) do (
set var=%%a
)
echo !var!
Example of log file:
06/02/2009, 12:22,Remote=Workstation-9,Local=,
mdb,bouncing box after updates,CAS-08754,
=================
07/02/2009, 2:38,Remote=,Local=SERVER1,
mdb,just finished ghosting c drive,CAS-08776,
=================
07/02/2009, 3:09,Remote=,Local=SERVER1,
mdb,audit of server,CAS-08776,
Any thoughts?
The script works great, just need it to pipe more lines to the screen.
Hopefully this will save Joel's eyes :)
#echo OFF
:: Get the number of lines in the file
set LINES=0
for /f "delims==" %%I in (data.txt) do (
set /a LINES=LINES+1
)
:: Print the last 10 lines (suggestion to use more courtsey of dmityugov)
set /a LINES=LINES-10
more +%LINES% < data.txt
This answer combines the best features of already existing answers, and adds a few twists.
The solution is a simple batch implementation of the tail command.
The first argument is the file name (possibly with path information - be sure to enclose in quotes if any portion of path contains spaces or other problematic characters).
The second argument is the number of lines to print.
Finally any of the standard MORE options can be appended: /E /C /P /S /Tn. (See MORE /? for more information).
Additionally the /N (no pause) option can be specified to cause the output to be printed continuosly without pausing.
The solution first uses FIND to quickly count the number of lines. The file is passed in via redirected input instead of using a filename argument in order to eliminate the printout of the filename in the FIND output.
The number of lines to skip is computed with SET /A, but then it resets the number to 0 if it is less than 0.
Finally uses MORE to print out the desired lines after skipping the unwanted lines. MORE will pause after each screen's worth of lines unless the output is redirected to a file or piped to another command. The /N option avoids the pauses by piping the MORE output to FINDSTR with a regex that matches all lines. It is important to use FINDSTR instead of FIND because FIND can truncate long lines.
:: tail.bat File Num [/N|/E|/C|/P|/S|/Tn]...
::
:: Prints the last Num lines of text file File.
::
:: The output will pause after filling the screen unless the /N option
:: is specified
::
:: The standard MORE options /E /C /P /S /Tn can be specified.
:: See MORE /? for more information
::
#echo OFF
setlocal
set file=%1
set "cnt=%~2"
shift /1
shift /1
set "options="
set "noPause="
:parseOptions
if "%~1" neq "" (
if /i "%~1" equ "/N" (set noPause=^| findstr "^") else set options=%options% %~1
shift /1
goto :parseOptions
)
for /f %%N in ('find /c /v "" ^<%file%') do set skip=%%N
set /a "skip-=%cnt%"
if %skip% lss 0 set skip=0
more +%skip% %options% %file% %noPause%
You should probably just find a good implementation of tail. But if you really really insist on using CMD batch files and want to run on any NT machine unmolested, this will work:
#echo off
setLocal EnableDelayedExpansion
for /f "tokens=* delims= " %%a in (c:\tmp\foo.txt) do (
set var9=!var8!
set var8=!var7!
set var7=!var6!
set var6=!var5!
set var5=!var4!
set var4=!var3!
set var3=!var2!
set var2=!var1!
set var1=!var!
set var=%%a
)
echo !var9!
echo !var8!
echo !var7!
echo !var6!
echo !var5!
echo !var4!
echo !var3!
echo !var2!
echo !var1!
echo !var!
There are several windows implementations of the tail command. It should be exactly what you want.
This one sounds particularly good:
http://malektips.com/xp_dos_0001.html
They range from real-time monitoring to the last x lines of the file.
Edit: I noticed that the included link is to a package It should work, but here are some more versions:
http://www.lostinthebox.com/viewtopic.php?f=5&t=3801
http://sourceforge.net/projects/tailforwin32
If file is too large it can take too long to get count of lines
another way is to use find and pass it a nowhere string
$find /v /c "%%$%!" yourtextfile.txt
this would result an output like this
$---------- yourtextfile.txt: 140
then you can parse output using for like this
$for /f "tokens=3" %i in ('find /v /c "%%$%!" tt.txt') do set countoflines=%i
then you can substract ten lines from the total lines
After trying all of the answers I found on this page none of them worked on my file with 15539 lines.
However I found the answer here to work great. Copied into this post for convenience.
#echo off
for /f %%i in ('find /v /c "" ^< C:\path\to\textfile.txt') do set /a lines=%%i
set /a startLine=%lines% - 10
more /e +%startLine% C:\path\to\textfile.txt
This code will print the last 10 lines in the "C:\path\to\textfile.txt" file.
Credit goes to OP #Peter Mortensen
using a single powershell command:
powershell -nologo "& "Get-Content -Path c:\logFile.log -Tail 10"
applies to powershell 3.0 and newer
I agree with "You should use TAIL" answer. But it does not come by default on Windows. I suggest you download the "Windows 2003 Resource Kit" It works on XP/W2003 and more.
If you don't want to install on your server, you can install the resource kit on another machine and copy only TAIL.EXE to your server. Usage is sooooo much easier.
C:\> TAIL -10 myfile.txt
Here's a utility written in pure batch that can show a lines of file within a given range.To show the last lines use (here the script is named tailhead.bat):
call tailhead.bat -file "file.txt" -begin -10
Any ideas how to echo or type the last
10 lines of a txt file?
The following 3-liner script will list the last n lines from input file. n and file name/path are passed as input arguments.
# Script last.txt
var str file, content ; var int n, count
cat $file > $content ; set count = { len -e $content } - $n
stex -e ("["+makestr(int($count))) $content
The script is in biterscripting. To use, download biterscripting from http://www.biterscripting.com , save this script as C:\Scripts\last.txt, start biterscripting, enter the following command.
script last.txt file("c:\log09.txt") n(10)
The above will list last 10 lines from file c:\log09.txt. To list last 20 lines from the same file, use the following command.
script last.txt file("c:\log09.txt") n(20)
To list last 30 lines from a different file C:\folder1\somefile.log, use the following command.
script last.txt file("C:\folder1\somefile.log") n(30)
I wrote the script in a fairly generic way, so it can be used in various ways. Feel free to translate into another scripting/batch language.