2

I'm starting bash scripting and i can't handle spaced filename. I searched other questions about my problem here, like:

how to output file names surrounded with quotes in SINGLE line?

In my script i need to take all directorys, end for each one doing some work. So the skeleton of my script is:

for directory in `find "$DIR" -type d`
do
# ... some work ...
done

This script, doesn't work with spaced filename. So i tried using find in combination with exec, that seems an elegant and easy solution, but nothing changed. More precisely i tried:

(I)  find "$DIR" -type d -exec echo -n '"{}" ' \;
(II) find "$DIR" -type d -exec echo -n '"{}" ' +

Before using the command -exec i read something about it, but something is still not clear to me, so:

  1. What's and how works '"{}" ' \;
  2. What's and how works '"{}" ' +
  3. How can i fix my script also for spaced filename?
Community
  • 1
  • 1
optimusfrenk
  • 1,271
  • 2
  • 16
  • 34
  • 2
    See previous answers [here](http://stackoverflow.com/questions/7039130/bash-iterate-over-list-of-files-with-spaces#7039579) and [here](http://stackoverflow.com/questions/1116992/capturing-output-of-find-print0-into-a-bash-array#1120952). – Gordon Davisson Apr 02 '13 at 00:11
  • The links @GordonDavisson provided contain THE answers to your problem. Just read an understand those. – Ed Morton Apr 02 '13 at 02:21

3 Answers3

2

What's and how works '"{}" ' \;

For each filename, this will basically execute echo -n "\"filename\" ", producing a bunch of lines like "filename".

Adding literal quotes doesn't help though, because bash doesn't start interpretting data as shell script code.

If you read foo; ls $foo, you can't just type in $(rm /) to run a command, and you can't just type in "foo bar" to avoid splitting a filename. Data is data, not code, and shell syntax isn't respected.

What's and how works '"{}" ' +

+ runs the command once for groups of filenames (for efficiency, instead of one at a time). Due to the way substitution works, it's identical to {} +, since the argument is just removed and replaced with a bunch of filenames.

How can i fix my script also for spaced filename?

You can make it work in 95% of cases by simply changing which characters bash uses for splitting, through the use of the IFS variable:

IFS=$'\n'
for dir in $(find "$DIR" -type d)
do 
   echo "Directory: >> $dir <<"
done

This makes bash split on just line feeds rather than the default of all whitespace (this can have some interesting side effects in the rest of your code, so you might want to set the value of IFS back to its original afterwards).

Filenames can contain line feeds too though. The best approach is to use:

while IFS= read -d '' -r dir
do
    echo "Directory: >> $dir <<"
done < <(find "$DIR" -type d -print0)

which makes find output files split by NUL bytes, which filenames can never contain.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • Will fail if the directory name starts with spaces and different version of echo will behave differently with different settings of $adir. Always use `IFS= read -r` unless you have a very specific reason not to and know exactly what the implications are. – Ed Morton Apr 02 '13 at 02:25
  • Thanks for answer all my subquestions! – optimusfrenk Apr 02 '13 at 16:44
1

This may work better for you:

while IFS= read -r adir ; do
    echo "Directory: $adir"
done < <(find "$DIR" -type d)
William
  • 4,787
  • 2
  • 15
  • 14
  • Will fail if the directory name starts with spaces or contains backslashes or newlines, and different version of echo will behave differently with different settings of $adir. Always use `IFS= read -r` unless you have a very specific reason not to and know exactly what the implications are. – Ed Morton Apr 02 '13 at 02:23
  • Good points, but allow me to say you work with rather messed up filenames (in 30+ years I've never encountered a newline in a file name - backspaces, sure - AppleDOS's secret filename hack, for example). Also, the echo is obviously a placeholder. – William Apr 02 '13 at 02:59
  • Unfortunately at work we use a CMS named SVN which routinely automatically creates backup files containing newlines in their names so it's something we deal with.... – Ed Morton Apr 02 '13 at 03:07
  • You have my sympathy - I've only had to deal with SVN recently, and not as an admin. (Perforce admin for 11 years, but its files are better behaved, generally.) – William Apr 02 '13 at 03:22
  • Thanks. We also get files with newlines in their names showing up in other situations, usually as a result of someone creating a file with a Windows tool. It is a nightmare. I'm not an admin, though, I'm a user and even I'm impacted by this disease... – Ed Morton Apr 02 '13 at 03:33
1

I use this format & it works fine:

find '/path/to/directories' -type d |\
  while read DIRNAME
  do
    echo "${DIRNAME}"
  done
Giacomo1968
  • 25,759
  • 11
  • 71
  • 103
  • Will fail if the directory name starts with spaces or contains backslashes or newlines, and different version of echo will behave differently with different settings of $adir. Always use `IFS= read -r` unless you have a very specific reason not to and know exactly what the implications are. – Ed Morton Apr 02 '13 at 02:24