This question already has answers here:
Passing arguments to an interactive program non-interactively
(5 answers)
Closed 6 years ago.
I have been reading a ton about bash scripts and program testing but I am still unable to make this code work.
Basically it is a simple program that asks the user for either of north east south or west. I start the program then it immediately asks for input. I just can not get the bash script to give it any input. I tried using echo and expect.
Any help is appreciated.
Here is the function used to get the players input:
int process_input(Map *game)
{
printf("\n> ");
char ch = getchar();
char a = getchar(); //eat enter
int damage = rand() % 4;
switch(ch) {
case -1:
printf("you suck\n");
return 0;
break;
case 'n':
game->proto.move(game, NORTH);
break;
case 's':
game->_(move)(game, SOUTH);
break;
case 'e':
game->_(move)(game, EAST);
break;
case 'w':
game->_(move)(game, WEST);
break;
case 'a':
game->_(attack)(game, damage);
break;
case 'l':
printf("You can go:\n");
if(game->location->north) printf("NORTH\n");
if(game->location->south) printf("SOUTH\n");
if(game->location->east) printf("EAST\n");
if(game->location->west) printf("WEST\n");
break;
default:
printf("Whats next?", ch);
}
return 1;
}
And here is the attempt at a bash script:
#!/bin/bash
/Desktop/c
./ex17 echo 'w'
You can feed input into a program from bash using any of the following mechanisms.
For a single line of input, you can use a here-string:
./ex17 <<<'w'
For multiple lines, you can use a here-document:
./ex17 <<'EOF'
w
second line of input
more input
EOF
Or you can move those lines out of the script and into a separate file:
./ex17 <filename
More generally, you can run a command that generates as its output the desired input to your program, and connect them together with a pipe. For instance, the above could also be written:
cat filename | ./ex17
or the original example as
echo w | ./ex17
That's more general because you can replace cat and echo here with any sort of program, which can do all sorts of computation to determine what it outputs instead of just dumping the contents of a static string or file.
But what you can't easily do from bash is drive input, read output, and make decisions about what to send as the next input. For that, you should look at expect. An expect script would look something like this:
#!/usr/bin/env expect
spawn ./ex17
expect ">"
send "w\n"
expect "Whats next?"
send "next line here\n"
# turn it back over to interactive user
interact
Try this: first:
echo w | ./ex17
This will send w to the example and output the move. This is called piping; and it essentially connects the stdout of echo to the stdin of ex17
You can do either a C code or a Bash script, not a mixed one.
Use scanf() for reading from keyboard (it stops reading when you hit ENTER) and complete this code in C language.
Related
from Python3, I'm trying to run a script (that I can't edit) that is looking in stdin for file names, runs some code on them and after all that, is expecting for "y/n" answer.
The whole code takes about 3 seconds at most.
How can I add "y" to the stdin after running the script?
This is the original script (written in perl):
if (! -t STDIN) {
while(my $file=<STDIN>) {
chomp $file;
printI "Getting file from STDIN : $file\n";
push(#table_files,$file);
}
close(STDIN);
}
...
<code that runs>
...
"Does it look right? (y/n) : ";
open(STDIN,"/dev/tty"); # reopen stdin cause script also takes files from stdin
my $answer=<STDIN>;
chomp($answer);
if (lc ($answer) ne "y") {die "Exiting!\n"}
Those were my tries (using the terminal):
( sleep 5 ; echo y ; ) | script.pl file1; # Takes "y" as a file instead of answer
yes | script.pl file1; # Fails because it tries to read 'y' all the time as files
This is my try in python3:
p = Popen("script.pl file1", stdout=PIPE, stderr=PIPE, stdin=PIPE, universal_newlines=True, shell=True)
(stdoutdata, stderrdata) = p.communicate(input=f'y\n') # Fails because of no delay
Any suggestions?
If there is a solution for both terminal(tcsh I believe) and python, that would be great. Thanks!!
I have a simple C++ program that counts from 0 to 10 with an increment every 1 second. When the value is incremented, it is written to stdout. This program intentionally uses printf rather than std::cout.
I want to call this program from a bash script, and perform some function (eg echo) on the value when it is written to stdout.
However, my script waits for the program to terminate, and then process all the values at the same time.
C++ prog:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int ctr = 0;
for (int i = 0; i < 10; ++i)
{
printf("%i\n", ctr++);
sleep(1);
}
return 0;
}
Bash script:
#!/bin/bash
for c in $(./script-test)
do
echo $c
done
Is there another way to read the output of my program, that will access it in real time, rather than wait for for the process to terminate.
Note: the C++ program is a demo sample - the actual program I am using also uses printf, but I am not able to make changes to this code, hence the solution needs to be in the bash script.
Many thanks,
Stuart
As you correctly observed, $(command) waits for the entire output of command, splits that output, and only after that, the for loop starts.
To read output as soon as is available, use while read:
./script-test | while IFS= read -r line; do
echo "do stuff with $line"
done
or, if you need to access variables from inside the loop afterwards, and your system supports <()
while IFS= read -r line; do
echo "do stuff with $line"
done < <(./script-test)
# do more stuff, that depends on variables set inside the loop
You might be more lucky using a pipe:
#!/bin/bash
./script-test | while IFS= read -r c; do
echo "$c"
done
I have given some compiled program. I want to communicate with it from my bash script by program stdin and stdout. I need two way communication. Program cannot be killed between exchange of information. How I can do that?
Simple example:
Let that program be compiled partial summation (C++) and script results will be squares of that sums. Program:
int main() {
int num, sum = 0;
while(true) {
std::cin >> num;
sum += num;
std::cout << sum << std::endl;
}
}
My script should looks like that:
for i in 1 2 3 4; do
echo "$i" > program
read program to line;
echo $((line * line))
done
If in program I have for(int i = 1; i <= 4; ++i), then I can do something like that:
exec 4< <(./program); # Just read from program
for i in 1 2 3 4; do
read <&4 line;
echo "sh: $((line * line))";
done
For more look here. From the other hand, if in program I have std::cout << sum * sum;, then solution could be:
exec &3> >(./program); # Write to program
for i in 1 2 3 4; do
echo "$i" > &3
done
My problem is two way communication with other process / program. I don't have to use exec. I cannot install third party software. Bash-only solution, without files, will be nice.
If I run other process, it will be nice to know pid to kill that at the end of script.
I think about communication with two or maybe three processes in the future. Output of firs program may dependents on output of second program and also in second side. Like communicator of processes.
However, I cannot recompile programs and change something. I have only stdin and stdout communication in programs.
If you have bash which is newer than 4.0, you can use coproc.
However, don't forget that the input/output of the command you want to communicate might be buffered.
In that case you should wrap the command with something like stdbuf -i0 -o0
Reference: How to make output of any shell command unbuffered?
Here's an example
#!/bin/bash
coproc mycoproc {
./a.out # your C++ code
}
# input to "std::cin >> num;"
echo "1" >&${mycoproc[1]}
# get output from "std::cout << sum << std::endl;"
# "-t 3" means that it waits for 3 seconds
read -t 3 -u ${mycoproc[0]} var
# print it
echo $var
echo "2" >&${mycoproc[1]}
read -t 3 -u ${mycoproc[0]} var
echo $var
echo "3" >&${mycoproc[1]}
read -t 3 -u ${mycoproc[0]} var
echo $var
# you can get PID
kill $mycoproc_PID
output will be
1
3
6
If your bash is older than 4.0, using mkfifo can do the same thing like:
#!/bin/bash
mkfifo f1 f2
exec 4<> f1
exec 5<> f2
./a.out < f1 > f2 &
echo "1" >&4
read -t 3 -u 5 var
echo $var
rm f1 f2
Considering that your C++ program reads from standard output, and prints to standard output, it's easy to put it inside a simple chain of pipes:
command_that_writes_output | your_cpp_program | command_that_handle_output
In your specific case you probably need to modify the program to only handle one single input and writing one single output, and remove the loop. Because then you can do it very simple, like this:
for i in 1 2 3 4; do
result=`echo $i | ./program`
echo $((result * result))
done
I have one third party library application which runs continuously and generates console print when some event occurs.
I want to take some action when some specific event occurs so I need to monitor console prints continuously to trigget my action.
Is it possible to write application which can continuously monitor string dumper on console(stdout) and do processing when one line is detected.
I have tried to use 'popen' function but it keeps waiting until library application stops execution.
Here is my sample code using open
#include <stdio.h>
int main()
{
FILE *fd = NULL;
char buf[512] = {0};
fd = popen ("./monitor","r");
while (fgets (buf, 512, fd) != NULL)
{
printf ("__FILE__ : message : %s\n",buf);
}
printf ("EOF detected!\n");
return 0;
}
Can anyone please let me know proper way of monitoring console logs and take action.
Thanks in advance.
Pratik
Here is an example piece o code I 've written recently that reads from stdin and prints to stdout .
void echo(int bufferSize) {
// Disable output buffering.
setbuf(stdout, NULL);
char buffer[bufferSize];
while (fgets(buffer, sizeof(buffer), stdin)) {
printf("%s", buffer);
}
}
As I understand you have a similar issue as I had initially getting delayed output because I didn't use:
setbuf(stdout, NULL);
You can also read from stdin(that's what my example code does ) just pipe your command to your c code or if you just want to filter output pipe it to grep. If it's a standardized syslog log you could also use tail on the log file:
tail -f <logfile>| <your c prgoramme>
or
for just filering
tail -f <logfile>|grep "<your string here>"
or if without log file pipe stdout logs this way:
<your app>|<your c prgoramme>
or
<your app>| grep "<your string here>"
3rd party program simulated by a shell script that writes to stdout
#!/bin/bash
while true; do
echo "something"
sleep 2
done
You want to write something like this to capture the output from the 3rd party program and then act on the information:
#!/bin/bash
while read line; do
if [[ $line == "something" ]]; then
echo "do action here"
fi
done
Then combine them with a pipe operator:
./dosomething.sh | act.sh
Is there a way to parse input and output from bash commands in an interactive terminal before they reach the screen ? I was thinking maybe something in .bashrc, but I'm new to using bash.
For example:
I type "ls /home/foo/bar/"
That gets passed through a script that replaces all instances of 'bar' with 'eggs'
"ls /home/foo/eggs/" gets executed
The output gets sent back to the replace script
The output of the script is sent to the screen
Yes. Here's something I wrote for my own use, to wrap old command line Fortran programs that ask for file-paths. It allows escaping back to the shell for e.g. running 'ls'. This only works one way, i.e. intercepts user-input and then passes it on to a program, but gets you most of what you want. You can adapt it to your needs.
#!/usr/bin/perl
# shwrap.pl - Wrap any process for convenient escape to the shell.
# ire_and_curses, September 2006
use strict;
use warnings;
# Check args
my $executable = shift || die "Usage: shwrap.pl executable";
my #escape_chars = ('#'); # Escape to shell with these chars
my $exit = 'exit'; # Exit string for quick termination
open my $exe_fh, "|$executable #ARGV" or die "Cannot pipe to program $executable: $!";
# Set magic buffer autoflush on...
select((select($exe_fh), $| = 1)[0]);
# Accept input until the child process terminates or is terminated...
while ( 1 ) {
chomp(my $input = <STDIN>);
# End if we receive the special exit string...
if ( $input =~ m/$exit/ ) {
close $exe_fh;
print "$0: Terminated child process...\n";
exit;
}
foreach my $char ( #escape_chars ) {
# Escape to the shell if the input starts with an escape character...
if ( my ($command) = $input =~ m/^$char(.*)/ ) {
system $command;
}
# Otherwise pass the input on to the executable...
else {
print $exe_fh "$input\n";
}
}
}