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]}".

  • 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 – Bodo Aug 9 '19 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. – HappyFace Aug 9 '19 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. – Bodo Aug 9 '19 at 16:54
  • @bodo Better now? – HappyFace Aug 9 '19 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. – Jonas Berlin Aug 9 '19 at 17:47
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

  • Feel free to un-upvote the original solution :) – Jonas Berlin Aug 9 '19 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}
}
  • 1
    It's the best thing if there is no better way. (eval is necessary to make it accept aliases.) – HappyFace Aug 9 '19 at 18:00
  • Can't say for sure; didn't figure out a better way yet :) – Jonas Berlin Aug 9 '19 at 18:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.