sending output of a c++ program into a variable bash - c++

I'm trying to write a system that grades a c++ code with pre-written examples that i have ready. It's a very simple c++ code like the following:
#include <iostream>
using namespace std;
int main()
{
int a;
cin >> a;
if (a > 100)
cout << "Big";
else
cout << "Small";
return 0;
}
So i want to test and grade this program with a bash, declare a score variable and echo it in the end. Here's what i wrote(I've marked where i need help writing with double quotes)
#!/bin/bash
g++ new.cpp -o new
test1=101
test2=78
score=0
if [ "Result of executing ./new with $test1 variable as input"=="Big" ]
then
(( score += 50 ))
fi
if [ "Result of executing ./new with $test2 variable as input"=="Small" ]
then
(( score += 50 ))
fi
echo $score
Also i'm still very new to bash so if you can tell me a simpler way to use bash for the examples (like loops) i'd love to hear it.
Thanks!

If you want to execute new with the params and get its result, you should try something like this:
#!/bin/bash
g++ new.cpp -o new
test1=101
test2=78
score=0
if [ $(./new $test1) == "Big" ]; then
(( score += 50 ))
fi
if [ $(./new $test2) == "Small" ]; then
(( score += 50 ))
fi
echo $score

Related

reading and analyzing a text file with bash script

I want to read a log file and want to extract 5-6 number digit that is written right next after the keyword "salary". And then want to analyze if the salary is above 2000. If there is even one above 2000, it is a MNC otherwise unknown. After writing the salary, the line ends mostly but sometimes there is an email option.
My script currently looks like this.
salary=$(grep -o 'salary [1-9][0-9]\+$' tso.txt | grep -o '[0-9]\+')
echo $salary
if [ $salary > 2000 ]; then echo "it is mnc....."; else ":it is unknown....."; fi
This can be done in a simple awk like this:
awk '
{
for (i=2; i<=NF; ++i)
if ($(i-1) == "salary" && $i+0 > 2000) {
mnc = 1
exit
}
}
END {
print (mnc ? "it is mnc....." : "it is unknown.....")
}' file
As you seem to be using a GNU grep, you can get the salary value directly with grep -oP 'salary *0*\K[1-9][0-9]*' and then you can check the salary with if [ $salary -gt 0 ].
See the online demo:
#!/bin/bash
tso='salary 23000'
salary=$(grep -oP 'salary *0*\K[1-9][0-9]*' <<< "$tso")
echo $salary # => 23000
if [ $salary -gt 0 ]; then
echo "it is mnc.....";
else
echo "it is unknown.....";
fi
# => it is mnc.....

why is this bash code so slow in comparison to corresponding cpp code?

