2

I'm trying to write a shell script, and to make it more or less readable, i decided to bring a part of my commands into environment variables:

#!/bin/bash
# Variables

name_expression="-type d \( -name .folder1 -o -name .wildcardfolder* -o -name .folder2 \)"

# Commands

find /root/ -maxdepth 1 "$name_expression" -execdir rm -rf {} \;
find /home/ -maxdepth 2 "$name_expression" -execdir rm -rf {} \;

The problem is that if i try to run this shell, many symbols are wrapped with quotes:

bash -x ./my_shell.sh 
+ name_expression='-type d \( -name .android -o -name .AndroidStudio* -o -name .gradle \)'
+ find /home/ /root/ -maxdepth 2 '-type d \( -name .folder1 -o -name .wildcardfolder* -o -name .folder2 \)' -execdir rm -rf '{}' ';'
find: unknown predicate `-type d \( -name .folder1 -o -name .wildcardfolder* -o -name .folder2 \)'

Is there any way to pass this variable without being modified?

Rui F Ribeiro
  • 55,929
  • 26
  • 146
  • 227
  • 1
    See [Why does my shell script choke on whitespace or other special characters?](http://unix.stackexchange.com/q/131766/135943) This is one of the very few cases where you *shouldn't* use double quotes when calling the variable. But using the `*` in there is going to be dangerous. – Wildcard Sep 06 '17 at 21:50
  • @Wildcard Actually, this is one of the common cases where you should use double quotes (because of the wildcards, among other reasons), but you can't store a *list of strings* in a string variable. – Gilles 'SO- stop being evil' Sep 06 '17 at 22:49
  • @Gilles, that's what I was trying to convey. See my answer and comments below. – Wildcard Sep 06 '17 at 22:54

2 Answers2

3
name_expression=( -type d \( -name .folder1 -o -name .wildcardfolder\* -o -name .folder2 \) )

find /root/ -maxdepth 1 "${name_expression[@]}" -execdir rm -rf {} \;

Instead of a string parameter which would have to be unquoted in order for the command to see several parts, this uses an array parameter. That way both the splitting and the quoting can be controlled (not completely i.e. per part but in a suitable way for this task).

An alternative which allows complete control would be the use of eval but that would make the rest of the command line more complicated.

Hauke Laging
  • 88,146
  • 18
  • 125
  • 174
  • The backslash in `.wildcardfolder\*` doesn't do what you think it does. The glob can still be expanded (to files with a backslash in the name) and the `-name` primary won't match the way it was intended. – Wildcard Sep 06 '17 at 21:54
  • @Wildcard I have not found an explanation for it but bash 4.4.12 does **not** expand `touch 'bar\_' ; testvar="bar\*" ; echo $testvar bar\\*`. Does not remove the `\\` either. Maybe a shell bug. – Hauke Laging Sep 06 '17 at 22:45
  • Interesting. You're right, it doesn't expand. However, it *does* get treated as an escape by `find` so that `find` will only match a literal `*` character for that position. So this command still won't work. – Wildcard Sep 06 '17 at 23:00
  • While I personally agree this is the proper way to address the issue, some explanation would be good so people understand what this is doing. – phemmer Sep 06 '17 at 23:58
  • (Note: the comments above refer to the earlier revision of this answer, but they contain interesting relevant technical data so shouldn't be deleted IMO.) – Wildcard Sep 07 '17 at 21:33
2

A modified version that avoids potentially hazardous glob expansions:

cleanupdir() {
  # args should be dirname, maxdepth
  find "$1" -maxdepth "$2" -type d \( -name .folder1 -o -name '.wildcardfolder*' -o -name .folder2 \) -execdir rm -rf {} \;
}

cleanupdir /root 1
cleanupdir /home 2
Wildcard
  • 35,316
  • 26
  • 130
  • 258