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
  • 67,283
  • 35
  • 116
  • 255
SBTech
  • 19

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
  • 47,233
  • 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