3

I'm trying to piece together a script that recursively finds all the *.plist files in a directory and then operates on them. The script is below, but its producing unexpected results.

The expected result is each filename is printed like:

File: XXX
File: YYY
File: ZZZ

The problem is the for file in "$FILES" is not sending one file at a time. Rather, its sending in the whole list at once (FILES was gathered using find), so I'm getting output like:

File: XXX
YYY
ZZZ

The real output is shown below, but the above is an accurate digest of it.

How do I make the shell loop over the individual files in the variable FILES?


#! /bin/sh

do_test_file() {
  FILE="$1"
  echo "File: $FILE"
  # echo `ls -al "$FILE"`
  echo ""
}

do_test_dir() {
  DIR="$1"
  FILES=`find "$DIR" -name "*.plist" 2>/dev/null`

  for file in "$FILES"
  do

    if [ ! -d "$file" ] && [ ! -L "$file" ]; then
      do_test_file "$file"
    fi

  done
}

########################################

ARG="$1"
if [ -z "$ARG" ]; then
  echo "Please invoke with a file or directory to search"
  exit 1
fi

if [ -d "$ARG" ]; then
  do_test_dir "$ARG"
else
  do_test_file "$ARG"
fi

$ ./plist-dump.sh .

File: ./916CD34C-4D41-41B7-9266-2DCAC416E2D1/.com.apple.mobile_container_manager.metadata.plist
./916CD34C-4D41-41B7-9266-2DCAC416E2D1/iTunesMetadata.plist
./916CD34C-4D41-41B7-9266-2DCAC416E2D1/example.app/ResourceRules.plist
./916CD34C-4D41-41B7-9266-2DCAC416E2D1/example.app/Info.plist
./916CD34C-4D41-41B7-9266-2DCAC416E2D1/example.app/Settings.bundle/Root.plist
./916CD34C-4D41-41B7-9266-2DCAC416E2D1/example.app/SC_Info/Manifest.plist
  • You created variable FILES with single value - the whole output from find. You should use arrays instead like FILES=($(find ...)) or rearrange the script completely. – jimmij Feb 6 '15 at 0:51
  • @jimmij - Maybe I should back up (I'm not a shell expert). How do I tell the shell the newline is a delimiter for for each since that's what find uses? – user56041 Feb 6 '15 at 0:53
  • 2
    This is the wrong approach entirely. Use -exec. – jordanm Feb 6 '15 at 1:03
6

You are probably looking for the -exec function in find itself:

find "${DIR}" -name "*.plist" -type f -exec echo "do some stuff to " {} \;

Breaking that down:

  • -type tests to ensure that the thing it found is a file
  • -exec executes the code....
  • echo "do some stuff "
  • {} represents the file being operated on during this iteration
  • \; closes the code statement and returns for next iteration

This might simplify your script.

  • I thought that do_test_file bit must be a separate script at first and only just now noticed the shell function definition at top. It's what confused me about the echo here. Still, this could be a whole lot faster (and protect it from possible breakage related to echo) = like... find ... -exec printf 'File: %s\n' {} + – mikeserv Feb 6 '15 at 4:30
3

You created variable FILES with single value - the whole output from find. One solution is to use arrays instead, like:

FILES=($(find . -name "*.txt" 2>/dev/null))

and later on

for file in "${FILES[@]}"

Note that you should rather use $() for command substitution instead of backticks ``.

  • 2
    This breaks on any files containing spaces or newlines in the name. – jordanm Feb 6 '15 at 1:03
  • I had to change from /bin/sh to /bin/bash. Otherwise, Syntax error: "(" unexpected (expecting "}"). – user56041 Feb 6 '15 at 19:09

Your Answer

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