1

In bashrc, I have this alarm function which can take 3 variables:

a () {
    local $1="${1:-3600}"
   local $2="${2:-paa}"
   local $3="${3:-alarm}"
    sleep "$1" && $2 && $3
}
alias pah='pactl set-card-profile 0 output:hdmi-stereo'
alias paa='pactl set-card-profile 0 output:analog-stereo'
alias alarm='vlc ~/alarm.mp3'

On the command line, the following command executes my aliases fine.

sleep 3600 && paa && alarm

However, when I try to make this part of a function shown above, I get this error:

bash: pah: command not found

Also: The $1, $2, $3 variables should default to 3600, paa, and alarm.

I'm also confused about how I would write call this function with a $3 input, while allowing $1 and $2 to be defaulted.

i.e.

a null null alarm2

Repost Note: I posted this in Stack Overflow, but realize that this question probably belongs on Unix & Linux stack exchange.

MarkB
  • 25

1 Answers1

2

I don't see how you'd get this output from running a: did you post output from a different version? But anyway I do see what's wrong with the code.

The first problem is that local $1=… doesn't do what you think it does. It sets a variable whose name is the first parameter, it doesn't set the first parameter. For example, if you call the function as a foo, the first line sets the local variable foo to the value foo. If you call a with no arguments, you get

bash: local: `=3600': not a valid identifier

(and more errors) because what's on the left of = is an empty string and the empty string is not a valid name for a variable.

You can't use the assignment syntax to set the numbered parameters: local 1=… doesn't work either. You need to either give the variable a name or use the set builtin to set all the numbered parameters at once.

a () {
    local delay="${1:-3600}"
    local first_command_to_split_and_glob="${2:-paa}"
    local second_command_to_split_and_glob="${3:-alarm}"
    sleep "$delay" && $first_command_to_split_and_glob && $second_command_to_split_and_glob
}

The second problem is that aliases are only expanded when they appear explicitly at the beginning of a command. They are not expanded from the result of some other expansion such as taking the value of a variable.

There's another problem in the call $first_command_to_split_and_glob, which is that it only works in simple cases. It splits the value of the variable (i.e. the argument passed to the function) at whitespace, then interprets each part as wildcard pattern which it expands if the pattern matches at least one file name. For example a 2 'cat "file name with spaces"' tries to display the content of the files called "file, name, with and spaces". A variable expansion outside of quotes applies the “split+glob” operation: it neither yields the value of the variable nor performs evaluation on the value, but something intermediate which is rarely useful. See Why does my shell script choke on whitespace or other special characters? for more details.

To fix these two problems, use eval on the string. Note the double quotes around "$first_command" and "$second_command" to avoid split+glob.

a () {
    local delay="${1:-3600}"
    local first_command="${2:-paa}"
    local second_command="${3:-alarm}"
    sleep "$delay" && eval "$first_command" && eval "$second_command"
}

Finally, to pass an empty argument, use quotes to delimit an empty string. For example:

a '' pah

waits 3600 seconds, then runs pah and then runs alarm.