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.
Related
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
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 .
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.
I'm working on a bash script that should do the following: for every directory beginning with Event_*, (in cat eventList), cd into the directory, and if the string "ZJ.ROT" exists in the file *.mcp, I want to copy the file "ROT" to another directory. In simpler terms: loop through directories: if string "ZJ.ROT" exists in a file in that directory, output another file from that directory to a separate directory.
#!/bin/bash
mkdir newdire
for dir in `cat eventList`; do
cd $dir
pwd
if grep "ZJ.KNYN" *.mcp; then
cp "ROT" "newdire"
fi
done
The error I get is:
./azim.sh: line 5: cd: Event_2014.11.21.10.10.19.630: No such file or directory
/Users/files/Event_2013.12.01.06.29.57.800
grep: *.mcp: No such file or directory
For some reason, this for loop isn't looping through each directory, but it's stuck in the first directory Event_2013.... Any ideas about how to implement this code?
After the first time you cd to a subdirectory you are in it for all future loop iterations so your subsequent cds will fail, as you are experiencing. You also need to quote your variables and there's other issues. Try this:
pwd="$PWD"
mkdir newdire
while IFS= read -r dir; do
cd "$dir"
grep -Fq "ZJ.KNYN" *.mcp &&
cp "ROT" "${pwd}/newdire"
cd "$pwd"
done < eventList
but of course you don't actually need to cd:
mkdir newdire
while IFS= read -r dir; do
grep -Fq "ZJ.KNYN" "$dir"/*.mcp &&
cp "${dir}/ROT" newdire
done < eventList
Problem seems to be here:
if grep "ZJ.KNYN" *.mcp; then
You should use -q option in grep to suppress the output and check the return status like this:
if grep -qF "ZJ.KNYN" *.mcp; then
-F is for fixed string search.
Also there is no need to change directory inside the loop.
Your full script can be better rewritten as:
#!/bin/bash
mkdir newdire
for dir in Event_*; do
if [[ -d "$dir" ]] && grep -qF "ZJ.KNYN" "$dir"/*.mcp 2>/dev/null; then
cp "$dir/ROT" "newdire/"
fi
done
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/*/