Take the 2-minute tour ×

I want to enter every directory retuned by ls command and execute script.

I tried this (and many other things), but it just does not work

ls  | awk '{print $1" && pwd"}' | xargs cd

How to do it without for loop?

share|improve this question
Why specifically ls and not a for-loop? The for-loop would be the easier approach, and in addition it won't break on directories containing whitespace or quotes, like you'll get problems with when parsing ls output. –  geirha Nov 9 '11 at 15:57
add comment

5 Answers

up vote 2 down vote accepted

If you can, use find as suggested in other answers, xargs is almost always to avoid.

But if you still want to use xargs, a possible alternative is the following:

printf '"%s"\n' */ | xargs -L 1 bash -c 'cd "$1" && pwd' _

Some notes:

  1. */ expands to the list of directories in the current folder, thanks to the trailing slash

  2. printf with \n separates the elements one for each line, and surround them with double quotes, thanks to the formatting string containing double quotes "%s"

  3. the option -L 1 to xargs makes it to execute once for every input line

  4. bash removes the double quotes and pass it to the inline script as a single parameter, but cd should put double quotes again to interpret it as a single string

  5. to avoid the strange use of $0 as a parameter, is usual to put a first dummy argument _

share|improve this answer
I knew echo * but was not aware of */. And putting an underscore as the 1st argument is a neat trick. –  lgarzo Nov 9 '11 at 15:59
thx a lot, I'm learning bash and this is very helpful. –  UAdapter Nov 10 '11 at 9:40
add comment
find . -type d -exec bash -c "cd '{}' && pwd" \;

Swap pwd for your script. And . for the root directory name, if it's not the "current" directory.

You have to wrap the exec clause in bash -c "..." because of the way -exec works. cd doesn't exist in its limited environment, but as you can see by running the above command, when you bring in bash to do the job, everything runs as predicted.

share|improve this answer
The reason cd alone do not work is because it is a shell builtin, nothing to do with the environment. –  enzotib Nov 9 '11 at 15:07
Nice work Oli. I added maxdepth here as mentioned before, also since it seems to imply current subdirectories i removed .: find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \; –  Todd Partridge 'Gen2ly' May 29 '12 at 16:59
add comment

The find command

The find command is pretty handy. It can generate a list of files or directorise in or below a specified directory, and it can also execute a command on each item in that list of results. (Note: if you're using a multi-user system, make sure to read the security considerations section of the Findutils documentation for the find command.)

The options to the find command that we need are -type d which tells find to return the names of every directory within the specified path, and -exec command '{}' \; which tells find to run command on the results. For example:

find /media/music/flac -type d -exec ~/tag-flac-with-rg.sh '{}' \;

This tells find to return every directory in or below the path /media/music/flac and then run the tag-flac-with-rg.sh script on each directory found. The '{}' is replaced by each directory name found, and gets passed as an argument to the tag-flac-with-rg.sh script. The \; marks the end of the arguments list to the tag-flac-with-rg.sh script. may be this will help u

share|improve this answer
No need to quote {}, and a -maxdepth 1 could be useful, if the OP do not want to recurse into subdirectories. –  enzotib Nov 9 '11 at 15:11
@enzotib I did not know about the -maxdepth 1, thx –  UAdapter Nov 9 '11 at 15:42
add comment

Just for curiosity I tried to take your example and make it work. There is no reason to use this technique instead of the find command.

Note: Thanks to enzotib for pointing it out, this does not work for directory names containing spaces or any other characters that make awk think the name consists of several fields.

To make your example work a few tweaks should be made. But at first see the code:

ls -l | grep ^d | awk '{print $NF}' | xargs -n 1 bash -c 'cd $0; ls'

The first two commands make sure that only directories are listed. Since ls long listing contains a d as the first character for directories, the grep command selects only these lines.

With awk only the last column is displayed in every line. NF is a built-in variable in awk holding the maximum number of fields. Thus printing $NF displays the last field.

Finally xargs needed to be tamed to run the specified script with only one parameters at a time. (I know this kind-of defeats its purpose.) Otherwise the script could not be run for every directory without a for loop.

And to work around the built-in cd barrier, we call bash with -c. Also note that $0 becomes the first parameter instead of $1.

share|improve this answer
Unfortunately, if you have directories names containing spaces, $NF gives only the last word of the name. This is one of the reason why people is suggested to not parse ls output. –  enzotib Nov 9 '11 at 15:15
This is a valid point and I do not see an easy workaround without creating a lengthy awk script. I was trying to demonstrate how the original thinking could work (and to convince the OP not to use it, because it is at least not elegant). Thank you for pointing it out, I'll update the answer. –  lgarzo Nov 9 '11 at 15:23
See my answer working with xargs. –  enzotib Nov 9 '11 at 15:43
add comment

Maybe take a look at using find with -exec?

share|improve this answer
add comment

Your Answer


