I am using grep on windows to filter a file and would like to pipe the output to another batch script.
e.g.
grep "foo" bar.txt | mybatch.bat
and in mybatch i would like to process each matching line
e.g. mybatch.bat:
echo Line with foo: %1
on linux I can solve this via
grep "foo" bar.txt | while read x; do mybatch.sh $x; done
but no idea how I can do this on a windows machine.
Thanks in advance!
You can use for:
for /f %x in ('grep "foo" bar.txt') do call mybatch.cmd "%x"
This will call your batch file once for every non-empty line, just as your shell script example would do.
Here's the trick of the trade:
head.bat
#echo off
setlocal ENABLEDELAYEDEXPANSION
set n=%1
for /F "tokens=*" %%x in ('more') do (
echo %%x
set /A n -= 1
if !n! EQU 0 exit /b
)
Using it:
REM print the first 10 lines:
type test.txt | head 10
Related
I am trying to use the tee code written for a bat file but am having trouble implementing it in my code. I don't want to use any third party installs to solve the tee problem as I want the code to work if I format my computer in a year and want to run the program again.
I have it setup in this fashion:
mycommand.exe | tee.bat -a output.txt
I've tried with a seperate .bat file and tried including as a function (preffered) in the original .bat to no avail with:
myprogram.exe | call tee -a output.txt
echo.
echo.
echo.
SET /P restart="Do you want to run again? (1=yes, 2=no): "
if "%restart%"=="1" GOTO LoopStart
::--------------------------------------------------------
::-- Function section starts below here
::--------------------------------------------------------
:tee
:: Check Windows version
IF NOT "%OS%"=="Windows_NT" GOTO Syntax
|
:: Keep variables local
SETLOCAL
:: Check command line arguments
SET Append=0
IF /I [%1]==[-a] (
SET Append=1
SHIFT
)
IF [%1]==[] GOTO Syntax
IF NOT [%2]==[] GOTO Syntax
:: Test for invalid wildcards
SET Counter=0
FOR /F %%A IN ('DIR /A /B %1 2^>NUL') DO CALL :Count "%%~fA"
IF %Counter% GTR 1 (
SET Counter=
GOTO Syntax
)
:: A valid filename seems to have been specified
SET File=%1
:: Check if a directory with the specified name exists
DIR /AD %File% >NUL 2>NUL
IF NOT ERRORLEVEL 1 (
SET File=
GOTO Syntax
)
:: Specify /Y switch for Windows 2000 / XP COPY command
SET Y=
VER | FIND "Windows NT" > NUL
IF ERRORLEVEL 1 SET Y=/Y
:: Flush existing file or create new one if -a wasn't specified
IF %Append%==0 (COPY %Y% NUL %File% > NUL 2>&1)
:: Actual TEE
FOR /F "tokens=1* delims=]" %%A IN ('FIND /N /V ""') DO (
> CON ECHO.%%B
>> %File% ECHO.%%B
)
:: Done
ENDLOCAL
GOTO:EOF
:Count
SET /A Counter += 1
SET File=%1
GOTO:EOF
:Syntax
ECHO.
ECHO Tee.bat, Version 2.11a for Windows NT 4 / 2000 / XP
ECHO Display text on screen and redirect it to a file simultaneously
ECHO.
IF NOT "%OS%"=="Windows_NT" ECHO Usage: some_command ¦ TEE.BAT [ -a ] filename
IF NOT "%OS%"=="Windows_NT" GOTO Skip
ECHO Usage: some_command ^| TEE.BAT [ -a ] filename
:Skip
ECHO.
ECHO Where: "some_command" is the command whose output should be redirected
ECHO "filename" is the file the output should be redirected to
ECHO -a appends the output of the command to the file,
ECHO rather than overwriting the file
ECHO.
ECHO Written by Rob van der Woude
ECHO http://www.robvanderwoude.com
ECHO Modified by Kees Couprie
ECHO http://kees.couprie.org
ECHO and Andrew Cameron
I am trying to split the output so I can save the console output to a file while still being able to interact with the program that is running.
How can I get the Tee command to work properly with my .bat so I can split the output to both a file and the console.
Your attempt to call a batch function within a pipe will always fail because of how Windows pipes work - Windows instantiates both sides of the pipe via new CMD shells. See https://stackoverflow.com/a/8194279/1012053 for more info.
That Rob van der Woude version of a batch tee cannot possibly work for you because it uses a FOR /F to read the results of a command - the command must execute to completion before any lines are read. That won't work if you need user interaction during the execution of the command. With that version of tee you might as well simply redirect output to a file and then TYPE the file when finished. Obviously not what you want.
There are pure batch tricks that can get you closer, but I think there is still one problem that can't be solved with pure batch. Your executable may put a prompt on a line without issuing a new line. I believe pure native batch always reads entire lines (except when at end of stream). I'm not aware of a batch method to read character by character.
Slight correction - SET /P can read partial lines of piped input, but it has limitations that prevent it from being used for a robust batch tee solution: There is no way to know for sure when each line ends. It is limited to 1021 characters per "line". It strips control characters from the end of each "line". There is no way to tell when it has reached the end of the input stream.
But there is a simple solution - JScript or VBScript works like a champ, and doesn't require any special installs. Here is a hybrid JScript/batch script that should work for you. The JScript is poorly written with lots of room for improvement. For example, there is no error checking.
I save the script as tee.bat. The first required argument specifies the name of the file to write to. By default, the file is over-written if it already exists. If a second argument is provided (value doesn't matter), then the output is appended to the file instead.
#if (#X)==(#Y) #end /* Harmless hybrid line that begins a JScript comment
::--- Batch section within JScript comment that calls the internal JScript ----
#echo off
cscript //E:JScript //nologo "%~f0" %*
exit /b
----- End of JScript comment, beginning of normal JScript ------------------*/
var fso = new ActiveXObject("Scripting.FileSystemObject");
var mode=2;
if (WScript.Arguments.Count()==2) {mode=8;}
var out = fso.OpenTextFile(WScript.Arguments(0),mode,true);
var chr;
while( !WScript.StdIn.AtEndOfStream ) {
chr=WScript.StdIn.Read(1);
WScript.StdOut.Write(chr);
out.Write(chr);
}
Usage is pretty much like you would expect.
command.exe | tee.bat output.txt 1
The last 1 argument forces append mode. It could be any value besides 1
It is possible to put everything in one batch script as you seem to prefer.
#if (#X)==(#Y) #end /* Harmless hybrid line that begins a JScript comment
::--- Batch section within JScript comment ----------------------------
#echo off
::This block of code handles the TEE by calling the internal JScript code
if "%~1"=="_TEE_" (
cscript //E:JScript //nologo "%~f0" %2 %3
exit /b
)
::The rest of your batch script goes here
::This pipes to TEE in append mode
mycommand.exe | "%~f0" _TEE_ output.txt 1
exit /b
----- End of JScript comment, beginning of normal JScript ------------------*/
var fso = new ActiveXObject("Scripting.FileSystemObject");
var mode=2;
if (WScript.Arguments.Count()==2) {mode=8;}
var out = fso.OpenTextFile(WScript.Arguments(0),mode,true);
var chr;
while( !WScript.StdIn.AtEndOfStream ) {
chr=WScript.StdIn.Read(1);
WScript.StdOut.Write(chr);
out.Write(chr);
}
Update
For anyone with an academic interest in batch scripting, I've posted a pure native batch version of tee at Asynchronous native batch tee script over at DosTips. But this hybrid approach is my preferred scripting solution.
Is it possible to read just the first line from a .sql file in the windows command line?
If so, how can I do this - googleing has not come up with a solution that works.
All I have is standard windows command prompt
This looks like a copy of this question;
Windows batch command(s) to read first line from text file
Quoting the accepted answer from that ticket;
Here's a general-purpose batch file to print the top n lines from a file like the GNU head utility, instead of just a single line.
#echo off
if [%1] == [] goto usage
if [%2] == [] goto usage
call :print_head %1 %2
goto :eof
REM
REM print_head
REM Prints the first non-blank %1 lines in the file %2.
REM
:print_head
setlocal EnableDelayedExpansion
set /a counter=0
for /f ^"usebackq^ eol^=^
^ delims^=^" %%a in (%2) do (
if "!counter!"=="%1" goto :eof
echo %%a
set /a counter+=1
)
goto :eof
:usage
echo Usage: head.bat COUNT FILENAME
For example:
Z:\>head 1 "test file.c"
; this is line 1
Z:\>head 3 "test file.c"
; this is line 1
this is line 2
line 3 right here
It does not currently count blank lines. It is also subject to the batch-file line-length restriction of 8 KB.
To read just the first line of a (text) file:
<file.txt set /p line=
echo %line%
This is the easiest solution. Dependent on the content (poison chars), echo might fail.
I'm trying to make a script to:
send a request to an url, then append (>>) the %%a variable i set in the FOR /F command IF i get a certain response from the command i run with FOR /F.
I tried with 2 scripts that are the followings:
FOR /F %%a in (usernames.txt) do (
IF "curl -k -d name=%%a https://club.pokemon.com/api/signup/verify-username" EQU "{"valid":true,"suggestions":[],"inuse":false}" %%a >> usernames1.txt
and
set /p VAR = < tmpFile
FOR /F %%a in (usernames.txt) do (
curl -k -d name=%%a https://club.pokemon.com/api/signup/verify-username
> tmpFile
IF VAR={"valid":true,"suggestions":[],"inuse":false}
%%a >> usernames1.txt)
EDIT: good enough with that script, thanks guys. but i've got another thing: can i add more than 1 variable to the script? I mean a variable like %%a, that takes every line from another txt file
First, you set VAR once, whereas your temporary file does not exist. Then you test with = instead of == and without the ! chars.
And don't put too many spaces like if you were using a real shell like bash :)
As Magoo noted, I also had to fix set /p VAR = < tmpFile to remove the extra spaces. Batch has a tendency to take them literaly.
(another example: echo foo > file: file now contains "foo ".
(needless to say that without enabledelayedexpansion it wouldn't work either because lots of things happen inside the FOR loop)
The fixed code (also had to protect the test string with extra quotes or it wouldn't work):
setlocal enabledelayedexpansion
del usernames1.txt >NUL 2>NUL
FOR /F %%a in (usernames.txt) do (
curl -k -d name=%%a https://club.pokemon.com/api/signup/verify-username > tmpFile 2>NUL
set /p VAR=<tmpFile
IF "!VAR!"=="{"valid":true,"suggestions":[],"inuse":false}" echo %%a >> usernames1.txt
)
I tested it with a random list of user names and changing "valid":true by "valid":false and the names were issued.
I have a batch script below which reads strings with spaces, line by line from a reference text file(testException.txt). I am using findstr to search in my text.txt to find any match to the strings in the reference file. If there is any match, i want to output everything in the text.txt to an error textfile and exit the loop.
:find.bat
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set source=testException.txt
set location=text.txt
for /f "tokens=* delims=" %%L IN (!source!) DO (
#findstr /B /I /C:"%%L" "!location!"
if %ERRORLEVEL% EQU 0 (type !location! >> Error.txt goto:eof) else (call)
)
pause
When I execute my script, it outputs as many times as the number of patterns in my testException.txt. It seems like my goto:eof does not end my for loop. Below is my text.txt and testException.txt. Any help & suggestions for a better way is greatly appreciated.
text.txt
hello, how are u.
im fine ty.
lol. apple 123 is here
testException.txt
orange 321
apple 123
lol 423
I have a batch file which does something like this
for %%a in (1 2 3 4 5) do dir > %%a.output
%%a just gives me a .
How can I use the value of the variable %%a to assign the file name (e.g. 1.output, 2.output) ?
You have to surround your variable:
for %a in (1 2 3 4 5) do dir > %a%.output
Your command syntax looks correct to me. I would expect that line of a batch file, as is, to produce these commands:
dir > 1.output
dir > 2.output
dir > 3.output
dir > 4.output
dir > 5.output
Which would in turn create 1.output, 2.output, etc.
As a debugging tip, you might try changing that line to something like this:
for %%a in (1 2 3 4 5) do echo dir ^> %%a.output
Note the ^, which is used to escape the > redirection.
Also:
Remember to use %%a in batch files
but %a at the command line.
Remember that for variables are
case-sensitive; %%A is different
from %%a.
Update
It's been a long time since I had to get a batch file working under such an old OS version, but I wouldn't be surprised if redirection was incompatible with for back in the day.
You could try this:
for %%a in (1 2 3 4 5) do call helper.bat %%a
In helper.bat:
dir > %1.output
Or, if you don't like an extra batch file, combine them into one file:
if .%1==.sub goto do_sub
for %%a in (1 2 3 4 5) do call %0 sub %%a
goto end
:sub
shift
dir > %1.output
:end
I don't have a Win98 system at my disposal, but enter for /? in a DOS box and see if there's something there that can help you.
The way to do this in NT-based Windows OS is:
FOR /L %variable IN (start,step,end) DO command [command-parameters]
So your statement would be something like:
FOR /L %%a IN (1,1,5) DO echo dir ^> %%a.output
Perhaps there's something similar in DOS under Win98.