batch script: The process tried to write to a nonexistent pipe - batch-file

#echo off
(for /f "skip=1 tokens=3,5,11" %%a in (Data.txt) do (if /i "%%c"=="%1" (if %%b==%2 echo %%a))
echo EXIT
)|Sum.exe
I'm trying to write a simple batch script that would take the .txt file with columns of data (Data.txt), find some values using 'if' and redirect all found values to stdin input of "Sum.exe".
"EXIT" is also redirected as it means that there's no more input to be given.
When I run above code first found value is printed in console and then "The process tried to write to a nonexistent pipe" message error is printed multiple times. Therefore echo EXIT somehow must be messing up with |Sum.exe. How to properly redirect both for and echo Exit into Sum?
EDIT:
Ok, so here's the input part of the Sum program (written in c++)
std::string a;
while (a != "EXIT")
{
std::cin >> a;
if (isNumber(a))
add(sum, std::stoi(a));
}
I added cout to see whether the data was being processed and it seems that the commands in batch script were treted as input aswell.

My first suggestion would be procedure call: call :PROCEDURE. But the call doesn't work with pipe redirection.
For example call :PROCEDURE | SORT return error: Invalid attempt to call batch label outside of batch script.
So I suggest to use input parameter as switch flag to call self batch. For example it would be third parameter: %3. Batch calls itself while third parameter equals x.
So I made this code
#echo off
if "%3"=="x" (%~nx0 %1 %2|sort )& GOTO :EOF
for /f "skip=1 tokens=3,5,11" %%a in (Data.txt) do (
if /i "%%c"=="%1" (if %%b==%2 echo %%a)
echo EXIT
)
I use sort.exe utility in my example. Change it to your sum.exe
To call batch-file use syntax: my_batch.cmd [value11] [value5] x
P.S. I think you do not need word EXIT in output but I left it in example code.

Related

How do I take the value of one batch file to another?

Key Generator
#ECHO OFF
COLOR A
ECHO Generating Key!
choice /d y /t 3 > nul
set /p "genkey"="%random%-%random%-%random%-%random%"
PAUSE
EXIT
Batch 2
COLOR A
#ECHO OFF
set /p base=
if %base% == %genkey% GOTO :ecs
:ecs
PAUSE
EXIT
The way I normally do this is by writing to a file and using SET to recall from the file.
For example:
BATCH FILE 1
echo off
set var1=%Random%-%Random%-%Random%
echo %var1%>temp.log
pause
exit
BATCH FILE 2
echo off
set Var1=nul
if EXIST Temp.log (set /p Var1=<Temp.log && del /Q Temp.log)
echo %Var1%
pause
exit
In this case, if you run the second batch file without running the first one, the output will be "nul". However, if you ran the first batch file before the seccond, the output of the first will be displayed.
You can change %Random%-%Random%-%Random% to whatever text or variable you want.
The program acts like the type function, however with this method it prints the contents of the file to a variable.
One last thing to note is that this method will ONLY read the first line of the file. This is useful where you are transfering numbers, then using that number in an operation. If you want to transfer the whole file, you can use a FOR state ment, but also note, the FOR statement will recall the entire into a singe line.

How to get a batch file only processed if it's called from another batch file?

