-2

I wrote a bash script to retrieve the last few days file info from the file system, and the file under some sub-folders will be excluded. Here is the script(test.sh):

#!/bin/bash
date_range=$1
base_dir=$2
excluded_dir=$3

# Command initialization
cmd="find $base_dir"
for item in ${excluded_dir[@]}
do
  cmd="$cmd -not \( -path '$base_dir/$item' -prune \)"
done
cmd="$cmd -type f -mtime -$date_range -ls"
echo $cmd

$cmd

I tried an example as below:

./test.sh 3 /root "excluded_folder1 excluded_folder2" 

The command has been initialized as:

find /root -not \( -path '/root/excluded_folder1' -prune \) -not \( -path '/root/excluded_folder2' -prune \) -type f -mtime -3 -ls

If I run this find command in my terminal, it works fine, I can get the results that I want. While if it's executed in the bash script. I always get such an error:

find: paths must precede expression: \(
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]

Does anybody knows what is the problem and how to fix this?

Coding_Rabbit
  • 1,287
  • 3
  • 22
  • 44
  • 2
    Possible duplicate of [Why does shell ignore quotes in arguments passed to it through variables?](https://stackoverflow.com/questions/12136948/why-does-shell-ignore-quotes-in-arguments-passed-to-it-through-variables) – oguz ismail Dec 06 '19 at 15:35
  • I suspect the issue is with the `\(` character being converted to just (. You may need to use `\\(` when you build your cmd. And the same for the `\)` of course. On second thought, just replace `\(` with `(` in your quoted string and the same for `\)`. The reason: when you type the command at the shell prompt, you need the backslash to escape the meaning of `(`. However, when you have it quoted (as in your script) you do not need to escape the `(`. – Mark Dec 06 '19 at 15:37
  • 2
    See [I'm trying to put a command in a variable, but the complex cases always fail!](https://mywiki.wooledge.org/BashFAQ/050). – chepner Dec 06 '19 at 16:50
  • Also, `excluded_dir` is not an array; there is no point using `${excluded_dir[@]}` when `$excluded_dir` produces the exact same expansion. – chepner Dec 06 '19 at 16:51

2 Answers2

0

Thanks for all the answers and suggestions I received here. But none of that solved my problem. The problem is finally solved by using 'eval' to execute the command. The final working bash script is as below:

#!/bin/bash
date_range=$1
base_dir=$2
excluded_dir=$3

# Command initialization
cmd="find $base_dir"
for item in ${excluded_dir[@]}
do
  cmd="$cmd -not \( -path '$base_dir/$item' -prune \)"
done
cmd="$cmd -type f -mtime -$date_range -ls"

eval $cmd

While there're some posts saying using eval in bash script is a bad and insecure choice, I still don't know how can I solve this problem with some other approaches. If someone got a better idea, please post it here.

Reference:

  1. What is the eval command in bash?
  2. Why and when should eval use be avoided in shell scripts?
Coding_Rabbit
  • 1,287
  • 3
  • 22
  • 44
-1

Based on my guess above, I suggest the following code:

#!/bin/bash
date_range=$1
base_dir=$2
excluded_dir=$3

# Command initialization
cmd="find $base_dir"
for item in ${excluded_dir[@]}
do
  cmd="$cmd -not ( -path '$base_dir/$item' -prune )"
done
cmd="$cmd -type f -mtime -$date_range -ls"
echo $cmd

$cmd
Mark
  • 4,249
  • 1
  • 18
  • 27