0

I have two scripts that work together by themselves and I have no idea how to make them work together

the task is to make a bash script accept operators and a list of integers and having the command perform the operations.

I basically need to be able to use the script as such

test.sh add 1 2 3
5
test.sh sub 4 2 1
1

the first script I have is

for var in $@
do

sum=$(( sum + var )) done echo $sum

second script is

if [ $operator = add ]; then
add= echo $(($a+$b))

elif

Kusalananda
  • 333,661
  • (1) Never say for var in $@.  Either shorten it to for var or change it to for var in "$@" (note the quotes).  (2) What are you asking?  You say you want to do test.sh add 1 2 3, but then you say you want to do test.sh 1 2 3.  (3) Your second script is nearly what you want.  Your first script adds all the arguments; what part of that do you want? – G-Man Says 'Reinstate Monica' Feb 12 '21 at 06:26
  • Ah my mistake, I meant to put test.sh add 1 2 3 for both

    the first script allows it to dotest.sh 1 2 3 and I need it to be able to add numbers together to get an output so my example was test.sh add 1 2 3 to get 5

    – Dooooofy Feb 12 '21 at 06:34
  • add= echo $(( sum + var )) fi echo $add ``` pops up an error line 7: [: =: unary operator expected I'm not sure what is means – Dooooofy Feb 12 '21 at 07:01
  • $operator is unset in your 2nd script, so the command sees [ = add ] and complains, that the first operand is missing. – pLumo Feb 12 '21 at 07:15
  • 1
    What is your initial task? Why do you want to do this? Do you want to learn scripting (good, we can help, but probably there are easier ways to achieve it) or do you want to sum up numbers (you don't need to script it yourself, rather use programs doing it for you). – pLumo Feb 12 '21 at 07:19
  • I'm trying to get a grasp on how to get scripting down, and I'm struggling

    the task is just to write a bash script that accepts operators and a list of integers

    – Dooooofy Feb 12 '21 at 07:25
  • Btw: why should test.sh add 1 2 3 return 5 and not 6? – pLumo Feb 12 '21 at 07:32
  • I would do calculator() { op=$1; shift; printf '%s\0' "$@" | paste -szd"$op" | xargs -0 echo 'scale=5;' | bc ; } and then run calculator + 1 2 3 --> 6 or calculator '/' 1 2 3 --> 0.16666 – pLumo Feb 12 '21 at 07:32
  • 1
    I'm just saying it is difficult to help you if we don't exactly know what you want to achieve, especially because your second script's purpose and how it should work is unclear. – pLumo Feb 12 '21 at 07:35
  • test.sh add 1 2 3 returns 5 because its adding the numbers together not multiplying them. what would I make operator equal? I need it to be able to do addition and subtraction.

    my task is as follows

    using a loop command write a bash script that will accept an operator and a list of integers and have it execute the operation on the list

    – Dooooofy Feb 12 '21 at 07:41
  • you know that 1+2+3 is 6 ? – pLumo Feb 12 '21 at 07:49
  • wow I really am dyslexic, I've been working on this for a couple of hours, my bad – Dooooofy Feb 12 '21 at 07:52

2 Answers2

1

A script that picks out its first command line argument into a separate variable called op and then removes this from the list of command line arguments:

#!/bin/sh

op=$1 shift

If this script is called with add 1 2 3 as arguments, then the code would assign the string add to op and would leave 1 2 3 as the only three command line arguments in "$@".

We may then loop over the remaining command line arguments:

#!/bin/sh

op=$1 shift

for arg do # some commands should go here done

The added loop will make sure that the variable arg will take the value of each of the remaining command line arguments in turn. We could also have written the start of the loop like for arg in "$@"; do ... but it's more to type and programmers sometimes forget to add the quotes around $@.

Depending on the value $op one of several operations may happen in the loop. We may use a simple test to see what $op is and carry out the correct operation. In any case, some value is being accumulated in acc below, and this acc value should (according to the way the commands are presented in the question) be initialized to the first number, and that number needs to be shifted off the list of arguments too.

#!/bin/sh

op=$1 shift

acc=$1 shift

for arg do case $op in add) acc=$(( acc + arg )) ;; sub) acc=$(( acc - arg )) ;; mul) acc=$(( acc * arg )) ;; div) acc=$(( acc / arg )) ;; # note: integer division here *) printf 'Unknown operation: %s\n' "$op" >&2 exit 1 esac done

printf '%s\n' "$acc"

Testing this script after saving it and making it executable:

$ ./script add 1 2 3
6
$ ./script sub 4 2 1
1
$ ./script xor a b c
Unknown operation: xor
$ ./script add
script[7]: shift: nothing to shift

Note the two last invocations. The last one failed to provide any numbers, so the second shift fails. The second to last invocation notices that the operation is unknown and terminates prematurely.

By changing the assignment to op and the initialization of acc slightly, we can make the error messages slightly more useful:

#!/bin/sh

op=${1:?Expected operator} shift

acc=${1:?Expected number} shift

for arg do case $op in add) acc=$(( acc + arg )) ;; sub) acc=$(( acc - arg )) ;; mul) acc=$(( acc * arg )) ;; div) acc=$(( acc / arg )) ;; # note: integer division here *) printf 'Unknown operation: %s\n' "$op" >&2 exit 1 esac done

printf '%s\n' "$acc"

Testing again:

$ ./script
script[3]: 1: Expected operator
$ ./script add
script[6]: 1: Expected number
$ ./script add 1 2
3

The exact formatting of these error messages may differ depending on what shell acts as /bin/sh on your system.

Kusalananda
  • 333,661
0

Your first script is looping through $@, if you want to add an operand, you can save the first argument (op=$1) and then use shift.

from man bash:

shift [n]
The positional parameters from n+1 ... are renamed to $1 .... Parameters represented by the numbers $# down to $#-n+1 are unset. n must be a non-negative number less than or equal to $#. If n is 0, no parameters are changed. If n is not given, it is assumed to be 1. If n is greater than $#, the positional parameters are not changed. The return status is greater than zero if n is greater than $# or less than zero; otherwise 0.

Initialize $result with the first argument, then use shift again.

Also, make sure to quote $@ when running the loop (read here).

#!/bin/bash
op=$1
shift
result=$1
shift
for var in "$@"; do
  result=$(( result $op var ))
done
echo "$result"

You could also do the shift in one step:

op=$1
result=$2
shift 2

Run test.sh + 1 2 3 or test.sh '*' 1 2 3 ...

('*' must be quoted, to not expand to filenames).


However, I would rather use bc and $IFS instead of a loop and $(( ... ))) for this:

#!/bin/bash
OLDIFS="$IFS"
IFS=$1
shift
bc <<< "scale=5; $*"
IFS=$OLDIFS

See also:

"$*" expands to a single word "$1c$2c...". Usually c is a space, but it's actually the first character of IFS, so it can be anything you choose.

pLumo
  • 22,565