I am coding a batch file and it needs some more files. But they files should only be able to run using the call function from another batch file. My code looks like this:
call compileData.bat
pause
I want the compilerData.bat just starts when it's called from this one, not if its just started from Explorer or something other.
Can you please help me?
I have tried to find a solution on this problem in a whole hour!
You can use a parameter.
compileData.bat:
if "%1" neq "somestring" exit /b
REM rest of your code
Another.bat:
call compileData.bat somestring
pause
I cannot think of any way that would prevent the bare "run" of the called script. Possibly that might only be done using NTFS permissions.
What you can do quickly is something like this:
MOTHERBATCH.bat
call compileData.bat SomePASSPHRASE
compileData.bat
#echo off
if not "%1"=="SomePASSPHRASE" (
echo "You can not run this script directly, please run MOTHERSCRIPT.bat."
exit /B 1
)
echo "Passphrase is correct, code is executed..."
Set an environment variable in the parent script, then if that variable is not set or doesn't have the correct value in the children, they just exit with an error message explaining they aren't intended for standalone use. You really can't prevent someone from reverse engineering the code and forcing it to run.
You could put the children in a password protected zip file and have the parent unpack it just before calling them. Then when the parent is done, it deletes the unpacked scripts.
Do all of the above.
You can use a not so well known system variable named cmdcmdline.
I will explain a brief usage for you.
For brevity's sake we will have two very simple batch files.
Parent.bat
#echo off
call compiledata.bat
And compiledata.bat
#echo off
echo %cmdcmdline%
pause
When compiledata.bat is executed on its own this variable's value is the batch file itself.
C:\WINDOWS\system32\cmd.exe /c ""C:\Batch\CALL\compiledata.bat" "
But when compiledata.bat is called from parent.bat the variable's value is that of the calling parent.bat.
C:\WINDOWS\system32\cmd.exe /c ""C:\Batch\CALL\parent.bat" "
My suggestion is putting all your batch code into a single batch file and use subroutines. Open a command prompt window and run call /? for help on how to use subroutines which is nothing else than calling a batch file being embedded in current batch file.
A simple example:
#echo off
echo Running %~f0 %*
call :compileData %*
call :WaitForUser
rem The next line results in exiting processing of this batch file
goto :EOF
:compileData
echo/
echo Running subroutine compileData with the arguments: %*
rem Exit processing subroutine compileData and continue above
rem after the command line calling the subroutine compileData.
goto :EOF
:WaitForUser
echo/
pause
rem Exit processing subroutine WaitForUser and continue above
rem after the command line calling the subroutine WaitForUser.
goto :EOF
See also Where does GOTO :EOF return to? And take a look on DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/ for the explanation on using echo/ to output an empty line.
Here's my solution:
when launched from the command line, %cmdcmdline% inherits the name from the base calling program, so it wouldn't be the name of the "middle man" calling your batch file
this is what I came up with. I had to use the "subroutine" method to get the variables properly expanded
Note: Edge Case: if you use complex paths with the batch files having the same name in different folders, you could run into an "Edge Case". If that is important to you, then you might have to further parse the file names. I'm not totally sure, it wasn't my use case so I didn't go further.
#echo OFF
setlocal EnableDelayedExpansion
call :myGetFileName "%CmdCmdLine%"
if /I "%sRet%"=="%~nx0" (
echo ************** Pause
) else (
echo ************** NO Pause
)
echo finished test
pause
exit
:myGetFileName
set "sRet=%~nx1"
exit /b

Getting errorlevel from system command

I have a C project and at some point I call the system command below:
ret_val = system("#ECHO OFF\nFC /A /L /N C:\\tmp\\test.out C:\\bin\\win\\output.txt");
FC command basically compares the two files and supposed to return an error level.
If I call this command on command prompt, I can view the error simply by echoing the errorlevel variable. I have no problem here.
My question is I would like to have this error level in an int variable in my code. I don't want it to be seen on my terminal but at the same time I want this variable in order to analyze my comparison result. This ret_val variable is always 0, it's not the functionality that I need.
How can I get errorlevel value in my code?
The system command only executes the first line of your string, so it executes #echo off, which doesn't change default return code, and you always get 0.
Of course, you could
paste the text in a .bat file and execute this .bat file
use commands chaining: #echo off && FC /A /L /N C:\\tmp\\test.out C:\\bin\\win\\output.txt
but in your case, since you have only one command to call, just call it without the #echo off
ret_val = system("FC /A /L /N C:\\tmp\\test.out C:\\bin\\win\\output.txt")
system doesn't need echo to be turned off. Only batch file execution defaults to verbose. System calls are silent (they don't print the issued commands).

Is it possible in a batch file to read from a pipe line by line?