I am new to OS as it was introduced to us very recently.
we have bash code to run as our lap experiments.
my last task was to code a program to print whether a number is prime or not.
i coded it first in cpp because i am comfortable in cpp. and then coded same in bash after lot of googling. but my bash code is very slower than the cpp code.
I wrote a cpp code to check whether a number is prime or not.
cpp code :
#include <iostream>
using namespace std;
bool isprime(int a) {
if (a == 2 || a == 3)
return true;
if (a % 2 == 0)
return false;
if (a % 3 == 0)
return false;
for (int i = 1; (6 * i - 1) * (6 * i - i) <= a; ++i) {
if (a % (6 * i - 1) == 0)
return false;
if (a % (6 * i + 1) == 0)
return false;
}
return true;
}
int main() {
int n;
cin >> n;
cout << ((isprime(n)) ? "Prime" : "Not Prime") << endl;
return 0;
}
I wrote the same code in bash (linux):
read num
f=1
if test $num -eq 2
then f=1
elif test $num -eq 3
then f=1
elif test `expr $num % 2` -eq 0
then f=0
elif test `expr $num % 3` -eq 0
then f=0
else
for (( i=1; $((`expr i\*6-1`))**2 <= num; i++))
do
t=`expr $i \* 6 - 1`
if test `expr $num % $t` -eq 0
then
f=0
fi
t=`expr $i \* 6 + 1`
if test `expr $num % $t` -eq 0
then
f=0
fi
done
fi
if test $f -eq 1
then
echo 'Prime'
else
echo 'Not-Prime'
fi
but cpp code is very fast and bash code is very slow
here is terminal input and output
a#a:~/Cp$ cat > input
1000000007
^?^C
a#a:~/Cp$ time ./a.out < input
Prime
real 0m0.003s
user 0m0.003s
sys 0m0.000s
a#a:~/Cp$ time ./prog.sh <input
^C
real 0m8.258s
user 0m5.906s
sys 0m2.794s
a#a:~/Cp$ # I interrupted the execution.
I have no idea why is this happening?
The C++ as other compiled languages have for sure a better performance with respect to interpreted languages like bash, sh, python.
This because when you use a compiled program, only a process is launched, while when you run an interpreted program it instatiates a great number of subprocesses also for basic operation.
So is trivial that c++ program is faster than the bash one.
There's not much to the code other than tests and math, but you are spending a lot of time on those individual bash-level commands.
We can eliminate the overhead of the bash-level command/process calls by pushing all of the code down into a single awk call.
Here's a verbose copy of your bash code running in awk:
$ cat isprime
#!/usr/bin/bash
input=${1} # user's input number;
# OP will probably want to add some logic to validate the input as numeric
awk -v num="${input}" ' # pass input number to awk in 'num' variable
BEGIN { f=1 } # init our flag
{ if ( num == 2 ) { f=1 }
else if ( num == 3 ) { f=1 }
else if ( (num % 2) == 0 ) { f=0 }
else if ( (num % 3) == 0 ) { f=0 }
else for ( i=1 ; (i*6-1)**2 <= num ; i++ ) {
t=(i*6-1)
if ( (num % t) == 0 ) { f=0 ; break } # for non-prime numbers we can abort the for loop to save some cycles
t=(i*6+1)
if ( (num % t) == 0 ) { f=0 ; break } # for non-prime numbers we can abort the for loop to save some cycles
}
}
END { msg="Prime"
if ( f == 0 ) { msg="Not-Prime" }
print msg # print our finding to stdout
}
' <<< "" # awk needs a 'file' to work on so we'll feed it an empty here-string
Timing this script for the sample number '1000000007` gives us:
$ time ./isprime 1000000007
Prime
real 0m0.262s
user 0m0.061s
sys 0m0.170s
NOTE: Repeated runs of the above test showed real run times ranging from 0m0.082s to 0m0.262s; the wide range of numbers is due to me running the above under cygwin in a Windows7 VM; I would expect more consistent run times (towards the lower end?) in a linux environment; ymmv ...
And a non-prime test:
$ time ./isprime 1000000006
Not-Prime
real 0m0.125s
user 0m0.015s
sys 0m0.045s
Not as fast as the C++ program but definitely a good bit faster than processing all those individual bash-level test/process calls.
There are a few awkisms that could be added to shave some additional time off the run but for the sake of this answer I wanted to make it as easy as possible to compare the code with the OPs C++/shell code snippets.
The usefulness of this awk solution will be based on the size of the input (number).
Testing the above against some 20-24 digit primes shows this awk solution immediately bails on the %2==0 or %3==0 tests. This comes down to numerical limits in a basic awk implementation.
From my man pages for awk:
-M
--bignum
Force arbitrary precision arithmetic on numbers. This option has no effect if gawk is not compiled to
use the GNU MPFR and GMP libraries. (In such a case, gawk issues a warning.)
Adding the -M flag to the above awk solution does allow the script to work on a 21-digit prime, but it's not fast ...
awk -M -v num="${input}" ' ...
# and then:
$ time ./isprime 998887766553300224411
... as I type this it's going on 8 minutess of 100% cpu utilization (total of a single core)
... aborted after 13 minutes
While I have no doubt this awk solution would eventually complete, the basic fact is it's going to take a looooong time to count from 1 to a 21-digit number by increments of 1 (i++) (this will be true for any implementation whether it be bash, awk, C++, etc).
For really large numbers I'd be looking at other (much more complex) algorithms for verifying a number is a prime.
This is a much faster bash version
read num
f=1
if test $num -eq 2 -o $num -eq 3
then f=1
elif (( num % 2 == 0 || num % 3 == 0 ))
then f=0
else
for (( i=1; ; i++)); do
t=$((i * 6 - 1))
(( t * t > num )) && break
(( num % t == 0 || num % (i * 6 + 1) == 0 )) && { f=0; break; }
done
fi
if test $f -eq 1; then
echo 'Prime'
else
echo 'Not-Prime'
fi
All calls to expr have been removed.
time bash test.sh <<< 1000000007
Prime
real 0m0.128s
user 0m0.000s
sys 0m0.094s

