How can I count the hidden files in a directory using a shell script? - bash

I have used this code
#!/bin/bash
ls -l
echo -n "Number of simple files : "
ls -l | egrep '^-' | wc -l
echo -n "Number of directories : "
ls -l | egrep '^d' | wc -l
echo -n "Number of hidden files : "
ls -la | egrep '^.*\.$' | wc -l
echo -n "Number of hidden directories : "
ls -la | egrep '^d.*\.$' | wc -l
echo " End"
While I can understand how the first two egrep work I can't figure out how the last
two work. More specific what does this mean '^.*\.$' ?
I want a file that starts with . (hidden file) and then how I should shape my regular expression?

You shouldn't be using grep (or ls) for this task at all. See http://mywiki.wooledge.org/ParsingLs for an in-depth discussion of how ls should only ever be used for interactive display to humans.
all_files=( * ) # includes directories
directories=( */ ) # directories only
hidden_files=( .* ) # includes directories
hidden_directories=( .*/ ) # directories only
echo "Number of files: $(( ${#all_files[#]} - ${#all_directories[#]} ))"
echo "Number of directories: ${#directories[#]}"
echo "Number of hidden files: $(( ${#hidden_files[#]} - ${#hidden_directories[#]} ))"
echo "Number of hidden directories: $(( ${#hidden_directories[#]} - 2 ))"
The - 2 in the last calculation is to remove . and .., which will always be present.

Note that your approach of parsing ls output for this purpose is wrong. See #Dharles Duffy's answer for a better alternative. To answer your question though and explain the regex a little bit:
'^.*\.$' means
^ // From the beginning of the string
.* // match zero or more of any character
\. // match one literal full-stop
$ // end of the string
I am not sure what you mean with a "secret" file but if you mean a hidden file, i.e. one that begins with . and then a filename, the way to regex that would be
'^\..*$'
Note that this is not when parsing ls output and is just for a file or directory name and does not discern between the two.

last two work incorrect, but
ls -la | egrep '^.*\.$' | wc -l
ls -la | egrep '^d.*\.$' | wc -l
return 2
ls -la | egrep '^.*\.$'
ls -la | egrep '^d.*\.$'
return
drwxr-xr-x 7 root root 4096 date time .
drwxr-xr-x 31 root root 4096 date time ..
variant:
secret files:
ls -la | grep '^-' |awk '{print $9}' |egrep '^\.[^\.]' |wc -l
secret dirs:
ls -la | grep '^d' |awk '{print $9}' |egrep '^\.[^\.]' |wc -l

The regex doesn't work, because '^.*\.$' matches the dot at the end of the line. Use these commands instead to count hidden files and directories:
ls -ld .* | egrep '^-' | wc -l
ls -ld .* | egrep '^d' | wc -l
Note that egrep '^d' matches . and .. too, so you need subtract 2 from the result:
ls -ld .* | egrep '^d' | wc -l | awk '{print $1 - 2}'
Alternatives:
ls -ld .* | egrep '^d' | tail -n +3 | wc -l
echo $(($(ls -ld .* | egrep '^d' | wc -l) - 2))

find $THE_DIRECTORY -maxdepth 1 -type f -name '.*' | wc --lines
Should work, you may want to use -L if you want to find symlinks as well.

Related

I want my script to echo "$1" into a file literally

This is part of my script
#!/bin/bash
echo "ls /SomeFolder | grep $1 | xargs cat | grep something | grep .txt | awk '{print $2}' | sed 's/;$//';" >> script2.sh
This echos everything nicely into my script except $1 and $2. Instead of that it outputs the input of those variables but i want it to literally read "$1" and "$2". Help?
Escape it:
echo "ls /SomeFolder | grep \$1 | xargs cat | grep something | grep .txt | awk '{print \$2}' | sed 's/;\$//';" >> script2.sh
Quote it:
echo "ls /SomeFolder | grep "'$'"1 | xargs cat | grep something | grep .txt | awk '{print "'$'"2}' | sed 's/;"'$'"//';" >> script2.sh
or like this:
echo 'ls /SomeFolder | grep $1 | xargs cat | grep something | grep .txt | awk '\''{print $2}'\'' | sed '\''s/;$//'\'';' >> script2.sh
Use quoted here document:
cat << 'EOF' >> script2.sh
ls /SomeFolder | grep $1 | xargs cat | grep something | grep .txt | awk '{print $2}' | sed 's/;$//';
EOF
Basically you want to prevent expansion, ie. take the string literaly. You may want to read bashfaq quotes
First, you'd never write this (see https://mywiki.wooledge.org/ParsingLs, http://porkmail.org/era/unix/award.html and you don't need greps+seds+pipes when you're using awk):
ls /SomeFolder | grep $1 | xargs cat | grep something | grep .txt | awk '{print $2}' | sed 's/;$//'`
you'd write this instead:
find /SomeFolder -mindepth 1 -maxdepth 1 -type f -name "*$1*" -exec \
awk '/something/ && /.txt/{sub(/;$/,"",$2); print $2}' {} +
or if you prefer using print | xargs instead of -exec:
find /SomeFolder -mindepth 1 -maxdepth 1 -type f -name "*$1*" -print0 |
xargs -0 awk '/something/ && /.txt/{sub(/;$/,"",$2); print $2}'
and now to append that script to a file would be:
cat <<'EOF' >> script2.sh
find /SomeFolder -mindepth 1 -maxdepth 1 -type f -name "*$1*" -print0 |
xargs -0 awk '/something/ && /.txt/{sub(/;$/,"",$2); print $2}'
EOF
Btw, if you want the . in .txt to be treated literally instead of as a regexp metachar meaning "any character" then you should be using \.txt instead of .txt.

Bash script getting error in files

Hi Guys pls help on this...
[root#uenbe1 ~]# cat test.sh
#!/bin/bash
cd /vol/cdr/MCA
no='106'
value='55'
size=`df -kh | grep '/vol/cdr/MCA' | awk '{print $5}'| sed 's/%//g'`
if [ "$size" -gt "$value" ] ;
then
delete=$(($size-$value))
echo $delete
count=$(($no*$delete))
`ls -lrth | head -n $count | xargs rm -rf`
fi
output:
+ cd /vol/cdr/MCA
+ no=106
+ value=55
++ df -kh
++ grep /vol/cdr/MCA
++ awk '{print $5}'
++ sed s/%//g
+ size=63
+ '[' 63 -gt 55 ']'
+ delete=8
+ echo 8
8
+ count=848
++ ls -lrth
++ head -n 848
++ xargs rm -rf
rm: invalid option -- 'w'
Try `rm --help' for more information.``
i want to delete these files which in $count.
The command ls -lrth prints lines like:
-rw-r--r-- 1 bize bize 0 may 22 19:54 text.txt
-rw-r--r-- 1 bize bize 0 may 22 19:54 manual.pdf
that text given to the command rm will be interpreted as options
$ rm -rw-r text.txt
rm: invalid option -- 'w'
List only the name of files. That is: remove the long -l option from ls (and the -h option since it works only with -l):
$ ls -1rt | head -n "$count" | xargs
But Please: do not make a rm -rf automatic, that is a sure path to future problems.
Maybe?:
$ ls -1rt | head -n "$count" | xargs -I{} echo rm -rf /vol/cdr/MCA/'{}' \;
why are you passing
ls -l
use just, it will find the list of file greater than given size,
if you get this list in a file you can then take list of files which are to be deleted or whatever
find /vol/cdr/MCA -type f -size +56320c -exec ls '{}' \;
> `ls -lrth | head -n $count | xargs rm -rf`
This line has multiple problems. The backticks are superfluous, and you are passing the directory permission, file size, owner information etc as if that were part of the actual file name.
The minimal fix is to lose the backticks and the -l option to ls (and incidentally, the -r option to rm looks misplaced, too); but really, a proper solution would not use ls here at all.

