0

I have a script calling this

find "/mnt/Data/Shared/$1" -type d -exec bash -c 'fixperm "'${1}'" "fd" "$0"' {} \;

$1 is a directory, when the name contains no spaces it works, when there is a space its fails an returns errors. Testing with "00_Office Test"

Test" "fd" "$0": -c: line 0: unexpected EOF while looking for matching `"'
Test" "fd" "$0": -c: line 1: syntax error: unexpected end of file

I thought it may be because the path was missing the \ before the space but that does not fix the issue. I'm sure I'm missing something trivial here.

Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
SBTech
  • 19
  • 3

2 Answers2

3

That's because the ${1} appears outside of single quotes, i.e. it gets expanded and word-split by the shell before find even sees it. The syntax highlighting here on StackExchange shows it clearly. Use double quotes to prevent the word splitting.

Also, by calling bash -c, you need to handle quoting yourself, but it can break if the file name contains a double quote. Don't use it and pass the parameters directly:

find "/mnt/Data/Shared/$1" -type d -exec fixperm "$1" fd {} \;
choroba
  • 45,735
  • 7
  • 84
  • 110
  • I think the `$0` would be the parameter that gets passed to `bash -c 'cmd'` -- `-exec fixperm "$1" fd {} \;` might be better. – glenn jackman Jul 17 '19 at 15:57
  • @glennjackman: You're right, fixed. I felt it was weird. But not having `fixperm` and having no idea what the expected behaviour was, I couldn't test it. – choroba Jul 17 '19 at 16:01
  • fixperm is a sub routine being exported and this method may be workable but the other answer solved with minimal change and this did not work because bash did not know about fixperm – SBTech Jul 17 '19 at 19:22
  • 1
    @SBTech, the probability is that `fixperm` is just a `chmod` command of some sort, wrapped in a Bash function. If so, you might as well just put it as `-exec chmod ...` rather than using a function. – Wildcard Jul 18 '19 at 20:52
1

That's because you are using an unquoted ${1}.

Quoting

Solving quoting issues could get quite complex.

Replacing '${1}' with '"${1}"' might seem to help.

Compare:

$ set -- "ab cd"; bash -c 'printf "<%s> " '${1}' "fd" "$0"'
<ab>

with:

$ set -- "ab cd"; bash -c 'printf "<%s> " '"${1}"' "fd" "$0"'
<ab> <cd> <fd> <bash>

However, shell "quote removal" is still applied to the variable value.
As a workaround you could use '"${1@Q}"'

$ set -- 'a"b c"d'; bash -c 'printf "<%s> " '"${1}"' "fd" "$0"'; echo
<ab cd> <fd> <bash>               # quotes got lost.

$ set -- 'a"bc"d'; bash -c 'printf "<%s> " '"${1@Q}"' "fd" "$0"'; echo
<a"b c"d> <fd> <bash>             # correct quotes.

But, still, that doesn't work for the two loops of shell exposure that your command has (first to the find command, then to the bash -c command):

$ mkdir 'a"bc"d' 'a"b c"d' 'a"bcd'

$ set -- 'a"bc"d'; find "./$1" -type d -exec bash -c 'printf "<%s> " fixperm "'"${1}"'" "fd" "$0"' {} >
<fixperm> <abcd> <fd> <./a"b c"d>

$ set -- 'a"b c"d'; find "./$1" -type d -exec bash -c 'printf "<%s> " fixperm "'"${1}"'" "fd" "$0"' {} >
<fixperm> <ab> <cd> <fd> <./a"b c"d>

$ set -- 'a"bcd'; find "./$1" -type d -exec bash -c 'printf "<%s> " fixperm "'"${1}"'" "fd" "$0"' {} \; 
./a"bcd: -c: line 0: unexpected EOF while looking for matching `"'
./a"bcd: -c: line 1: syntax error: unexpected end of file

Correct

However, what really happens is that there seems to be a confusion between the $1 that is a parameter of the script you call and what $1 means to the shell that is being called with bash -c

The line:

find "/mnt/Data/Shared/$1" -type d -exec bash -c '
     fixperm "'"${1}"'" "fd" "$0"' {} \;

Should read:

find "/mnt/Data/Shared/$1" -type d -exec bash -c '
     fixperm "$1" "fd" "$2"' bash-shell "$1" {} \;

Which makes the quoting direct and a lot more robust.

Simple

If there is no loop or other complex function to run inside the bash -c script, almost all quoting could be removed and write:

dir="/mnt/Data/Shared"

find "$dir/$1" -type d -exec fixperm "$1" fd {} \;
  • its ugly but it works! `find "/mnt/Data/Shared/$1" -type d -exec bash -c 'fixperm "'"$1"'" "fd" "$0"' {} \;` – SBTech Jul 17 '19 at 19:19
  • Does is work for filenames containing double quotes? – choroba Jul 17 '19 at 19:34
  • @choroba As long as the variable (`$1` in this case) contains the quotes I can not find a way to make it fail. Care to show an example where it fails? –  Jul 17 '19 at 21:13
  • In the `set` example, use `set -- 'a"b"c'`, the double quotes will be lost; using `'a"b'` makes it fail. – choroba Jul 17 '19 at 23:48
  • @choroba Thanks for your comments. Answer corrected. I believe that now it is correct. –  Jul 18 '19 at 22:21