I'm attempting to write a script that will be run in a given directory with many single level sub directories. The script will cd into each of the sub directories, execute a command on the files in the directory, and cd out to continue onto the next directory. What is the best way to do this?
5 Answers
for d in ./*/ ; do (cd "$d" && somecommand); done
-
25So since the answerer has omitted any sort of explanation, I'll attempt one.
for d in ./*/
starts a loop that stores every item in./*/
(a list of files/folders, in this case) in a variable$d
.do (cd "$d" && somecommand);
starts the body of the loop. Inside the body, it starts a subshell and runs thecd
andsomecommand
commands. Since it is a child shell, the parent shell (the shell from which you're running this command) retains its CWD and other environment variables.done
simply closes the loop body. Commented Dec 6, 2014 at 3:25 -
1this methodworks for sub directories of directories:
for d in ./*/ ; do (cd "$d" && ls); done
,will not work. but,for d in ./*/ ; do (cd "$d" && for d in ./*/ ; do (cd "$d" && ls); done ); done
will work. -using ls as the command in this example. Commented Sep 6, 2016 at 13:06 -
-
The best way is to not use cd
at all:
find some/dir -type f -execdir somecommand {} \;
execdir
is like exec
, but the working directory is different:
-execdir command {} [;|+]
Like -exec, but the specified command is run from the
subdirectory containing the matched file, which is not normally
the directory in which you started find. This a much more
secure method for invoking commands, as it avoids race
conditions during resolution of the paths to the matched files.
It is not POSIX.
-
Does this work with aliases? I have one to download certain files but it isn't recognizing it when I type in find */.link -type f -execdir md $(cat .link) {} \; Commented Dec 5, 2014 at 15:51
-
@SomethingJones no,
find
executes those commands, so it wouldn't be aware of aliases. What ismd
and is the.link
a directory?– muruCommented Dec 5, 2014 at 15:52 -
.link is a text file that has the URL it needs to download. md is an alias to wget with a bunch of flags set. Is there a way to make it aware of aliases? Commented Dec 5, 2014 at 16:15
-
@SomethingJones For your particular use-case, in
bash
:find . -type f -iname '*.link' -execdir ${BASH_ALIASES[md]} -i {} \;
You don't need to docat
withwget
, which has an-i
flag for reading in an URL from a file. Also this is somewhat different from your original question (since you seem to be interested in only files named.link
and not any other files which may be present).– muruCommented Dec 5, 2014 at 16:20 -
Do you know how to do that with zsh? I tried what you gave me and am getting a "Bad substitution" error. Also, how would I be able to extract the content of the .link file? I know I don't need it in this case but I imagine I will soon. Commented Dec 5, 2014 at 21:13
cd -P .
for dir in ./*/
do cd -P "$dir" ||continue
printf %s\\n "$PWD" >&2
command && cd "$OLDPWD" ||
! break; done || ! cd - >&2
The above command doesn't need to do any subshells - it just tracks its progress in the current shell by alternating $OLDPWD
and $PWD
. When you cd -
the shell exchanges the value of these two variables, basically, as it changes directories. It also prints the name for each directory as it works there to stderr.
I just had a second look at it and decided I could do a better job with error handling. It will skip a dir into which it cannot cd
- and cd
will print a message about why to stderr - and it will break
w/ a non-zero exit code if your command
does not execute successfully or if running command
somehow affects its ability to return to your original directory - $OLDPWD
. In that case it also does a cd -
last - and writes the resulting current working directory name to stderr.
Method 1 :
for i in `ls -d ./*/`
do
cd "$i"
command
cd ..
done
Method 2 :
for i in ./*/
do
cd "$i"
command
cd..
done
Method 3 :
for i in `ls -d ./*/`
do
(cd "$i" && command)
done
I hope this was useful. You can try all permutation and combinations on this.
Thanks :)
-
1Welcome to the site, and thank you for your contribution. Please note, however, that parsing the output of
ls
is highly disrecommended as it can stumble on directory names with whitespace or other special characters, so promoting method 1 and 3 without a warning might not be a good idea.– AdminBeeCommented Jan 26, 2021 at 8:17