How to measure the depth of a file system path?

I'm looking for a way to do this on the command line, since this is not too hard a task in Java or Python.
Something like:
$ measure_depth /a/b/c/d/e/f
6
$ measure_depth /a
1
This question is functionally equivalent to "is there an easy way to count the number of slashes in a filename?"
Define a measure_depth function:
measure_depth() { echo "${*#/}" | awk -F/ '{print NF}'; }
Then, use it as follows:
$ measure_depth /a/b/c/d/e/f
6
$ measure_depth /a
1
You can do something like
tr -s "/" "\n" | wc -l
which gives you an extra one, so a "hacky" way around it would be
sed "s/^\///" | tr -s "/" "\n" | wc -l
echo "/a/b/c/d/e/f" | sed "s/^\///" | tr -s "/" "\n" | wc -l
6
Use realpath before counting the slashes to avoid overestimations as e.g. /home/user/../user/../user/../user/dir/ would be translated to /home/user/dir.
realpath <dir> | grep -o '/' | wc -l

How to print number of occurances of a word in a file in unix

This is my shell script.
Given a directory, and a word, search the directory and print the absolute path of the file that has the maximum occurrences of the word and also print the number of occurrences.
I have written the following script
#!/bin/bash
if [[ -n $(find / -type d -name $1 2> /dev/null) ]]
then
echo "Directory exists"
x=` echo " $(find / -type d -name $1 2> /dev/null)"`
echo "$x"
cd $x
y=$(find . -type f | xargs grep -c $2 | grep -v ":0"| grep -o '[^/]*$' | sort -t: -k2,1 -n -r )
echo "$y"
else
echo "Directory does does not exists"
fi
result: scriptname directoryname word
output: /somedirectory/vtb/wordsearch : 4
/foo/bar: 3
Is there any option to replace xargs grep -c $2 ? Because grep -c prints the count=number of lines which contains the word but i need to print the exact occurrence of a word in the files in a given directory
Using grep's -c count feature:
grep -c "SEARCH" /path/to/files* | sort -r -t : -k 2 | head -n 1
The grep command will output each file in a /path/name:count format, the sort will numerically (-n) sort by the 2nd (-k 2) field as delimited by a colon (-t :) in reverse order (-r). We then use head to keep the first result (-n 1).
Try This:
grep -o -w 'foo' bar.txt | wc -w
OR
grep -o -w 'word' /path/to/file/ | wc -w
grep -Fwor "$word" "$dir" | sed "s/:${word}\$//" | sort | uniq -c | sort -n | tail -1

Bash scripting: Deleting the oldest directory

I want to look for the oldest directory (inside a directory), and delete it. I am using the following:
rm -R $(ls -1t | tail -1)
ls -1t | tail -1 does indeed gives me the oldest directory, the the problem is that it is not deleting the directory, and that it also list files.
How could I please fix that?
rm -R "$(find . -maxdepth 1 -type d -printf '%T#\t%p\n' | sort -r | tail -n 1 | sed 's/[0-9]*\.[0-9]*\t//')"
This works also with directory whose name contains spaces, tabs or starts with a "-".
This is not pretty but it works:
rm -R $(ls -lt | grep '^d' | tail -1 | tr " " "\n" | tail -1)
rm -R $(ls -tl | grep '^d' | tail -1 | cut -d' ' -f8)
find directory_name -type d -printf "%TY%Tm%Td%TH%TM%TS %p\n" | sort -nr | tail -1 | cut -d" " -f2 | xargs -n1 echo rm -Rf
You should remove the echo before the rm if it produces the right results

Resources