1

I'm using zsh, and I have this function:

function timer-raw() {
    # Just think `loop fsayd` is `echo hi` for the purposes of this question.
    eval "sleep $((($1)*60))" && eval ${(q+@)@[2,-1]:-loop fsayd}
}

timer-raw's first argument tells it how many minutes to wait; The rest of the arguments, if present, would be evaled as if typed on the command line, after the wait:

timer 3 echo sth
# After 3 minutes, echoes `sth`.

However, if no further arguments are supplied, I want it to run the default command loop fsayd:

timer 4
# runs `loop fsayd` after 4 minutes

The problem is that I want loop fsayd to be substituted as two words, but I don't know how. I tried these variations, too, but they didn't work either:

"${(q+@)${@[2,-1]:-loop fsayd}}"
"${(q@)${(q@)=@[2,-1]:-loop fsayd}}"
"${(qq@)=@[2,-1]:-loop fsayd}"

By "didn't work", I mean either a simple timer 0 would fail by a command not found, or that timer 0 ff hi 'man hi jk k' failed to return the correct number of input arguments. (ff() echo "$#")

Note: I do not want to use test -z "${@[2,-1]}".

5
  • Please show how exactly you call your function, what happens and what you want to happen. Maybe you can replace the default value with something like if [ check_that_your_variables_are_defined ] ; then eval your_variables ; else loop fsayd ; fi Commented Aug 9, 2019 at 16:04
  • @Bodo As the comment says, loop fsayd is a very simple command that can be substituted by echo hi if you want to test it yourself. The problem is that it needs to be substituted as "loop" "fsayd" but it is substituted as "loop fsayd" which causes a command not found error. Commented Aug 9, 2019 at 16:36
  • Repeating the information from the question doesn't help. I suggest to edit your question to add more information. You should explain your variable substitution and what would be the case when the script will not use the default value (echo hi). For echo hi a double eval eval ${...} works, but I don't know what would be the other case. Commented Aug 9, 2019 at 16:54
  • @bodo Better now? Commented Aug 9, 2019 at 17:36
  • 1
    there are of course alternatives to using default values to solve this particular case but I guess those are outside the scope. Commented Aug 9, 2019 at 17:47

2 Answers 2

1

Lol I figured it out:

function timer-raw() {
    eval "sleep $((($1)*60))" && eval ${(q+@)@[2,-1]:-${(z)/#/loop fsayd}}
}

This substitutes (${name/pattern/repl}) the contents of the non-existent variable named "" (nothing) -- which is an empty string -- replacing the beginning of the (empty) string (pattern #) with loop fsayd, while doing word splitting (options (z))..... :D

1
  • Feel free to un-upvote the original solution :) Commented Aug 9, 2019 at 18:25
1

If it is okay to define the defaults in a variable, you should be able to do:

function timer-raw() {
    defs=( loop fsayd )
    eval "sleep $((($1)*60))" && eval ${(q+@)@[2,-1]:-$defs}
}

Personally I would not use eval and just do:

function timer-raw() {
    defs=( loop fsayd )
    sleep ${1}m && ${@[2,-1]:-$defs}
}
2
  • 1
    It's the best thing if there is no better way. (eval is necessary to make it accept aliases.) Commented Aug 9, 2019 at 18:00
  • Can't say for sure; didn't figure out a better way yet :) Commented Aug 9, 2019 at 18:01

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.