1

How can I "inject" a function argument to a defined variable like in this example?

mood="i am $1 happy"

happy ()
{
    echo "$mood"
}

happy "very"

Current output:

i am  happy

Desired output:

i am very happy

Thanks!

Edit:

The real world example is: I have a lot of translatable strings in another file, like so:

installing="Installing"
installation_started="The installation of <app> started at <date>"
installation_ended="The installation of <app> ended at <date>"

And a function:

apt_get_install ()
{
    echo "$installing $1..."
    echo "$installation_started"
    apt-get -y install "$1"
    echo "$installation_ended"
}

apt_get_install <app>

And then I want to inject <app> to the output.

bone
  • 13

4 Answers4

1

This should work. Be very careful with eval, never use eval on user input, it will execute anything.

mood='i am $1 happy'

happy ()
{
    eval echo "$mood"
}

happy "very"
RalfFriedl
  • 8,981
  • Thanks, I try to avoid using eval but your answer solves the problem. Accepted. – bone Sep 05 '18 at 08:25
  • That means eval ends up evaluating echo i am $1 happy where $1 is not quoted. You'd want: eval "echo \"$mood\"". So we end up evaluating echo "i am $1 happy" instead. – Stéphane Chazelas Sep 05 '18 at 10:20
1

You seem to want to provide informational messages to a user in various languages. You should probably use software specifically designed for this purpose, if you want to do it right. In the GNU world, there is gettext that may be of use to you. (It is not a trivial problem to solve!)

The text below addresses the question before its update, and part of the updated question (before I noticed that it was about providing translated messages):


Your example does not make much sense as your function is just an alias for echo. You can easily call your happy function as

happy "I am $adjective happy"   # $adjective would be the string "very"

Apart from that, it sounds like the standard printf function could also be useful to know about:

adjective='very'
printf 'I am %s happy\n' "$adjective"

printf take one or more arguments, where the first is a static string describing the output format, possibly containing placeholders, and the rest of the arguments are referenced by the placeholders.


If the messages are static format strings, then just use printf:

apt_get_install ()
{
    printf 'Installing %s...\n' "$1"
    printf '%s installation started at %s\n' "$1" "$(date)"
    apt-get -y install "$1"
    printf '%s installation ended at %s\n' "$1" "$(date)"
}

apt_get_install thing

Related:

Kusalananda
  • 333,661
0

Try the below code,

function happy ()
{
        mood="i am $1 happy"
        echo "$mood"
}

happy "very"

Or Try the below one,

function happy ()
{
        echo "i am $1 happy"
}

happy "very"
Stack EG
  • 1,636
0

With zsh:

mood='I am $1 happy'

happy () {
  printf '%s\n' ${(e)mood}
}

happy very

The e parameter expansion flags causes all the parameter expansions, arithmetic expansions and command substitutions to be evaluated in the content of the variable (backslash in front of the $ or ` can be used to prevent them).

Another approach that avoids dangerous things like the e or eval is to use gettext's envsubst, where you can specify exactly what you want substituted:

mood='I am $HOWMUCH happy'
happy() {
  HOWMUCH=$1 envsubst <<< "$mood"
}
happy very

Though <<< is also a zsh extension, it is now supported by a few other shells including bash.

Replace envsubst with envsubst '$HOWMUCH' if you want to make sure that only $HOWMUCH (or ${HOWMOUCH}) gets substituted.

With ksh93 or zsh, you can also use:

mood='I am %1$s happy\n'
happy() {
  printf "$mood" "$@"
}

In other printf implementations that don't support the %1$s to specify which argument to use, in this particular case, you can use %s. If the format has %s %s, the first %s gets the first argument, the second the second argument. You can use %2$s %1$s to reverse the order with ksh93 or zsh. In other implementations, you could use this trick instead:

case $(locale language) in
  (*English*) msg="I am in a %s %s%.0s%.0s\n";     mood=mood;;
  (*French*)  msg="Je suis d'une %.0s%s %s%.0s\n"; mood=humeur;;
esac

mood() {
  printf "$msg" "$@" "$@"
}

mood formidable "$mood"

Which would print I'm in a formidable mood to an English-speaking user and Je suis d'une humeur formidable to a French-speaking user. We pass the arguments twice and use %.0s (the string truncated to zero width) to discard the ones we don't want to be able to use those arguments in different orders.