Bash script that remove duplicates - bash

This works incorrectly. The script should delete only copies, but this script deletes all files.
#!/bin/bash
DIR=$1
if [[ -z "$DIR" ]]; then
echo "Error: files dir is undefined"
fi
files="$( find ${DIR} -type f )"
for file1 in $files; do
for file2 in $files; do
if cmp -s "$file1" "$file2"; then
rm $file2
fi
done
done

Found this on superuser:
#!/bin/bash
declare -A arr
shopt -s globstar
for file in **; do
[[ -f "$file" ]] || continue
read cksm _ < <(md5sum "$file")
if ((arr[$cksm]++)); then
echo "rm $file"
fi
done

I find answer:
#!/bin/bash
DIR=$1
if [[ -z "$DIR" ]]; then
echo "Error: files dir is undefined"
fi
files="$( find ${DIR} -type f )"
for file1 in $files; do
for file2 in $files; do
if cmp -s "$file1" "$file2"; then
rm $file2
fi
done
done

Related

Counting directories and files in bash

Simple counting dirs and files does not work. I am checking each file by -f and -d flag.
Where is a problem?
LOCATION=$1
for FILE in $(ls $LOCATION | egrep '^.{0,3}$');
do
echo "$FILE"
if [ -f $FILE ]
then
echo "its a file"
fi
if [ -d $FILE ]
then
echo "its a dir"
fi
done
shopt -s dotglob # count hidden files
for file in "$LOCATION/"*; do
[[ -f $file ]] && ((f++))
[[ -d $file ]] && ((d++))
done
echo "${d:-0} dirs"
echo "${f:-0} files"
without involving external utilities

Bash script loop through subdirectories and write to file

