How to give a magic variable to a script in Bash

Viewed 148

How to give a magic variable (placeholder) to a script?

Let's have a loop script:

#!/bin/bash

count=$1
shift

for i in $(seq 1 $count)
do
    $*
done

I want to call it like that:

loop 5 echo Mambo number $i

However, Bash expands the variable before the script call and produces:

Mambo number
Mambo number
Mambo number
Mambo number
Mambo number

If I call it like that:

loop 5 echo Mambo number '$i'

the variable is not expanded within the script

Mambo number $i
Mambo number $i
Mambo number $i
Mambo number $i
Mambo number $i

Is there a clever way to use a magic variable in bash?

I want to achieve:

Mambo number 1
Mambo number 2
Mambo number 3
Mambo number 4
Mambo number 5

And also:

loop 3 touch 'foo-$i.log'

To create three files:

foo-1.log
foo-2.log
foo-3.log
2 Answers

It seems you want to eval your arguments

#!/bin/bash
    
count=$1
shift
    
for i in $(seq 1 $count)
do
    eval "$@"
done

you have to protect any variables to be expanded later

bash loop.sh 3 echo test '"$i"'
test 1
test 2
test 3

or better protect the whole string in single quotes to be one string argument

bash loop.sh 3 'echo "$i" "$((i+1))"  $(bc -l <<< "$i+2")'
1 2 3
2 3 4
3 4 5

and carefully.

I can think of two ways to delay the evaluation of a variable (do "lazy evaluation") in bash.

  1. with the ${!varname} "indirect expansion" form, which is not portable to other shells

  2. with eval, which is portable, and more versatile and obvious, but will get you into trouble with some, huh, less-than-ideal people examining your code ;-)

So, I'll give a possible solution with the first.

loop(){
   local i cnt=$1 cmd=$2 arg=$3
   for((i=0;i<cnt;i++)); do "$cmd" "${!arg}"; done 
}
mambo(){
   printf '%s ' mambo nr "$@"; echo
}
loop 6 mambo i
mambo nr 0
mambo nr 1
mambo nr 2
mambo nr 3
mambo nr 4
mambo nr 5
Related