Bash for loop doesn't finish looping - bash

I want to get a list of subdirs, cd into each and run a command, here's what I'm trying:
#!/bin/bash
for D in installedPlugins/*; do
if [ -d "${D}" ]; then
cd "${D}" && echo "${D}" && cd ..
fi
done
It does this for the first subdir but does not continue for some reason.

Your script traverses 2 directories into ${D} but only one directory out (cd ..). For example:
cd "installedPlugins/SomeDir" && echo "installedPlugins/SomeDir" && cd ..
...and voilà! The currect directory for your script is now "installedPlugins/".
You probably meant to use cd ../.. or cd - instead. As already noted in the commands, it is better to use find to iterate over the directories, e.g.:
find installedPlugins/ -type d -maxdepth 1 -exec echo "{}" \;

Here's another way with pushd and popd. They're sort of noisy, so I'm sending their output to /dev/null:
#!/bin/bash
for D in installedPlugins/*; do
test -d "$D" && {
pushd "$D" > /dev/null
pwd
popd > /dev/null
}
done

I'd guess the second iteration's cd fails because the first iteration changed your working directory :
1st iteration, $D is installedPlugins/something. You cd into ./installedPlugins/something/, then cd back to ./installedPlugins
2nd iteration, $D is installedPlugins/somethingElse. you cd into ./installedPlugins/somethingElse, but that fails because you already are into installedPlugins.
You could use a simple find to solve your problem :
find ./installedPlugins/ -type d

The problem is your in a deeper directory once you cd:
for D in installedPlugins/*; do
if [ -d "${D}" ]; then
cd "${D}" && echo "${D}" && cd ../.. # notice change here
fi
done
On the first cd you've to depth 2, not 1, relative to your point. So you need to go back 2 levels.
Note: You should be using find for this task.

Related

Bash doesn't create all files and directories with loop

I'm trying to create a directory with list of directories with list of files.
Could you explain me, why this script doesn't work properly?
createapp() {
local folders=('graphql' 'migrations' 'models' 'tests')
local files=('__init__.py' 'admin.py' 'apps.py' 'views.py')
cd backend/apps
mkdir $COMMAND2
cd $COMMAND2
for folder in $folders
do mkdir $folder && cd $folder
for file in $files
do touch $file && cd ..
done
done
}
It creates a graphql directory, and an __init__.py file in it, but that's all.
There are a few problems:
You aren't iterating over the contents of the array, only the first element.
You are executing cd .. too soon; you want to do that after the loop that creates each file.
for folder in "${folders[#]}"; do
mkdir -p "$folder" &&
cd "$folder" &&
for file in "${files[#]}"; do
touch "$file"
done &&
cd ..
done
There are two ways to simplify this. If you keep the cd command, you only need one call to touch:
for folder in "${folders[#]}"; do
mkdir -p "$folder" && cd "$folder" && touch "${files[#]}" && cd ..
done
Or you can get rid of the cd command, and pass a longer path to touch:
for folder in "${folders[#]}"; do
mkdir -p "$folder" &&
for file in "${files[#]}"; do
touch "$folder/$file"
done
done
or even
for folder in "${folders[#]}"; do
mkdir -p "$folder" &&
touch "${files[#]/#/$folder/}"
done
If you want to get fancy, you can do all of this with zero loops and a combination of bash brace and array expansions:
#!/bin/bash
createapp() {
local folders=('graphql' 'migrations' 'models' 'tests')
local files=('__init__.py' 'admin.py' 'apps.py' 'views.py')
IFS=,
eval mkdir -p "{${folders[*]}}"
eval touch "{${folders[*]}}/{${files[*]}}"
}
Note that the use of eval can be dangerous, but it's pretty limited in this implementation as the two arrays are local and not using user-defined input. If this was zsh, you could embed brace and array expansion without the need for eval

How do I get this script to delete zero-length files in the current directory and subdirectories without using find?

I want my script to delete all zero length files in the directory that is shown as an argument as well as all of the zero length files in the sub directory.
I wrote a script that can delete all of the zero length files in the current directory. I need it to be able to enter sub directories and delete those zero length files too.
What can I do to solve this problem?
DIR=$1
TRAV= touch -c "$DIR"/*
for d in ./$(DIR)/*; do
if [ -f "${d}" ] && [ -s "${d}" ]; then
echo "$d has some data"
else
echo "$d has no data, we're deleting it."
rm -d -R ${d}
fi
done
~ ~ ~
The program works perfectly fine so far, it just doesn't delete the zero length files in the sub directories of the specified directory.
add shopt -s globstar to the script to enable globstar, change line four
from
for d in ./$(DIR)/*; do
to
for d in ./$(DIR)/**/*; do
then run the script with your directory as an argument
Why don't you use "find"?
If you don't use "find", you can do by using a recursive call and ls command instead.
#!/bin/bash
function del(){
local dir=$1
cd ${dir}
for f in $(ls -1)
do
if [ -d "${f}" ]; then
(del ${f})
else
if [ ! -s ${f} ]; then
rm -f ${f}
fi
fi
done
}
del .

Bash/shell search for subfolder in current dir and cd into it

