I have the following aliases set in my zsh config:

alias artisan='[ -f vendor/bin/sail ] && sail artisan || php artisan'
alias sail='[ -f sail ] && bash sail || bash vendor/bin/sail'
alias dep='[ -f dep ] && dep || vendor/bin/dep'

The sail and dep aliases work as expected. For example, I can type sail up or dep deploy and the arguments (up, deploy) will be passed to the aliased command.

However, for artisan this is not the case. I always get the default help page that will be displayed when no argument was supplied. Why does this happen?

  • It probably breaks when you’re not in the “root” directory. You’re calling a command that assumes you’re in a directory where vendor/… exists. If you’re in a different one, it will fail.
    – Allan
    Commented Apr 2, 2023 at 16:58
  • @Allan those with vendor are the two working
    – mmmmmm
    Commented Apr 2, 2023 at 17:18
  • What do you expect the artisan alias tro be expanded to?
    – mmmmmm
    Commented Apr 2, 2023 at 17:18
  • @mmmmmm, artisan is the only alias that specifies a path for the conditional test. . Sail and dep could be in the path which is why those work
    – Allan
    Commented Apr 2, 2023 at 17:22
  • Ah yes missed that
    – mmmmmm
    Commented Apr 2, 2023 at 17:29

1 Answer 1


An alias is a simple text substitution, so the command artisan up will be expanded to this:

[ -f vendor/bin/sail ] && sail artisan || php artisan up

The up is only at the end. If the vendor/bin/sail file exists, then the sail artisan command will be executed without any other parameters. If the file does not exist, the command after || will be run - since it's at the end, it will get up passed as a parameter. That's probably what happened in the situations where the alias worked.

To pass arguments to a command, you can use a function:

artisan() {
  if [ -f vendor/bin/sail ]; then
    sail artisan "$@"
    php artisan "$@"

"$@" will expand to the parameters from the function invocation, i.e. with artisan up it'll pass up to either of the commands.

Note that this example uses an if-else statement. Although [ condition ] && cmd1 || cmd2 looks like a ternary statement, it really isn't - if the condition evaluates to true and cmd1 exits with a non-zero return code, cmd2 will also be executed. Most of the time that's unexpected behavior.

  • Thank you. Do I understand it correctly, that therefore my sail and dep aliases only worked, because there were no files called sail and dep in my current working directory, therefore it used the "fallback" with my arguments substituted to the end? Knowing that the substitution happens immediately explains a lot, but makes sense when thinking about it. Commented Apr 3, 2023 at 23:29
  • That's correct. I updated the answer to try to explain it a bit better.
    – Gairfowl
    Commented Apr 4, 2023 at 5:56
  • 1
    Good remark about the unexpected side effects of [ c ] && c1 || c2. They can be avoided by using [ c ] && { c1; true; } || c2 but using a proper if then else might be easier then :-)
    – nohillside
    Commented Apr 4, 2023 at 6:38
  • 2
    @nohillside And in zsh if-else can be just as compact: if [[ c ]] { c1 } else { c2 }.
    – Gairfowl
    Commented Apr 4, 2023 at 16:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .