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?
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It only takes a minute to sign up.
Sign up to join this communityfind . -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 \;
cd
alone do not work is because it is a shell builtin, nothing to do with the environment.
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" \;
May 29, 2012 at 16:59
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 slash
printf
with \0
(null byte) separates the elements one for each line
the option -L1
to xargs
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, but cd
should put double quotes again to interpret it as a single string; using --
makes the cd
command robust against filenames that start with a hyphen
to avoid the strange use of $0
as a parameter, is usual to put a first dummy argument _
echo *
but was not aware of */
. And putting an underscore as the 1st argument is a neat trick.
find
, as it can use xargs
in parallel (-P
) much more easily.
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
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
{}
, and a -maxdepth 1
could be useful, if the OP do not want to recurse into subdirectories.
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
.
$NF
gives only the last word of the name. This is one of the reason why people is suggested to not parse ls
output.
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.
Other alternative:
for directory in *
do
(cd $directory && awk '{print $1" && pwd"}')
done
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.