5

I am trying to feed Directory names into a for loop. My code is as follows:

td='/Test/TopDir/'
cd "$td" 
for x in $(ls -d */ | cut -f1 -d'/'); do
  echo x=$x
done

The top directory I run this on looks like this when running an ls command:

ls -l
drwxrwxrwx    4 Jason    users         4096 May  6 06:36 2014-02-02 - Jebby (
drwxrwxrwx    3 Jason    users         4096 May  6 06:09 2014-02-04 - Jebby (
drwxrwxrwx    2 root     root          4096 May  6 06:09 @eaDir
-rw-r--r--    1 Jason    users      3956225 Jan 26 10:17 DSC01062.JPG
-rw-r--r--    1 Jason    users      3927603 Jan 26 10:18 DSC01063.JPG

The results of my for loop is as follows:

x=2014-02-02
x=-
x=Jebby
x=(
x=2014-02-04
x=-
x=Jebby
x=(
x=@eaDir

As you can see the for loop is breaking the directory names into sub-pieces after each space. In this example I only want the For Loop to execute three time with the three directories:

  1. 2014-02-02 - Jebby (
  2. 2014-02-04 - Jebby (
  3. @eaDir

What am I doing wrong?

Wags
  • 317

2 Answers2

16

Avoid parsing the output (or at least the filename portion) of ls in shell scripts if at all possible. It will always give issues with word-splitting where filenames contain whitespace.

If you want to iterate over directories you can do that using a simple shell glob i.e.

for d in */; do 
  echo "$d"
done

The ls command should only be used for displaying directory listings in human-readable form in the terminal.

steeldriver
  • 81,074
  • You should also explain how to check if a file is a directory. – Kevin May 10 '14 at 14:48
  • It's a directory just by virtue of matching the */ glob, I think? – steeldriver May 10 '14 at 14:51
  • @Kevin */ only matches directories (and symlinks to directories). – Gilles 'SO- stop being evil' May 10 '14 at 16:45
  • This is bad advice. It will have issues as long as you don't handle them. They can be handled. And besides, set -- */ is more useful than for ... in ... – mikeserv May 11 '14 at 01:31
  • @mikeserv what issues? How would you use set -- */ here? – terdon May 11 '14 at 02:47
  • @terdon - just like that. And i meant ls - you can use it. It bothers me when people rule out a tool completely because somebody blogs about how it was inconvenient to use in a certain scenario. It just feels a little... sheepy. – mikeserv May 11 '14 at 03:17
  • @mikeserv in this particular case, all problems go away if you don't use ls. The whole point here is that there are spaces in the file names and avoiding it is a good habit to get into. Anyway, seriously, I'd be interested to see what you mean with set -- */. I don't really know what it does and have no idea how it could be useful here. Why not post an answer with it? – terdon May 11 '14 at 03:20
  • @terdon I dont want to post an answer is why not. And set -- */ will set your positional parameter array to all directories in the current directory - so you now have a shell array of the data you wish to work with. So $# becomes a count of all of the folders and all of the rest of those useful array features. As for ls - spaces are as easily handled as changing $IFS. – mikeserv May 11 '14 at 03:25
2

Using @mikserv's suggestion, you can do

cd /Test/TopDir/
set -- "$PWD" */
cd /somewhere/else
td=$1 ; shift 
for x; do
  echo x="${td}/$x"
done
terdon
  • 242,166
  • No. You dont need for ... in ... if its already in your $@array you can just do for ... - personally i like while ${1+:} false ; do though. – mikeserv May 11 '14 at 03:47
  • @mikeserv thanks, edited. I made this CW, so feel even freer than usual to add any other improvements. I just thought it was a good idea and should be here. – terdon May 11 '14 at 03:51
  • Whats a CW - what does that mean? – mikeserv May 11 '14 at 03:53
  • @mikeserv Community wiki, also see here. In short it's used to make posts into collaborative efforts that the community edits. An often useful side effect is that the author gets no rep from it. – terdon May 11 '14 at 03:56
  • There - maybe its a little clearer now why i said it was more useful. I guess you suckered me into doing the answer after all. – mikeserv May 11 '14 at 04:11
  • @mikeserv heh, I knew you'd fall into the trap :). Thanks! – terdon May 11 '14 at 04:11