This question already has answers here:
call bash script and fill input data from another bash script
(2 answers)
Closed 4 years ago.
I have this bash script that runs calculus operations for me. It starts off with "read"...
How can I make Script A enter a value into Script B, read the output and dismiss Script B again?
Example:
#ScriptA.sh
a=12
b=4
[open Script B, enter $a and $b and read the result]
echo "The result is [the result of Script B]."
#ScriptB.sh
read x y
echo $(($x+$y))
Desired Output:
bash ScriptA.sh
The result is 16.
Of course it's about more complex maths but you get the idea. Note that, for convenience purposes, I don't want to change the structure of Script B (read x y). But I hope that there are some guys here that can solve this problem.
Thanks in advance.
You should do something like this:
#!/bin/bash
a=12
b=4
result=$(echo $a $b | ./script_b.sh)
echo "the result is $result"
Script B should work like -bc does.
Example:
echo `echo "4^3.5" | -scriptb.sh`
[result]
Edit: I just came up with a part of the solution by myself and thought I'd share it:
# ScriptA.sh
echo `echo "44 33" | bash ScriptB.sh`
# ScriptB.sh
read x y
echo $(($x+$y))
Output:
bash ScriptA.sh
77
The next problem is my ScriptB.sh looks a little more like this:
# ScriptB.sh
until [[ 1 = 2 ]]; do
echo Enter x and y
read x y
if [[ x = q ]]; then
break 1
fi
echo $(($x+$y))
done
This is in order to allow multiple inputs, if I want to use ScriptB manually. If I let ScriptA use ScriptB in the above mentioned way the output looks like this:
bash ScriptA.sh
b.sh: line 9: +: syntax error: operand expected (error token is "+")
Enter x and y 77 Enter x and y
It seems to be the case that after ScriptA inputs 44 and 33 and hits enter, like it should, but it hits enter right away a second time triggering the syntax error message and ending ScriptB. This is suboptimal, because in the case of the real ScriptB it will enter a "(standard_in) 1: parse error"-chain, resulting in no result at all. The solution to this problem would be by teaching ScriptA to read what ScriptB promts as result and ending it right after this. Or making it enter "q" as a second input instead of just hitting enter.
Edit 2:
Ok. Got it. Script A should look like this in order to work as desired:
e=2.7182818285
pi=3.141
a=$(printf "$e $pi \n q \n" | bash ScriptB.sh)
a=${a:14:20}
echo $a
Related
We have a simple exercise into which we have to get some values from a file and print out the result. The file contains a matrix and we have to do f=3*x^2 +4*y+5*z where x y z are the numbers of a 3x3 array that is inputted through a file.
Lets say that i name the matrix file f1.
How do i input the value of this file into the bash script
This is what i have done:
#!bin/bash/
while read x y z
do
let f=3*x*x+4*y+*z;
echo -n "f"
done < f1
exit 0
This script shows how to encapsulate an input file definition into the calling script, allowing a variable to be expanded into the resulting file (${MINE}).
if you wanted the contents to include non-expanded variables, you would put double quotes around the first "EnDoFiNpUt", on the "cat" line, not around the second instance. That would deposit the exact text into the file.
#!/bin/bash
MINE=8
cat >f1 <<EnDoFiNpUt
9 3 6
7 ${MINE} 2
2 1 4
EnDoFiNpUt
while read x y z
do
let f=3*x*x+4*y+z
echo -e "\t[${x} ${y} ${z}] f = ${f}"
done <f1
exit 0
I'm writing a bash script that converts units from a very specific input.
I started out doing simple read and echo statements and was able to get it to read a very specific input derived from declared integers and numbers but I'm having trouble getting it to work with if statements.
Here is my simple code so far:
#!/bin/bash
declare -i n
in=inches
ft=feet
read number in "as" feet
if [ ]; then
echo "$n $in = $[n/12] $ft"
fi
What I want to do now is to create if/else statements to flow according to a number of conditionals dependent on the user input. So I want the user to type something like "24 inches as feet" or "50 yards as inches" and execute its respective output. Right now, I don't really know what to put into the if statements without getting an error like "command not found".
Any help would be appreciated. Thank you.
First, don't use quotes on the variables in your read command.
read number in as feet
Another improvement is to rename the variables to represent what they store. I'm going to replace as with the underscore, a valid but "unreadable" name that emphasizes that it's just a placeholder, and we don't really care about its value.
read value src_unit _ dest_unit
# If the user enters "24 inches as feet", we have:
# value=24
# src_unit=inches
# _=as
# dest_unit=feet
Now, your if statements need to check two things: what is the units of the value, and what unit do we want to convert it to. Here's a template:
if [ "$src_unit" = X ] && [ "$dest_unit" = Y ]; then
# Convert X into Y
fi
You would replace X and Y with the units you are converting from and to, and
the code in the middle would be something like new_value=$(( $value / 12 )), if
converting from inches to feet. Note that bash cannot handle floating-point arithmetic,
which is a topic for another question.
A case statement, as suggested by cwgem:
case "$src_unit-$dest_unit" in
inches-feet)
new_value=$(( $value / 12 ))
;;
gallons-quarts)
new_value=$(( $value * 4 ))
;;
*) echo "I don't know how to convert $src_unit to $dest_unit"
;;
esac
!/bin/bash
echo "enter a number to be converted"
read number
feet=$(($number*12))
inches=$(($number/12))
echo "feet conversion to inches="$feet
echo "inches conversion to feet="$inches
fi
Assume let the value of the variable a be 42,
b=18+$a
The value of b should be 60
But I'm not getting the value 60. Instead it's printing 18+42.
How can i do it ?
New Query :
grep -F "$name" -A1000 filename | sed -n '1p;19p;24p'
Assume let a=10,b=20,c=30.In the above grep command can i use '$ap;$bp;$cp' instead of '1p;19p;24p' ?
Another thing, I've given as -A1000. Which implies that starting from 1p it considers till 1000 line , right ? I need to search throughout file without giving the number. Ho
Bash:
b=$((18+a))
echo "value of b is $b"
or
let b=18+a
echo "value of b is $b"
Any mathematical operation in Bash involves the use of $(())
So, for addition, you would do :-
b=$((18 + a))
Notice that the '$' before a is not required. Some more examples of mathematical operations in Bash are :-
a=$((17+1))
b=$((100-a))
c=$((a*b))
d=$((c/b))
echo $((10 * 1024 * 1024)) # Echoes the number bytes in 10 MB
I have a very basic shell script here:
for file in Alt_moabit Book_arrival Door_flowers Leaving_laptop
do
for qp in 10 12 15 19 22 25 32 39 45 60
do
for i in 0 1
do
echo "$file\t$qp\t$i" >> psnr.txt
./command > $file-$qp-psnr.txt 2>> psnr.txt
done
done
done
command calculates some PSNR values and writes a detailed summary to a file for each combination of file, qp and i. That's fine.
The 2>> outputs one line of information that I really need. But when executed, I get:
Alt_moabit 10 0
total 47,8221 50,6329 50,1031
Alt_moabit 10 1
total 47,8408 49,9973 49,8197
Alt_moabit 12 0
total 47,0665 50,1457 49,6755
Alt_moabit 12 1
total 47,1193 49,4284 49,3476
What I want, however, is this:
Alt_moabit 10 0 total 47,8221 50,6329 50,1031
Alt_moabit 10 1 total 47,8408 49,9973 49,8197
Alt_moabit 12 0 total 47,0665 50,1457 49,6755
Alt_moabit 12 1 total 47,1193 49,4284 49,3476
How can I achieve that?
(Please feel free to change the title if you think there's a more appropriate one)
You could pass the -n option to your first echo command, so it doesn't output a newline.
As a quick demonstration, this :
echo "test : " ; echo "blah"
will get you :
test :
blah
With a newline between the two outputs.
While this, with a -n for the first echo :
echo -n "test : " ; echo "blah"
will get you the following output :
test : blah
Without any newline between the two output.
The (GNU version of) echo utility has a -n option to omit the trailing newline. Use that on your first echo. You'll probably have to put some space after the first line or before the second for readability.
You can use printf instead of echo, which is better for portability reasons.
printf is the correct way to solve your problem (+1 kurumi), but for completeness, you can also do:
echo "$file\t$qp\t$i $( ./command 2>&1 > $file-$qp-psnr.txt )" >> psnr.txt
While echo -n may work if you just want the print the output to console, it won't work if you want the output redirected to file.
If you want the concatenated output to be redirected to a file, this will work:
echo "Str1: `echo "Str2"`" >> file
I was also facing the same problem.
To define my problem, I have a script which is using the echo function like this:
echo -n "Some text here"
echo -n "Some text here"
The output I was getting is like this:
-n Some text here
-n Some text here
and I want the text to be in same line and it is also printing -n option in the output.
Note :- According to man Page, -n option do not print the trailing newline character.
The way I solved it using by adding the shebang in the starting of the script file like this.
#!/bin/bash
echo -n "Some text here"
echo -n "Some text here"
This will print the desired output like this:
Some text here Some text here
Hope this helps!
Assume that I have programs P0, P1, ...P(n-1) for some n > 0. How can I easily redirect the output of program Pi to program P(i+1 mod n) for all i (0 <= i < n)?
For example, let's say I have a program square, which repeatedly reads a number and than prints the square of that number, and a program calc, which sometimes prints a number after which it expects to be able to read the square of it. How do I connect these programs such that whenever calc prints a number, square squares it returns it to calc?
Edit: I should probably clarify what I mean with "easily". The named pipe/fifo solution is one that indeed works (and I have used in the past), but it actually requires quite a bit of work to do properly if you compare it with using a bash pipe. (You need to get a not yet existing filename, make a pipe with that name, run the "pipe loop", clean up the named pipe.) Imagine you could no longer write prog1 | prog2 and would always have to use named pipes to connect programs.
I'm looking for something that is almost as easy as writing a "normal" pipe. For instance something like { prog1 | prog2 } >&0 would be great.
After spending quite some time yesterday trying to redirect stdout to stdin, I ended up with the following method. It isn't really nice, but I think I prefer it over the named pipe/fifo solution.
read | { P0 | ... | P(n-1); } >/dev/fd/0
The { ... } >/dev/fd/0 is to redirect stdout to stdin for the pipe sequence as a whole (i.e. it redirects the output of P(n-1) to the input of P0). Using >&0 or something similar does not work; this is probably because bash assumes 0 is read-only while it doesn't mind writing to /dev/fd/0.
The initial read-pipe is necessary because without it both the input and output file descriptor are the same pts device (at least on my system) and the redirect has no effect. (The pts device doesn't work as a pipe; writing to it puts things on your screen.) By making the input of the { ... } a normal pipe, the redirect has the desired effect.
To illustrate with my calc/square example:
function calc() {
# calculate sum of squares of numbers 0,..,10
sum=0
for ((i=0; i<10; i++)); do
echo $i # "request" the square of i
read ii # read the square of i
echo "got $ii" >&2 # debug message
let sum=$sum+$ii
done
echo "sum $sum" >&2 # output result to stderr
}
function square() {
# square numbers
read j # receive first "request"
while [ "$j" != "" ]; do
let jj=$j*$j
echo "square($j) = $jj" >&2 # debug message
echo $jj # send square
read j # receive next "request"
done
}
read | { calc | square; } >/dev/fd/0
Running the above code gives the following output:
square(0) = 0
got 0
square(1) = 1
got 1
square(2) = 4
got 4
square(3) = 9
got 9
square(4) = 16
got 16
square(5) = 25
got 25
square(6) = 36
got 36
square(7) = 49
got 49
square(8) = 64
got 64
square(9) = 81
got 81
sum 285
Of course, this method is quite a bit of a hack. Especially the read part has an undesired side-effect: termination of the "real" pipe loop does not lead to termination of the whole. I couldn't think of anything better than read as it seems that you can only determine that the pipe loop has terminated by try to writing write something to it.
A named pipe might do it:
$ mkfifo outside
$ <outside calc | square >outside &
$ echo "1" >outside ## Trigger the loop to start
This is a very interesting question. I (vaguely) remember an assignment very similar in college 17 years ago. We had to create an array of pipes, where our code would get filehandles for the input/output of each pipe. Then the code would fork and close the unused filehandles.
I'm thinking you could do something similar with named pipes in bash. Use mknod or mkfifo to create a set of pipes with unique names you can reference then fork your program.
My solutions uses pipexec (Most of the function implementation comes from your answer):
square.sh
function square() {
# square numbers
read j # receive first "request"
while [ "$j" != "" ]; do
let jj=$j*$j
echo "square($j) = $jj" >&2 # debug message
echo $jj # send square
read j # receive next "request"
done
}
square $#
calc.sh
function calc() {
# calculate sum of squares of numbers 0,..,10
sum=0
for ((i=0; i<10; i++)); do
echo $i # "request" the square of i
read ii # read the square of i
echo "got $ii" >&2 # debug message
let sum=$sum+$ii
done
echo "sum $sum" >&2 # output result to stderr
}
calc $#
The command
pipexec [ CALC /bin/bash calc.sh ] [ SQUARE /bin/bash square.sh ] \
"{CALC:1>SQUARE:0}" "{SQUARE:1>CALC:0}"
The output (same as in your answer)
square(0) = 0
got 0
square(1) = 1
got 1
square(2) = 4
got 4
square(3) = 9
got 9
square(4) = 16
got 16
square(5) = 25
got 25
square(6) = 36
got 36
square(7) = 49
got 49
square(8) = 64
got 64
square(9) = 81
got 81
sum 285
Comment: pipexec was designed to start processes and build arbitrary pipes in between. Because bash functions cannot be handled as processes, there is the need to have the functions in separate files and use a separate bash.
Named pipes.
Create a series of fifos, using mkfifo
i.e fifo0, fifo1
Then attach each process in term to the pipes you want:
processn < fifo(n-1) > fifon
I doubt sh/bash can do it.
ZSH would be a better bet, with its MULTIOS and coproc features.
A command stack can be composed as string from an array of arbitrary commands
and evaluated with eval. The following example gives the result 65536.
function square ()
{
read n
echo $((n*n))
} # ---------- end of function square ----------
declare -a commands=( 'echo 4' 'square' 'square' 'square' )
#-------------------------------------------------------------------------------
# build the command stack using pipes
#-------------------------------------------------------------------------------
declare stack=${commands[0]}
for (( COUNTER=1; COUNTER<${#commands[#]}; COUNTER++ )); do
stack="${stack} | ${commands[${COUNTER}]}"
done
#-------------------------------------------------------------------------------
# run the command stack
#-------------------------------------------------------------------------------
eval "$stack"