5

I have the following script - its supposed to loop files names using find but it seams to break the files names up by a space? I need the file names to remain intact

#!/bin/bash

for file in `find -name "*.avi"`
do
./myscript $file
done

Can anyone help me?

Thanks

Rob
  • 215
  • 4
  • 10
  • possible duplicate of [Linux command to remove directories with white spaces](http://serverfault.com/questions/385474/linux-command-to-remove-directories-with-white-spaces) – womble Jul 08 '12 at 15:37
  • I updated the question to be more accurate of what I want to do – Rob Jul 08 '12 at 15:43
  • 1
    See [BashFAQ #020: How can I find and deal with file names containing newlines, spaces or both?](http://mywiki.wooledge.org/BashFAQ/020/). – Gordon Davisson Jul 08 '12 at 17:45

4 Answers4

13

Also: find -name "*.avi" -exec ./myscript '{}' \;

Womble's answer definitely works in the majority of cases, but Xargs isn't perfect with spaces across all implementations. I've never found find's 'exec' mode to break. Solaris was particularly cranky in that respect.

Obviously, the '-print' would be better for simply generating output for a list of files in a for loop. But if you're running a command on a list of files, it's more efficient to just run the '-exec' option.

Magellan
  • 4,451
  • 3
  • 30
  • 53
5
find -name "*.avi" -print0 | xargs -0 -n 1 echo

You don't have to echo in xargs:

find -name "*.avi" -print0 | xargs -0 -n 1 ./myscript
womble
  • 96,255
  • 29
  • 175
  • 230
  • This is another way to output the filename - I need to loop file names to another script (in the example above I replaced the command with echo) – Rob Jul 08 '12 at 15:38
  • So show us what you actually want to do then. We can't help you do what you want to do if you don't tell us what you want to do. – womble Jul 08 '12 at 15:38
3

If you don't need recursion:

#!/bin/bash

for file in *.avi
do
    ./myscript "$file"
done

Note that the quotes around the variable are essential.

Edit:

As kojiro points out, if you need recursion and you have Bash 4, you can add shopt -s globstar and change the glob to **/*.avi.

Dennis Williamson
  • 62,149
  • 16
  • 116
  • 151
  • If a file name has spaces, this method will break. If it has \n or other strange characters, it is also a security risk. Think of "/tmp/file\n/etc/shadow" – hayalci Jul 08 '12 at 19:54
  • 1
    @hayalci: No, you are *completely* wrong. It works perfectly with such filenames and without any security risks. Globbing with a `for` loop is the *correct* way to do this when recursion is not needed. – Dennis Williamson Jul 08 '12 at 20:24
  • If he does need recursion and has bash 4, he can just use `shopt -s globstar; for file in **/*.avi` – kojiro Jul 08 '12 at 22:13
  • @kojiro: Thanks, I should have mentioned that and I will add it to my answer. – Dennis Williamson Jul 08 '12 at 22:15
  • @DennisWilliamson: You are right, I confused the issue with "for i in \`ls *.avi\`" case – hayalci Jul 13 '12 at 13:33
1

In addition to the solutions based on xargs and the find -exec there is also the option of piping to while read. It has the benefit of allowing you to structure your script in a more classic loop manner. Let me apply it to your example.

find -name "*.avi" | while read avifile; do
  ./myscript "$avifile"
done
andol
  • 6,938
  • 29
  • 43
  • 3
    By default `read` will support escaping, so you need `-r`. And your solution will break with filenames containing newlines. – Pierre Carrier Jul 08 '12 at 16:03
  • @Pierre: I have so far never encounted a regular space situation where I have needed the -r parameter (to read?), but thanks for the pointer, I'll look into it. Agree that this solution would break on newline. Assumed the original questioner only dealt with the regular space character. When expecting arbitrary whitespace, or any other really crazy characters I agree on xargs -0 being the better choice. Well, either that or doing something more robust in a language such as Perl, Python, etc. – andol Jul 08 '12 at 16:12
  • 1
    `-r` is not needed for spaces, but in case a backslash appears in the filename. – Pierre Carrier Jul 08 '12 at 16:16