0

I need to make a for loop that loops for every item in a directory.

My issue is the for loop is not acting as I would expect it to.

  cd $1

  local leader=$2
  if [[ $dOpt = 0 ]]
  then
        local items=$(ls)
        local nitems=$(ls |grep -c ^)
  else
        local items=$(ls -l | egrep '^d' | awk '{print $9}')
        local nitems=$(ls -l | egrep '^d' | grep -c ^)
  fi

  for item in $items;
  do
     printf "${CYAN}$nitems\n${NONE}"
     let nitems--
     if [[ $nitems -lt 0 ]]
     then
          exit 4
     fi
     printf "${YELLOW}$item\n${NONE}"
  done

dOpt is just a switch for a script option.

The issue I'm having is the nitems count doesn't decrease at all, it's as if the for loop is only going in once. Is there something I'm missing?

Thanks

7
  • Try quoting your... let "nitems--", and also making items and nitems not local. Commented Apr 22, 2014 at 22:56
  • @MarkSetchell That would be a syntax error, unnecessarily. Commented Apr 22, 2014 at 22:58
  • I do not see the point in grep -c ^. Try wc -l instead. Do you have filenames containing characters in $IFS? In general, use … | while read -r …; do …; done here instead. And try the built-in bash debugger. Commented Apr 22, 2014 at 23:05
  • grep -c ^ will count all the items in the directory, wc -l doesn't change anything. Also I changed $IFS to ":" at the beginning of the script. But thanks, I'll try making it a while loop instead of for loop. Commented Apr 22, 2014 at 23:09
  • I know what grep -c does; wc -l changes something in that it is more to the point, and probably more efficient as it does not need a regular expression engine. Looking forward to your results. Commented Apr 22, 2014 at 23:15

3 Answers 3

3

Goodness gracious, don't rely on ls to iterate over files.
local is only useful in functions.
Use filename expansion patterns to store the filenames in an array.

  cd "$1"
  leader=$2             # where do you use this?

  if [[ $dOpt = 0 ]]
  then
      items=( * )
  else
      items=( */ )       # the trailing slash limits the results to directories
  fi
  nitems=${#items[@]}

  for item in "${items[@]}"     # ensure the quotes are present here
  do
      printf "${CYAN}$((nitems--))\n${NONE}"
      printf "${YELLOW}$item\n${NONE}"
  done

Using this technique will safely handle files with spaces, even newlines, in the name.

Sign up to request clarification or add additional context in comments.

4 Comments

+1; shopt -s nullglob prior to globbing would deal with the case where no files/subfolders exist in the target folder.
Thanks a lot, very clean and elegant solution :)
Well, spaces will be handled as well in my script if you add IFS=$'\n' before the ls commands.
@NotANumber, not with awk '{print $9}'
0

Try this:

if [ "$dOpt" == "0" ]; then 
    list=(`ls`)
else
    list=(`ls -l | egrep '^d' | awk '{print $9}'`)
fi

for item in `echo $list`; do
    ... # do something with item
done

3 Comments

This just does not work. It would iterate over what the command outputs that $mycmd expands to.
That didn't work. I don't understand what you're trying to do with that. you just changed the variable name when getting the items, but not in the for loop?
You are right... the command is wrong. I corrected it. Try now again please.
0

Thanks for all the suggestions. I found out the problem was changing $IFS to ":". While I meant for this to avoid problems with whitespaces in the filename, it just complicated things.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.