How to enter every directory in current path and execute script
Solution 1
If you can, use find
as suggested in other answers, xargs
is almost always to be avoided.
But if you still want to use xargs
, a possible alternative is the following:
printf '%s\0' */ | xargs -0 -L1 bash -c 'cd -- "$1" && pwd' _
Some notes:
*/
expands to the list of directories in the current folder, thanks to the trailing slashprintf
with\0
(null byte) separates the elements one for each linethe option
-L1
toxargs
makes it to execute once for every input line and the option-0
makes it separate the input on the null byte: filenames can contain any character, the command doesn't break!bash
removes the double quotes and pass it to the inline script as a single parameter, butcd
should put double quotes again to interpret it as a single string; using--
makes thecd
command robust against filenames that start with a hyphento avoid the strange use of
$0
as a parameter, is usual to put a first dummy argument_
Solution 2
find . -type d -exec bash -c 'cd "$0" && 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.
Edit: I'm not sure why 2011 Oli didn't think of -execdir
but that's probably a faster solution:
find . -type d -execdir pwd \;
Solution 3
You shouldn't parse ls
in the first place.
You can use GNU find
for that and its -execdir
parameter, e.g.:
find . -type d -execdir realpath "{}" ';'
or more practical example:
find . -name .git -type d -execdir git pull -v ';'
and here is version with xargs
:
find . -type d -print0 | xargs -0 -I% sh -c 'cd "%" && pwd && echo Do stuff'
For more examples, see: How to go to each directory and execute a command? at SO
Solution 4
Other alternative:
for directory in *
do
(cd $directory && awk '{print $1" && pwd"}')
done
Solution 5
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
.
UAdapter
Updated on September 18, 2022Comments
-
UAdapter over 1 year
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?
-
geirha over 12 yearsWhy 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 parsingls
output. -
kenorb over 9 yearsSee also: How to go to each directory and execute a command? at stackoverflow.
-
-
enzotib over 12 yearsThe reason
cd
alone do not work is because it is a shell builtin, nothing to do with the environment. -
enzotib over 12 yearsNo need to quote
{}
, and a-maxdepth 1
could be useful, if the OP do not want to recurse into subdirectories. -
enzotib over 12 yearsUnfortunately, 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 parsels
output. -
lgarzo over 12 yearsThis 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. -
UAdapter over 12 years@enzotib I did not know about the -maxdepth 1, thx
-
enzotib over 12 yearsSee my answer working with
xargs
. -
lgarzo over 12 yearsI knew
echo *
but was not aware of*/
. And putting an underscore as the 1st argument is a neat trick. -
UAdapter over 12 yearsthx a lot, I'm learning bash and this is very helpful.
-
UAdapter over 12 yearsthx a lot, I'm learning bash and this is very helpful.
-
Todd Partridge 'Gen2ly' almost 12 yearsNice 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" \;
-
enzotib over 8 years@gniourf_gniourf: thank for editing and providing a more robust command.
-
bwduncan over 5 yearsThis is actually better than
find
, as it can usexargs
in parallel (-P
) much more easily.