[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: For Loops and Space in Names

On 10Dec2008 15:05, Robert Wuest <rwuestfc wuest org> wrote:
| Spaces have always been one of my pet peeves and I find this discussion
| rather interesting.  Spaces don't belong in filenames and they make
| script writing a pain.  I'm going to include a script I wrote a long
| time ago to handle the problem of junk characters in music files.  Even
| buying music online from Amazon will get you spaces in filenames :(
| I remember messing with this little script quite a bit before it worked.
| One lesson I learned writing this was to use lot's of double quotes.
| The script replaces spaces (and other things) in filenames.  I just put
| it up here as an example since it's pretty short.  I seem to use it a
| lot.  
| The main for loop is:
|     for i in *; do 
|         fixname "$i"
|     done
| Notice the quotes around "$i".  They're important.
| function fixname() 
| {
|     newname=`echo $1 | \

Because the $1 is unquoted here you probably don't need the "+" in the
regexps. But you probably should quote $1 because of this:

  [/Users/cameron]fleet*> x='t*'
  [/Users/cameron]fleet*> echo "$x"
  [/Users/cameron]fleet*> echo $x
  test.txt them tmp

i.e. if $x is not quoted then glob characters in a filename will get expanded
(supposing you have a name like "t*", and some other files starting with "t").

|         sed -r \
|             -e "s/[ ]+/_/g" \
|             -e "s/[_]+/_/g" \
|             -e "s/'//g" \
|             -e "s/[+]//g" \
|             -e "s/,//g" \
|             -e "s/_-_/-/g" \
|             -e "s/\&/and/g"`;
|     # only do rename if the name is changed
|     if [ \"$1\" != \"${newname}\" ]; then

You don't need to backslash the "s in this comparison.

|         echo mv \"$1\" \"${newname}\"

This is actually dangerous. By leaving the (expanded) $1 in double
quotes, it is subject to parameter and command substitution in the
shell that runs the commands you generate. For example, supposing I ship
you a file whole name contains:

  `rm -rf $HOME`

That _will_ get through your script, and _will_ run the rm command in
the subshell. In both the $1 and $newname parts.

But it gets better. Even though you remove all single quotes form
$newname, and are thus safe to emit:

  mv ..... '$newname'

the $1 is still unsafe, and cannot easily be made safe. (Actually,
bash's "printf %q" formatter may do the trick for you, and since you're
already in nonportable GNU sed land, you may as well step right in with
nonportable bash too:-)

Personally, I would change the script mode. Instead of trying to emit
safe shell syntax for piping to "sh", I would instead give it two modes
of operation. Default is to recite commands and a -x option would make
it actually move files around.

Like this:

  { ( set -x

  # handle leading -x option
  if [ "x$1" = x-x ]

and then down where you emit the mv commands go:

  $trace mv "$1" "$newname"

which should be robust against weird names in $1 or $newname.

BTW, have you worried about having mv move a "bad" filename onto an
already existing "good" name? A "mv -i" might avoid a little pain.

|     else
|         echo "# \"$1\" and \"${newname}\" are the same file"
|     fi
| }
| if [ "$#" == "0" ]; then

No quotes needed here, and "==" is not a test notation. You want "="
(string comparison) or "-eq" (numeric comparison).

|     for i in *; do 
|         fixname "$i"
|     done
| else
|     while [ "z$1" != "z" ]; do
|         fixname "$1"
|         shift
|     done

This loop is better written:

  for i; do
    fixname "$i"

otherwise running your script like this:

  your-script a b c "" d e f

will only operate on the first 3 names. Besides, the "for i; do" notation
is shorter and clearer (once you know that leaving off the "in" clause
iterates over the command line arguments). And all to frequently you
see this in scripts:

  for i in $*; do

which has the usual "spaces in words" trouble we're all working around

Personally, I strive to be robust against weird names in my code, to
avoid having to play rename games like yours. I do have renaming scripts
like your for much the same reason you do, though.

Cameron Simpson <cs zip com au> DoD#743

Lady, have you ever seen a cat skeleton in a tree?      - Kevin Dunn

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]