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.

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 at 19:19
  • Does is work for filenames containing double quotes? – choroba Jul 17 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? – Isaac Jul 17 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 at 23:48
  • @choroba Thanks for your comments. Answer corrected. I believe that now it is correct. – Isaac Jul 18 at 22:21
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 {} \;
  • 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 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 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 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 at 20:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.