How would I go about search if the folder "test" is anywhere within my current dir and once the first occurrence is found automatically cd into it? Using the terminal on mac (bash).
You can write:
cd "$(find . -type d -name test -print -quit)"
(Caveat: this works for test, but will not work for any filename ending in newlines. Fortunately, I've never heard of anyone having a real filename that ended in a newline — it's possible, but never done — and the filename is an argument under your control. So I can't imagine that this will be a problem.)
The below will work if that dir exists ->
cd `find . -name test -type d`
For bash I guess below should work ->
cd $(find . -name test -type d)
You could use the globstar option of bash which searches directories recursively.
shopt -s globstar
for i in **/*test; do
if [[ -d $i ]]; then
cd "$i"
break
fi
done
You can try :
if [ -d "test" ]; then cd test; fi
Or simply :
[ -d "test" ] && cd test
Or just do ...
cd test
... if test is a directory the you just cd'ed into it.
If is not ... so what.

Looping through directories

I have a directory structure like and I am looping through directories like but getting error that could not find the directory any idea? In the first dir it is looking for dir1 dir2 dir3 and so on and in the second dir1 it is looking for dirA dir B and dirC Thanks.
./subtle1/
/fooA/file.txt
/fooB/file.txt
/fooC/file.txt
./subtle2/
/fooA/file.txt
/fooB/file.txt
/fooC/file.txt
for i in ~/new/subtle*;
do
if [ -d "$i" ] ;
then
cd $i
for j in "$i"/foo* ; do
if [ -d "$j" ] ;
then
cd $j
mv file.txt $i.$j.file.txt
done
done
One problem that strikes me immediately is that you cd within a loop, and you don't come out of that directory for the next iteration.
So you'll cd into ~/new/subtle1/fooA, do some work, and then for the next iteration you're already in that directory, whereas you want to be at your original (starting) point.
I would check out pushd/popd. pushd works like cd, but it maintains a stack of visited directories, and popd will take you back in that stack.
e.g.
$ pwd
/home/brian
$ pushd /var/log
$ pwd
/var/log
$ popd
$ pwd
/home/brian
If you want to just change the filenames that way:
mv ./subtle1/fooB/file.txt ./subtle1/fooB/subtle1.fooB.file.txt
mv ./subtle1/fooA/file.txt ./subtle1/fooA/subtle1.fooA.file.txt
mv ./subtle1/fooC/file.txt ./subtle1/fooC/subtle1.fooC.file.txt
mv ./subtle2/fooB/file.txt ./subtle2/fooB/subtle2.fooB.file.txt
mv ./subtle2/fooA/file.txt ./subtle2/fooA/subtle2.fooA.file.txt
mv ./subtle2/fooC/file.txt ./subtle2/fooC/subtle2.fooC.file.txt
than all you need is:
find -type f -print|sed -r -e 's/\.\/(.+)\/(.+)\/(.+)/\0 .\/\1\/\2\/\1.\2.\3/'|xargs -n 2 mv
If you really want to do this by a script and use cd to walk around than try this:
#!/bin/bash
for sub in subtle*; do
if [ -d "${sub}" ]; then
cd ${sub}
for foo in foo*; do
if [ -d "${foo}" ]; then
cd ${foo}
pwd
echo mv -- file.txt ${sub}.${foo}.file.txt
cd ..
fi
done
cd ..
fi
done
you can remove pwd and echo to just execute mv.

Deleting all directories in a given folder in bash

I am trying to write a shell script to delete all the sub directories in a given directory. I know there is an easy approach for the same.
Like doing this
find ./ -type d -exec rm -r {} \;
but since I am learning shell scripting so I prefer to write a script for the same. Here is my approach
for i in `ls ./*`; do
if [ -d $i ];then
rm -r $i
fi
done
When I run this script this gives me following errors
rm: cannot remove directory: `.'
after giving this error this stops.So what is the error in my approach.As far as I understand blank names should create some problem. But this script has failed to go that far.
The ls ./* makes a list of all the files in each immediate subdir of . The -d then checks the name of the file but as if it was in . not the subdir it comes from.
For example if you had:
foo1/
bar
foo2/
baz
then ls ./* would make a list of bar and baz, as the ./* would match foo1 and foo2 and ls would then list the contents of each of those 2 directories.
The error message you are getting is probably because your ls has been aliased to be 'ls -a' which lists . and .. As the answer by Florin says, you can use ls -A ./* to avoid that issue.
If you just want to delete the directories in ., just do:
for i in `ls -A`; do if [ -d $i ]; then rm -r $i; fi; done
. and .. should not appear in the output of ls.
However, you can test with
ls -A
(-A means 'almost all'
-a means 'all')
And: why you don't jus use for i in 'ls' ?
You shouldn't use ls in this command. There's a very simple way to make sure you only iterate over directories:
for dir in */
do
echo "$dir"
done
The problem is that ls command list links for the current folder and the parent folder. So you must test if your i variable is setted to '.' or '..'
if [ -d $i ] -a [ $i != '.' ] -a [ $i != '..' ]
then
rm -r $i
fi
"*/" should match all subdirectories
Try something like this:
rm -rf /path/to/mySubfolder/*/

Resources