Bash - Find variable in many .txt files and calculate statistics

I have many .txt files in a folder. They are full of statistics, and have a name that's representative of the experiment those statistics are about.
exp_1_try_1.txt
exp_1_try_2.txt
exp_1_try_3.txt
exp_2_try_1.txt
exp_2_try_2.txt
exp_other.txt
In those files, I need to find the value of a variable with a specific name, and use them to calculate some statistics: min, max, avg, std dev and median.
The variable is a decimal value and dot "." is used as a decimal separator. No scientific notation, although it would be nice to handle that as well.
#in file exp_1_try_1.txt
var1=30.523
var2=0.6
#in file exp_1_try_2.txt
var1=78.98
var2=0.4
#in file exp_1_try_3.txt
var1=78.100
var2=1.1
In order to do this, I'm using bash. Here's an old script I made before my bash skills got rusty. It calculates the average of an integer value.
#!/bin/bash
folder=$1
varName="nHops"
cd "$folder"
grep -r -n -i --include="*_out.txt" "$varName" . | sed -E 's/(.+'"$varName"'=([0-9]+))|.*/\2/' | awk '{count1+=$1; count2+=$1+1}END{print "avg hops:",count1/NR; print "avg path length:",count2/NR}' RS="\n"
I'd like to modify this script to:
support finding decimal values of variable length
calculate more statistics
In particular std dev and median may require special attention.
Update: Here's my try to solve the problem using only UNIX tools, partially inspired by this answer. It works fine, except it does not calculate the standard deviation. The chosen answer uses Perl and is probably much faster.
#!/bin/bash
folder=$1
varName="var1"
cd "$folder"
grep -r -n -i --include="exp_1_run_*" "$varName" . | sed -E 's/(.+'"$varName"'=([0-9]+(\.[0-9]*)?))/\2/' | sort -n | awk '
BEGIN {
count = 0;
sum = 0;
}
{
a[count++] = $1;
sum += $1;
}
END {
avg = sum / count;
if( (count % 2) == 1 ) {
median = a[ int(count/2) ];
} else {
median = ( a[count/2] + a[count/2-1] ) / 2;
}
OFS="\t";
OFMT="%.6f";
print avg, median, a[0], a[count-1];
}
'
To extract just the values, use the -o and -P grep options:
grep -rioPh --include="*_out.txt" "(?<=${varName}=)[\d.]+" .
That looks for a pattern like nHops=1.234 and just prints out 1.234
Given your sample data:
$ var="var1"
$ grep -oPh "(?<=$var=)[\d.]+" exp_1_try_{1,2,3}.txt
30.523
78.98
78.100
To output some stats, you should be able to pipe those numbers into your favourite stats program. Here's an example:
grep -oPh "(?<=$var=)[\d.]+" f? |
perl -MStatistics::Basic=:all -le '
#data = <>;
print "mean: ", mean(#data);
print "median: ", median(#data);
print "stddev: ", stddev(#data)
'
mean: 62.53
median: 78.1
stddev: 22.64
Of course, since this is perl, we don't need grep or sed at all:
perl -MStatistics::Basic=:all -MList::Util=min,max -lne '
/'"$var"'\s*=\s*(\d+\.?\d*)/ and push #data, $1
} END {
print "mean: ", mean(#data);
print "median: ", median(#data);
print "stddev: ", stddev(#data);
print "min: ", min(#data);
print "max: ", max(#data);
' exp_1_try_*
mean: 62.53
median: 78.1
stddev: 22.64
min: 30.523
max: 78.98

Execute a command using popen

I have a C++ program in which I want to execute the following command:
cmd = "(diff <(perl -ne 's/^\\S+\\s//; if ((/aaa/ .. /bbb/) && /ccc/)"
" { print \"$_\"}' file1)"
"<(perl -ne 's/^\\S+\\s//; if ((/aaa/ .. /bbb/) && /ccc/)"
" { print \"$_\"} file2)) ";
I get this error when I want to execute this command:
Search pattern not terminated at -e line 1.
I've noticed that the following commands work like this:
cmd = "diff <(echo aa) <(echo bb)"
string strCall = "bash -c \"( " + cmd + " ) 2>&1\"";
stream = popen(strCall.c_str(),"r"); // doesn't work popen(**str**.c_str(),"r")
and an example perl command containing '"' works like this:
cmd = "perl -ne '{print \"$1\"}' file"
stream = popen(str.c_str(),"r"); // doesn't work popen(**strCall**.c_str(),"r");
but if the perl command doesn't contains '"', it works both ways:
cmd = "perl -ne '{print $1}' file"
string strCall = "bash -c \"( " + cmd + " ) 2>&1\"";
stream = popen(str.c_str(),"r"); // also works popen(**strCall**.c_str(),"r");
How can I do to use both diff and perl in the same command. I assume I have to use strCall.
I've tried also to escape the perl cmd like this, but it doesn't work:
cmd = "perl -ne '{print \\\"$1\\\"}' file" // one '/' for '/', one for "'" and one for '"'.
Also it didn't worked this, but I am however not allowed to use R("str"):
cmd = R"(perl -ne '{print \"$1\"}' file)"
string strCall = "bash -c \"( " + cmd + " ) 2>&1\"";
stream = popen(strCall.c_str(),"r")
Thanks.
I know I am not answering your question, but a common solution once you reach this many levels of quoting is to write a simple shell script and then call that from popen.
E.g., popen("/path/diffscript.sh", "r");

regular expression to match C function call over multiple lines

I am struggling to put together a regex to match a function call like following:
funcname (...(..
...)..(..(...
)...)..)
so the function can have multiple bracketed parameters spread over multiple lines.
The dots can be anything else appart from '(' or ')'.
I would use the regex with sed or grep.
Thanks,
Risto
So, I went on writing this simple parser in bash. It is not perfect but can serve as a starting point. For example it cannot distinguish if a function call is commented out or not, etc.
while read file; do
linenum=0
while IFS= read -r line; do
(( linenum++ ))
if [ $fmatch -eq 0 ]; then
if [[ ! $line =~ $funcname ]]; then
continue
fi
linenummatch=$linenum
fmatch=1
fstripped=0
openbracket=0
closebracket=0
spacenum=0
fi
linelen=${#line}
position=0
while [ $position -lt $linelen ]; do
if [ $fstripped -eq 0 ]; then
subline=${line:$position}
mlen=`expr "$subline" : "$funcname"`
if [ $mlen -gt 0 ]; then
(( position+=mlen ))
resultstr=$funcname
fstripped=1
continue
fi
(( position++ ))
continue
fi
ch=${line:$position:1}
case $ch in
'(' )
(( openbracket++ ))
spacenum=0
newresultstr="$resultstr$ch"
;;
')' )
if [ $openbracket -eq 0 ]; then
fmatch=0
break
fi
(( closebracket++ ))
spacenum=0
newresultstr="$resultstr$ch"
if [ $closebracket -eq $openbracket ]; then
echo "$file $linenummatch $newresultstr"
fmatch=0
break
fi
;;
' ' | '\t' )
if [ $spacenum -eq 0 ]; then
newresultstr=$resultstr' '
fi
(( spacenum++ ))
;;
'\n' )
# line feeds are skipped
;;
* )
if [ $openbracket -eq 0 ]; then
fmatch=0
break
fi
spacenum=0
newresultstr="$resultstr$ch"
;;
esac
resultstr=$newresultstr
(( position++ ))
done
done < $file
done < $filelist
As C is an irregular language you may need a parser for that. The problem you will have is working out when all the open brackets are closed again. You can do some fairly strange things with C. For example you can have a parameter that is a function definition in its own right. For example consider in the following program how you would distinguish between a(), b(), c(), d(), e(), f() and g()?
#include <stdio.h>
#define f(c) c;
char a()
{
return f('z');
}
/*
A function in a comment.
char b()
{
return 'y';
}
*/
char c(char d())
{
return d();
}
#if 0
This code is not included
char g()
{
return 'v';
}
#endif
void main()
{
printf ("A function in a string: char e() { return 'x'; }\n");
printf ("The result from passing a to c: %c\n", c(a));
printf ("Press enter to exit");
getchar();
}
I have seen many attempts to do this kind of thing with Regular Expressions but most of them end up with Catastrophic Backtracking issues.