Jason
Jason

Reputation: 11363

Shell script to traverse directories

I'm working on a project that requires batch processing of a large number of image files. To make things easier, I've written a script that will create n directories and move m files to them based on user input.

My issue is to now understand directory traversal via shell script.

I've added this snippet at the end of the sort script described above

dirlist=$(find $1 -mindepth 1 -maxdepth 1 -type d)

for dir in $dirlist
do
  cd $dir
  echo $dir
  ls
done

When I ran it inside a Pano2 folder, whcih contains two inner folders, I always got an error

./dirTravel: line 9: cd: Pano2/05-15-2012-2: No such file or directory

However, after that, I get the file listing from specified directory.

What is the reason behind the warning? If I add cd ../ after ls I get the listing of the folders inside Pano2/, but not the files themselves.

Upvotes: 5

Views: 28000

Answers (3)

jordanm
jordanm

Reputation: 34924

There really isn't any reason to cd to the directory to run the ls command. The following should work:

find "$1" -mindepth 1 -maxdepth 1 -type d -exec ls {} \;

If that was just an example, and you really run other commands that depend on the working directory, you can use bash -c with -exec:

find "$1" -mindepth 1 -maxdepth 1 -type d -exec bash -c 'cd "$1"; echo "$1"; ls' -- {} \;

The bash -c call spawns a subshell, so you don't need to worry about it changing the directory of the current shell.

Upvotes: 2

Jonathan Leffler
Jonathan Leffler

Reputation: 753535

I recommend using a sub-shell to switch directories:

dirlist=$(find $1 -mindepth 1 -maxdepth 1 -type d)

for dir in $dirlist
do
  (
  cd $dir
  echo $dir
  ls
  )
done

The code inside the sub-shell won't affect the parent shell, so it can change directory anywhere without giving you any issues about 'how to get back'.

This comes with the standard caveat that the whole process is fraught if your directory names can contain spaces, newlines or other unexpected and awkward characters. If you keep with the portable filename character set ([-_.A-Za-z0-9]) you won't run into problems as written.

Upvotes: 6

Nate Kohl
Nate Kohl

Reputation: 35914

My guess is that you're going in two levels deep, but only coming back up one level. Try adding a cd ../.. after the ls, or use pushd and popd instead.

For example:

for dir in $dirlist
do
  pushd $dir
  echo $dir
  ls
  popd
done

As @shellter points out, if those directories have spaces, then something like this might work better:

find $1 -mindepth 1 -maxdepth 1 -type d | while read -r dir
do
  pushd "$dir"  # note the quotes, which encapsulate whitespace
  echo $dir
  ls
  popd
done

Upvotes: 4

Related Questions