1

I encountered a very strange behavior when running below command, let me explain the issue:

Consider this simple bash script:

#!/bin/bash
zip -r /var/backup.zip /var/www -x /ignore_dir_1/\*

It compresses the whole www folder recursively and excludes ignore_dir_1 which is perfectly fine.

Now, write that script like this:

#!/bin/bash
Exclude="/ignore_dir_1/\*"
zip -r /var/backup.zip /var/www -x $Exclude

It runs without error but does not exclude ignore_dir_1.

Can anyone please explain this behavior?

- Disclaimer:
I have already tried the following alternatives:
Exclude="/ignore_dir_1/*"
Exclude="/ignore_dir_1/***"

Update:
Thanks to @pLumo, the problem solved by putting variable inside quotation like:

#!/bin/bash
Exclude="/ignore_dir_1/*"
zip -r /var/backup.zip /var/www -x "$Exclude"

Now, the problem is if Exclude variable contain multiple folders, it does not work, I mean this:

#!/bin/bash
Exclude="/ignore_dir_1/* /ignore_dir_2/*"
zip -r /var/backup.zip /var/www -x "$Exclude"

I even tried "${Exclude}" but no result.

  • 3
    Always quote your variables --> -x "$Exclude", then your first alternative should work just fine. – pLumo Jul 18 '22 at 12:02
  • It worked! Can you please write your comment as an answer and also describe the reason? I would appreciate that. – nima1024 Jul 18 '22 at 12:07
  • That's already very well documented and if you had done what the bash tag you included tells you to do (For shell scripts with errors/syntax errors, please check them with the shellcheck program (or in the web shellcheck server at https://shellcheck.net) before posting here.) that tool would have told you about the issue and provided a link to documentation about it. – Ed Morton Jul 18 '22 at 12:21
  • I'm so sorry about that, I will promise to read documentations carefully before posting. Thanks. – nima1024 Jul 18 '22 at 12:24
  • @pLumo Would you please read my updated question? I would be really grateful. Thanks. – nima1024 Jul 18 '22 at 12:52
  • 1
    Change each zip command to echo zip and review the result when you run the script. You'll (hopefully) see why the command is failing to do what you want. (Hint: double-quote your variables when you use them.) – Chris Davies Jul 18 '22 at 13:51
  • 1
    you can also write a file of excluded patterns and pass that file to zip. zip ... -x @excludes.txt – don_aman Jul 18 '22 at 15:56

2 Answers2

3

If you write ...

Exclude="/ignore_dir_1/* /ignore_dir_2/*"
zip -r /var/backup.zip /var/www -x "$Exclude"

, zip receives $Exclude as a single argument, and considers the space between the files as part of the path.


To pass multiple arguments to your command, you need to use an array.

Exclude=("/ignore_dir_1/*" "/ignore_dir_2/*")
zip -r /var/backup.zip /var/www -x "${Exclude[@]}"

This makes sure that the items are individually expanded and passed as arguments to your command.

pLumo
  • 22,565
0

ninja'd by pLumo :-P


I suggest that you use an array,

#!/bin/bash

echo "directory tree ---------------------------------------------------"

find

echo "without exclusions -----------------------------------------------"

echo zip -sf -r backup.zip . zip -sf -r backup.zip .

echo "with exclusions --------------------------------------------------"

declare -a Exclude=(ignore_dir_1/* ignore_dir_2/*)

echo zip -sf -r backup.zip . -x ${Exclude[@]} zip -sf -r backup.zip . -x ${Exclude[@]}

echo 'double quoted ${Exclude[@]} --------------------------------------'

echo zip -sf -r backup.zip . -x "${Exclude[@]}" zip -sf -r backup.zip . -x "${Exclude[@]}"

Result with a directory tree shown before the 'dry runs',

$ ./script 
directory tree ---------------------------------------------------
.
./dir_0
./dir_0/file-1
./dir_0/file-2
./ignore_dir_1
./ignore_dir_1/file-11
./ignore_dir_1/file-12
./ignore_dir_2
./ignore_dir_2/file name with spaces
./ignore_dir_2/file-22
./ignore_dir_2/file-21
./script
without exclusions -----------------------------------------------
zip -sf -r backup.zip .
Would Add/Update:
  dir_0/
  dir_0/file-1
  dir_0/file-2
  ignore_dir_1/
  ignore_dir_1/file-11
  ignore_dir_1/file-12
  ignore_dir_2/
  ignore_dir_2/file name with spaces
  ignore_dir_2/file-22
  ignore_dir_2/file-21
  script
Total 11 entries (621 bytes)
with exclusions --------------------------------------------------
zip -sf -r backup.zip . -x ignore_dir_1/file-11 ignore_dir_1/file-12 ignore_dir_2/file-21 ignore_dir_2/file-22 ignore_dir_2/file name with spaces
Would Add/Update:
  dir_0/
  dir_0/file-1
  dir_0/file-2
  ignore_dir_1/
  ignore_dir_2/
  script
Total 6 entries (621 bytes)
double quoted ${Exclude[@]} --------------------------------------
zip -sf -r backup.zip . -x ignore_dir_1/* ignore_dir_2/*
Would Add/Update:
  dir_0/
  dir_0/file-1
  dir_0/file-2
  script
Total 4 entries (621 bytes)
sudodus
  • 6,421