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/*/
Related
I came across scenario using shell script. I need to delete the folder and Zip if it has the same name. Can any one please help me in this .
Example
Below is the directory path in which script need to search the same name in the directory (here it needs to print and delete example and example.zip)
Path:/tmp/test/
/tmp/test/example
/tmp/test/example.zip
/tmp/test/zack
You can use script as below:
#!/bin/bash
parent_dir=/tmp/test/
for i in `ls /tmp`
do
if [[ $i != *.zip ]]; then
if [[ -f /tmp/$i.zip ]]; then
array=$array" "$i
fi
fi
done
array=( $array )
for i in ${array[#]}
do
rm -r $parent_dir$i
rm -r $parent_dir$i".zip"
done
This should do a trick, even suitable for a cronjob:
find $DIR_PATH -type d -exec sh -c '[ -f "{}.zip" ] && rm -fr {}.zip {}' \;
Just set DIR_PATH to where youre searching
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 have a folder called /input/temp. Inside the folder I have lot of files. I need to find the file of pattern Article_????_test_?????????.txt and replace by format below.
Article_????_?????????.txt
Below is the code I tried and which doesn't work:
echo "Please provide the file name Corresponding to DC..."
read file
ls $HOME/*.txt | grep $file
if [ $? -eq 0 ]
find . $file '*_Test_*' -exec bash -c 'mv $0 ${0/_Test/ }' {} \;
if [ $? -eq 0 ]
find . $file -name "*.txt" -exec bash -c "mv {} \`echo {} | sed -e 's/[_TEST_]/_/g'\`" \;
then
I Got below error:
find: 0652-083 Cannot execute bash:: A file or directory in the path name does not exist.
find: 0652-083 Cannot execute bash:: A file or directory in the path name does not exist.
bash can't be executed on my platform.
Unless the file name is a regular expression you can use if [ -e "$file" ] instead of ls + grep + test.
When you do find . $file '*_Test_*' the last three parameters are actually taken as files or directories to search underneath! It will return
all files in the current directory,
all files in the directory $file or the path $file if it's not a directory, and
all files in any directories matching *_Test_* or their paths if they are not directories.
There's no need for bash - you can run mv directly in -exec. This is just extra complexity for no gain.
Use $(command) instead of command for much easier quote handling. Each $() has a separate quoting context, so you can do for example echo "$(command "$(c2 "argument with spaces")")".
According to the link here:
This should work
ls -1 Article_????test?????????.txt|awk '{old=$0;gsub(/test/,"_",$0);system("mv \""old"\" "$0)}'
Also try the 'rename' command.
rename 's/_test_/_/' *.txt
You can fine tune the regular expression...
Update from your code:
cd $HOME
find . -name '*_Test_*' |while read line
do
echo mv ${line) ${line/_Test/}
done
If you need search the pattern Article_????test?????????.txt, try this
cd $HOME
find . -name 'Article_????_test_?????????.txt' |while read line
do
echo mv ${line) ${line/_Test/}
done
I cannot get the following piece of script (which is part of a larger backup script) to work correctly:
BACKUPDIR=/BACKUP/db01/physical/incremental # Backups base directory
FULLBACKUPDIR=$BACKUPDIR/full # Full backups directory
INCRBACKUPDIR=$BACKUPDIR/incr # Incremental backups directory
KEEP=5 # Number of full backups (and its incrementals) to keep
...
FIRST_DELETE=`expr $KEEP + 1` # add one to the number of backups to keep, this will be the first deleted
FILE0=`ls -ltr $FULLBACKUPDIR | awk '{print $9}' | tail -$FIRST_DELETE | head -1` # search for the first backup to be deleted
...
find $FULLBACKUPDIR -maxdepth 1 -type d ! -newer $FULLBACKUPDIR/$FILE0 -execdir echo "removing: "$FULLBACKUPDIR/$(basename {}) \; -execdir bash -c 'rm -rf $FULLBACKUPDIR/$(basename {})' \; -execdir echo "removing: "$INCRBACKUPDIR/$(basename {}) \; -execdir bash -c 'rm -rf $INCRBACKUPDIR/$(basename {})' \;
So the find works correctly which on its own will output something like this:
/BACKUPS/db01/physical/incremental/full/2013-08-12_17-51-28
/BACKUPS/db01/physical/incremental/full/2013-08-12_17-51-28
/BACKUPS/db01/physical/incremental/full/2013-08-12_17-25-07
What I want is the -exec to echo a line showing what is being removed and then remove the folder from both directories.
I've tried various ways to get just the basename but nothing seems to be working. I get this:
removing: /BACKUPS/mysql/physical/incremental/full/"/BACKUPS/mysql/physical/incremental/full/2013-08-12_17-51-28"
removing: /BACKUPS/mysql/physical/incremental/incr/"/BACKUPS/mysql/physical/incremental/full/2013-08-12_17-51-28"
removing: /BACKUPS/mysql/physical/incremental/full/"/BACKUPS/mysql/physical/incremental/full/2013-08-12_17-25-07"
And of course the folders arn't deleted because they don't exist, just fail silently because of the -f option. If I remove the -f I get the 'cannot be found' error on each rm.
How do I accomplish this? Because backups and parts of backups may be stored across different storage systems I really need the ability to just get the folder name for use in any known path.
the $(basename {}) is run first, making removing: "$INCRBACKUPDIR/$(basename {}) to removing: "$INCRBACKUPDIR/{} then the replacement is done of {}.
a way around it may be to pipe it to bash:
-exec echo "echo \"removing: \\\"$INCRBACKUPDIR/\$(basename {})\\\"\" | bash" \;
Lots of broken here.
All caps variables are by convention env vars and should not be used in scripts.
Using legacy backticks instead of $()
Parsing the output of ls (!)
Parsing the output of ls -l (!!!)
Expanding variables known to contain paths without full quotes.
All you absolutely need in order to improve this is to -exec bash properly, e.g.
-execdir bash -c 'filepath="$1" ; base=$(basename "$filepath") ; echo use $filepath and $base here' -- {} \;
But how about this instead:
#!/usr/bin/env bash
backup_base=/BACKUP/db01/physical/incremental
full_backup="$backup_base"/full
incremental_backup="$backup_base"/incr
keep=5
rm=echo
let n=0
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
if [[ $n -lt $keep ]] ; then
let n=n+1
continue
fi
base=$(basename "$file")
echo "removing: $full_backup/$base"
"$rm" -rf -- "$full_backup"/"$base"
echo "removing: $incremental_backup/$base"
"$rm" -rf -- "$incremental_backup"/"$base"
done < <(find "$full_backup" -maxdepth 1 -printf '%T#.%p\0' 2>/dev/null | sort -z -r -n -t. -k1,2)
Iterate over files and directories immediately under the backup dir and skip the first 5 newest. Delete from the full and incremental dirs files matching the names of the rest.
This is an essentially safe version, except of course for timing attacks.
I have defined rm as being echo to avoid accidental deletes; swap it back to rm for actual deletion once you're sure it's correct.
I'm trying to do a bash script that will find & copy similar files to a destination directory.
For example, I'm passing a parameter 12300 to a script and I want to copy all files that start with 12300... to a new directory.
like this:
sh script.sh 12300
and here's the script:
if [ -f /home/user/bashTest/$#*.jpg ]
then
cp /home/user/bashTest/$#*.jpg /home/user/bashTest/final/
fi
This just doesn't work. I have tried all kinds of solutions but nothing has worked.
The question is: How can I use wildcard with parameter?
When you're checking for multiple files with -f or -e it can get nasty. I recommend kenfallon's blog. This is something like what he would recommend:
#! /bin/bash
ls -l /home/user/bashTest/$1*.jpg > /dev/null
if [ "$?" = "0" ]
then
cp /home/user/bashTest/$1*.jpg /home/user/bashTest/final/
fi
Not sure how the $# would play in here, or if it's required.
Enclose the thing that expands to the parameters in {}, i.e. /home/user/bashTest/${#}*.jpg. You should use $1 instead of $# in your case however as you only seem to be able to handle the first argument given to the script. $1 expands to the first argument, $2 to the second etc.
You also need a loop to iterate over all files that this glob expands to, e.g.
for file in /tmp/${#}*.jpg
do
if [ -f $file ]
then
echo $file
fi
done
Here is a solution:
#!/bin/bash
cp /home/user/bashTest/${1}*.jpg /home/user/bashTest/final/
Discussion
In this case, a simple cp command will do
I have tested it with files that have embedded spaces
Write this in script.sh:
cp /home/user/bashTest/$1*.jpg /home/user/bashTest/final/
That's all.
UPD. #macduff solution usefull too.
This will find all of them in your $HOME directory and subdirectories (you may wish to tweak find to follow/not follow symlinks and/or adjust the $HOME base directory where it starts the search)
#!/bin/sh
DEST=/your/dest/folder
for FILE in `find "$HOME" -iname "$1"*`;do
[ -f "$FILE" ] && mv "$FILE" "$DEST/$FILE"
#or ln -s ...if you want to keep it in its original location
done
if you want to do multiple patterns using $#
for PATTERN in $#; do
for FILE in `find "$HOME" -iname "$PATTERN"*`;do
[ -f "$FILE" ] && mv "$FILE" "$DEST/$FILE"
done
done