Search code examples
bashzshgnu-parallel

Why does zsh expand globs for me in a bash script using GNU parallel?


In a bash script, I have a command using rsync:

#!/usr/bin/bash -e
...
parallel rsync --exclude '*to?be?deleted*' ... 
    --files-from some_file /auto $instance_ip:/somewhere_else/

According to rsync's documentation, their --exclude field has a different style of pattern matching.

When I run this in a bash terminal, it works fine.

However, running this on zsh gives me an error because zsh tries to expand this literal string I'm trying to pass in:

zsh:1: no matches found: *to?be?deleted*

This should not happen. Why is zsh even expanding my globs in my bash script in the first place? Is there some settings on my zsh that I can set to make the two behave the same way? I don't want to develop in zsh and deploy to an environment with bash and have to behave in different ways.

I am using oh-my-zsh's plugins:

plugins=(
  git
  colored-man-pages
  zsh-autosuggestions
  zsh-syntax-highlighting
)

Specifically, with this set of commands it fails:

#!/usr/bin/bash -e
find . -name '*filelist' | parallel -j10 rsync --exclude "*to?be?deleted*" testing somewhere_else:/some/where/else

But with rsync's command by itself, it doesn't break.


Solution

  • parallel is starting an instance of your login shell using a string consisting of the arguments it is passed. Your bash script strips the quotes before passing the argument, so parallel is executing the equivalent of

    zsh -c "rsync --exclude *to?be?deleted* testing somewhere_else:/some/where/else"
    

    in which the pattern is not quoted. To prevent this, pass a single string as an argument to parallel:

    ... | parallel -j10 'rsync --exclude "*to?be?deleted*" testing somewhere_else:/some/where/else'