I have no idea I have spent a lot of hours dealing with this problem. I need to write script. Script should loop recursively through subdirectories in current directory. It should check files count in each directory. If file count is greater than 10 it should write all names of these file in file named "BigList" otherwise it should write in file "ShortList". This should look like
---<directory name>
<filename>
<filename>
<filename>
<filename>
....
---<directory name>
<filename>
<filename>
<filename>
<filename>
....
My script only works if subdirecotries don't include subdirectories in turn.
I am confused about this. Because it doesn't work as I expect. It will take less than 5 minutes to write this on any programming language for my.
Please help to solve this problem , because I have no idea how to do this.
Here is my script
#!/bin/bash
parent_dir=""
if [ -d "$1" ]; then
path=$1;
else
path=$(pwd)
fi
parent_dir=$path
loop_folder_recurse() {
local files_list=""
local cnt=0
for i in "$1"/*;do
if [ -d "$i" ];then
echo "dir: $i"
parent_dir=$i
echo before recursion
loop_folder_recurse "$i"
echo after recursion
if [ $cnt -ge 10 ]; then
echo -e "---"$parent_dir >> BigList
echo -e $file_list >> BigList
else
echo -e "---"$parent_dir >> ShortList
echo -e $file_list >> ShortList
fi
elif [ -f "$i" ]; then
echo file $i
if [ $cur_fol != $main_pwd ]; then
file_list+=$i'\n'
cnt=$((cnt + 1))
fi
fi
done
}
echo "Base path: $path"
loop_folder_recurse $path
I believe that this does what you want:
find . -type d -exec env d={} bash -c 'out=Shortlist; [ $(ls "$d" | wc -l) -ge 10 ] && out=Biglist; { echo "--$d"; ls "$d"; echo; } >>"$out"' ';'
If we don't want either to count subdirectories to the cut-off or to list them in the output, then use this version:
find . -type d -exec env d={} bash -c 'out=Shortlist; [ $(ls -p "$d" | grep -v "/$" | wc -l) -ge 10 ] && out=Biglist; { echo "--$d"; ls -p "$d"; echo; } | grep -v "/$" >>"$out"' ';'

Simulating the find command: why is my code not recursing correctly?

My assignment is to write a Unix shell script that asks the user for the name of a directory, and then works exactly like find.
Here is what I have so far:
#!/bin/bash
dir_lister()
{
cd "$1"
echo "$1"
list=$(ls -l ${1})
nolines=$(echo "$list" | awk 'END{printf "%d",NF}')
if [ $nolines -eq 2 ]
then
echo "$1"
return
fi
filelist=$(echo "$list" | grep ^-.*)
dirlist=$(echo "$list" | grep ^d.*)
filename=$(echo "$filelist"| awk '{printf "%s\n",$NF}')
present=$(pwd)
echo "$filename"| awk -v pres=$present '{printf "%s/%s\n",pres,$0}'
dirlist2=$(echo "$dirlist" | awk '{printf "%s\n",$NF}')
echo "$dirlist2" | while IFS= read -r line;
do
nextCall=$(echo "$present/$line");
dir_lister $nextCall;
cd ".."
done
cd ".."
}
read -p "Enter the name of the direcotry: " dName
dir_lister $dName
The problem is, after a depth of three directories, this script gets into an infinite loop, and I don't see why.
EDIT:
Here is the code i came up with after looking at your answer, it still doesn't go more than 1 directory depth:
#!/bin/bash
shopt -s dotglob # don't miss "hidden files"
shopt -s nullglob # don't fail on empty directories
list_directory()
{
cd "$2"
cd "$1"
##echo -e "I am called \t $1 \t $2"
for fileName in "$1/"*
do
##echo -e "hello \t $fileName"
if [ -d "$fileName" ];
then
echo "$fileName"
list_directory $fileName $2
else
echo "$fileName"
fi
done
}
read -p "Enter the direcotory Name: " dirName
var=$(pwd)
list_directory $dirName $var
Okay, that is completely the wrong way to list files in a directory (see ParsingLs). I'll give you the pieces and you should be able to put them together into a working script.
Put this at the top of your script:
shopt -s dotglob # don't miss "hidden files"
shopt -s nullglob # don't fail on empty directories
Then you can easily loop over directory contents with:
for file in "$directory/"* ; do
#...
done
Test if you have a directory:
if [ -d "$file" ] ; then
# "$file" is a directory, recurse...
fi

Need a quick bash script

I have about 100 directories all in the same parent directory that adhere to the naming convention [sitename].com. I want to rename them all [sitename].subdomain.com.
Here's what I tried:
for FILE in `ls | sed 's/.com//' | xargs`;mv $FILE.com $FILE.subdomain.com;
But it fails miserably. Any ideas?
Use rename(1).
rename .com .subdomain.com *.com
And if you have a perl rename instead of the normal one, this works:
rename s/\\.com$/.subdomain.com/ *.com
Using bash:
for i in *
do
mv $i ${i%%.com}.subdomain.com
done
The ${i%%.com} construct returns the value of i without the '.com' suffix.
What about:
ls |
grep -Fv '.subdomain.com' |
while read FILE; do
f=`basename "$FILE" .com`
mv $f.com $f.subdomain.com
done
See: http://blog.ivandemarino.me/2010/09/30/Rename-Subdirectories-in-a-Tree-the-Bash-way
#!/bin/bash
# Simple Bash script to recursively rename Subdirectories in a Tree.
# Author: Ivan De Marino <ivan.demarino#betfair.com>
#
# Usage:
# rename_subdirs.sh <starting directory> <new dir name> <old dir name>
usage () {
echo "Simple Bash script to recursively rename Subdirectories in a Tree."
echo "Author: Ivan De Marino <ivan.demarino#betfair.com>"
echo
echo "Usage:"
echo " rename_subdirs.sh <starting directory> <old dir name> <new dir name>"
exit 1
}
[ "$#" -eq 3 ] || usage
recursive()
{
cd "$1"
for dir in *
do
if [ -d "$dir" ]; then
echo "Directory found: '$dir'"
( recursive "$dir" "$2" "$3" )
if [ "$dir" == "$2" ]; then
echo "Renaming '$2' in '$3'"
mv "$2" "$3"
fi;
fi;
done
}
recursive "$1" "$2" "$3"
find . -name '*.com' -type d -maxdepth 1 \
| while read site; do
mv "${site}" "${site%.com}.subdomain.com"
done
Try this:
for FILE in `ls -d *.com`; do
FNAME=`echo $FILE | sed 's/\.com//'`;
`mv $FILE $FNAME.subdomain.com`;
done

bash scripting challenge

I need to write a bash script that will iterate through the contents of a directory (including subdirectories) and perform the following replacements:
replace 'foo' in any file names with 'bar'
replace 'foo' in the contents of any files with 'bar'
So far all I've got is
find . -name '*' -exec {} \;
:-)
With RH rename:
find -f \( -exec sed -i s/foo/bar/g \; , -name \*foo\* -exec rename foo bar {} \; \)
find "$#" -depth -exec sed -i -e s/foo/bar/g {} \; , -name '*foo*' -print0 |
while read -d '' file; do
base=$(basename "$file")
mv "$file" "$(dirname "$file")/${base//foo/bar}"
done
UPDATED: 1632 EST
Now handles whitespace but 'while read item' never terminates. Better,
but still not right. Will keep
working on this.
aj#mmdev0:~/foo_to_bar$ cat script.sh
#!/bin/bash
dirty=true
while ${dirty}
do
find ./ -name "*" |sed -s 's/ /\ /g'|while read item
do
if [[ ${item} == "./script.sh" ]]
then
continue
fi
echo "working on: ${item}"
if [[ ${item} == *foo* ]]
then
rename 's/foo/bar/' "${item}"
dirty=true
break
fi
if [[ ! -d ${item} ]]
then
cat "${item}" |sed -e 's/foo/bar/g' > "${item}".sed; mv "${item}".sed "${item}"
fi
dirty=false
done
done
#!/bin/bash
function RecurseDirs
{
oldIFS=$IFS
IFS=$'\n'
for f in *
do
if [[ -f "${f}" ]]; then
newf=`echo "${f}" | sed -e 's/foo/bar/g'`
sed -e 's/foo/bar/g' < "${f}" > "${newf}"
fi
if [[ -d "${f}" && "${f}" != '.' && "${f}" != '..' && ! -L "${f}" ]]; then
cd "${f}"
RecurseDirs .
cd ..
fi
done
IFS=$oldIFS
}
RecurseDirs .
bash 4.0
#!/bin/bash
shopt -s globstar
path="/path"
cd $path
for file in **
do
if [ -d "$file" ] && [[ "$file" =~ ".*foo.*" ]];then
echo mv "$file" "${file//foo/bar}"
elif [ -f "$file" ];then
while read -r line
do
case "$line" in
*foo*) line="${line//foo/bar}";;
esac
echo "$line"
done < "$file" > temp
echo mv temp "$file"
fi
done
remove the 'echo' to commit changes
for f in `tree -fi | grep foo`; do sed -i -e 's/foo/bar/g' $f ; done
Yet another find-exec solution:
find . -type f -exec bash -c '
path="{}";
dirName="${path%/*}";
baseName="${path##*/}";
nbaseName="${baseName/foo/bar}";
#nbaseName="${baseName//foo/bar}";
# cf. http://www.bash-hackers.org/wiki/doku.php?id=howto:edit-ed
ed -s "${path}" <<< $'H\ng/foo/s/foo/bar/g\nwq';
#sed -i "" -e 's/foo/bar/g' "${path}"; # alternative for large files
exec mv -iv "{}" "${dirName}/${nbaseName}"
' \;
correction to find-exec approach by gregb (adding quotes):
# compare
bash -c '
echo $'a\nb\nc'
'
bash -c '
echo $'"'a\nb\nc'"'
'
# therefore we need
find . -type f -exec bash -c '
...
ed -s "${path}" <<< $'"'H\ng/foo/s/foo/bar/g\nwq'"';
...
' \;

Resources