I was wondering if it is possible to read from a pipe in a batch file. If I write:
echo Test
i get, unsurprising, Test. That's nice. But what if I want to pipe the output, and read it from another command?
echo Test | echo ???
How to obtain the same result as before, but through a pipe?
Thanks!
EDIT: what I am after really after is this.
I have a list of files, and i need to filter this list with some words that i put, line by line, in a file named filter.txt. So I have to use findstr /g:filter.txt.
But then I need to do something to the list files that matches, and since findstr returns one row for each file, i have to read the matches line by line.
This is how i did it:
dir /b | findstr /g:filter.txt | for /F "delims=" %a in ('more') do del "%a"
SOLUTION:
It looks like that what I wanted to do was not reading from a pipe but just reading the output of another command in a batch file.
To do a single line read, you could use this:
echo Test | ( set /p line= & call echo %%line%%)
or you can use this, that works also with multi line input:
echo Test | for /F "delims=" %a in ('more') do #echo %a
(this trick of using more could be useful in some situations). But in my particular case, the solution is this:
for /F "delims=" %a in ('echo Test') do #echo %a
Thanks to everyone!
Based on this answer https://stackoverflow.com/a/6980605/1630171 it looks like that a way to answer my question is this:
echo Test | for /F "delims=" %a in ('more') do #echo %a
It's a bit weird but it works :)
It only looks a little strange to me that there's no native solution to this... but this does exactly what i want!
Excuse me, I think there is a confusion here...
You said you want to read from a pipe. A pipe is used to redirect the output of one command into the input of another command; the second command is called filter. For example, in
dir /b | findstr /g:filter.txt
there is a pipe between dir and findstr commands. A pipe is always established between two processes. There is no way to read the data that flow from dir command to findstr command (that is the only pipe that exist here). However, you can read from the output of findstr command.
If we insert an additional filter, the behavior is the same. For example, in
dir /b | findstr /g:filter.txt | more
there are two pipes, but there is no way to read from anyone of them. However, you can read from the output of the last command (more in this case). What is the native Batch solution to read the output of one command? It is the FOR /F command. For example, the native way to get echo command output in:
echo Test | for /F "delims=" %a in ('more') do #echo %a
is:
for /F "delims=" %a in ('echo Test') do #echo %a
Please note that in the first example the %a parameter does NOT get the information from the pipe that exist between echo and for commands, but from the output of more command.
In the same way, the natural method to achieve this task:
dir /b | findstr /g:filter.txt | for /F "delims=" %a in ('more') do del "%a"
is this way:
for /F "delims=" %a in ('dir /b ^| findstr /g:filter.txt') do del "%a"
that process the multi-line output of findstr command.
Second method is not just faster than the former, but it is also clearer because the inclusion of a more command that really do nothing may lead to undesired misconceptions or errors.
Antonio
For reading a single line, you could also use set /p, but this only works with one line.
echo test | ( set /p line= & call echo %%line%%)
The problem is here, that a pipe creates two new cmd.exe contexts, for each side one.
They run in the same window as the parent cmd.exe, they can't change any variables of the parent cmd, as they are only childs.
That's the cause why this one fails
echo test | set /p line=
echo %line%
line will be set, but it will be destroyed when the pipe ends.
Building on another answer I saw elsewhere, it is possible to capture the output of a command and store it in a variable without an intermediary file quite simply, so long as it is numeric.
Child processes, like those inside the pipe cannot share their environment variables, but can return a value which is picked up by %errorlevel%. %errorlevel% isn't an environment variable though, and is calculated by the shell every time it is invoked. It also cannot be set normally, and must be set using a child process. Example:
#echo off
echo %errorlevel%
cmd /c exit 56
echo %errorlevel%
Returns:
0
56
Interestingly, you can also do:
#echo off
echo %errorlevel%
cmd /c exit 56 & echo hi
echo %errorlevel%
Returns:
0
hi
56
Which I believe is because the echo hi is run by another child process in turn, which doesn't wait for the exit statement to finish before printing. This might be changed by a race condition though if the text printed is longer, I'm not to sure if the child process (running exit) which is parent to the one printing 'hi' will wait for it's child to exit (or any subsequent children either) before it completes the exit command. I tried to test this with a longer-running command like Tree, but I got a zero returned by a query to %errorlevel% which is probably due to Tree affecting the results by returning 0, possibly after the exit 56.
Anyway, to get back to what most will find useful:
#echo off
echo %errorlevel%
echo 456 | ( set /p line= & call exit %%line%% )
echo %errorlevel%
pause
Returns:
0
456
Here, the 456 printed by echo is captured and returned by subsequent queries to %errorlevel%. You can capture any command's output this way, although it's limited to one numeric value. This is still very useful, but unfortunately doesn't allow you to store textual output and I can't think of a way to make it work for multi line output either. (unexplored)
I think in theory you can chain as many commands as you want, and should be able to use && to force the order in which they run. I don't know how or if this can be used to capture multiple lines of input or allow the return of text, but should provide some additional wiggle room inside the pipe by providing nested child processes sharing their environment down and possibly return value up. (again untested, perhaps try multiple exit statements or something, and if I learn anything later I'll try to post it here)

Redirecting command input using <

Input redirection is working for .exe files or internal windows commands.
app.exe < ListOfNames.txt
sort < input.txt
However it isn't working when I try to redirect it into a batch script.
test.bat :-
#echo off
echo %1 %2
Running it using :-
test.bat<input.txt
where input.txt has two strings.
However, it is working fine for redirecting output even in case of batch scripts.
Is this the expected behavior or I am making some syntax mistake? Is there any other way to read arguments from a file instead of manually parsing it?
Parameters that are provided on the command line are completely different than stdin ( where your redirected input goes). This is true for both batch scripts as well as .exe programs.
Some programs are designed to accept the same values via command line arguments or stdin. But that is not the norm. That is a feature that is provided by the developer of the program.
If you want to read redirected input within a batch script, then you must do one of the following.
To read a single line:
set /p "ln="
echo %ln%
To read all lines in a loop:
for /f "delims=" %%A in ('findstr "^"') do (
echo %%A
)
Additionally to dbenhams answer, you could also read multiple lines with set/p for a input redirection, like myBatch.bat < commands.txt
#echo off
set "line_1="
set "line_2="
set "line_3="
set /p line_1=
set /p line_2=
set /p line_3=
set line_
But this would fail with an input pipe like type commands.txt | myBatch.bat

Resources