[Gllug] spaces and shell scripting

Peter Grandi pg_gllug at gllug.for.sabi.co.UK
Thu Sep 1 14:45:04 UTC 2005


>>> On Thu, 01 Sep 2005 14:43:50 +0100, Jon Dye <jon at pecorous.co.uk>
>>> said:

jon> Hi, I'm trying to write a shell (bash) script [ ... ]

jon> My problem is spaces in filenames and how to deal with them
jon> when I have a list of file names in a variable.

The usual ''solution'' is to ignore the issue. :-( Indeed most
''professional'' shell script out there are simply awful; not
many understand even the basics of shell scripting, and your
script is not worse than the average.

jon> I got the script to work by running find multiple times and
jon> not using variables, e.g. [ ... script ... ]

Using a two pass approach one could have written instead (note
the differences with yours):

  #!/bin/sh

  ME="`basename \"$0\"`"

  doNoElements()	{ echo "$ME: no elements found"; }
  doSingleElement()	{ echo "$ME: single element '$1'"; }
  doOneOfManyElements()	{ echo "$ME: one of many elements '$1'"; }

  C="`find ${1+\"$@\"} -type f -print | wc -l`"

  case "$C" in
  '0') doNoElements;;
  '1')
    : '1 file, scan again to get the name of that file'
    find ${1+"$@"} -type f -print | while read FILE
    do doSingleElement "$FILE"
    done;;
  *)
    : 'N files, scan again to get the list'
    find ${1+"$@"} -type f -print | while read FILE
    do doOneOfManyElements "$FILE"
    done;;
  esac

It may be more efficient to usr '-print0 | xargs -0 ...' if the
processing is done by a single command that can take multiple
file arguments.

So, the above still requires two passes; this can be avoided
with some effort: the idea is to keep a count of file names
seen, and to process not the current file name, but the previous
one, as this allows a 1-degree of lookahead, and to special case
the cases where the unknown-length stream of file names is 0
tokens or 1 token long:

  #!/bin/sh

  ME="`basename \"$0\"`"

  doNoElements()	{ echo "$ME: no elements found"; }
  doSingleElement()	{ echo "$ME: single element '$1'"; }
  doOneOfManyElements()	{ echo "$ME: one of many elements '$1'"; }

  N='0'; PREV=''

  : 'We ensure the last element is a terminating empty string.'

  { find ${1+"$@"} -type f -print; echo ''; } | while read FILE
  do
    case "$FILE" in
    ?*)
      : 'Non empty element, check if it is the 0th, the 1st, or
	 a subsequent one.'
      case "$N" in
      '0') : 'Do precisely nothing; this is the first element';;
      *)
	: 'If the count it not 0, this must be the second or
	   larger element of the stream, process the previous
	   one.'

	 doOneOfManyElements "$PREV";;
      esac

      PREV="$FILE"
      N="`expr \"$N\" + 1`";;

    '')
      : 'Empty terminator: if "$N" is 0, no files were found;
	 if 1, one file was found and has to be done;
	 if > 1 then the last file of many has to be done.'

      case "$N" in
      '0') doNoElements;;
      '1') doSingleElement "$PREV";;
      *)   doOneOfManyElements "$PREV";;
      esac;;
    esac
  done

OK, that should be all. To check:

  mkdir /tmp/{empty,single,many}
  touch /tmp/single/a /tmp/many/{a,b,c}

and run either script with the obvious argument.

Note: the above are quick'n'dirty lightly tested stuff...

-- 
Gllug mailing list  -  Gllug at gllug.org.uk
http://lists.gllug.org.uk/mailman/listinfo/gllug




More information about the GLLUG mailing list