How to get the total number of files in 3 directories? - shell

How to write a single-line command line invocation that counts the total number of files in the directories /usr/bin, /bin and /usr/doc ?
So far, what I can think of is to use
cd /usr/bin&&ls -l | wc -l
but I don't know how to add them together, something like:
(cd /usr/bin&&ls -l | wc -l) + (cd /bin&&ls -l | wc -l)
Maybe there is a better way to do it, like get all the stdout of each directory, then pipe to wc -l
Any idea?

how about using find command + wc -l?
find /usr/bin /bin /usr/doc -type f |wc -l

Use ls for multiple directories in conjunction with wc is a little more succinct:
ls /usr/bin /bin /usr/doc | wc -l

Assuming bash or similarly capable shell, you can use an array:
files=(/usr/bin/* /bin/* /usr/doc*)
num=${#files[#]}
This technique will correctly handle filenames that contain newlines.

As Kent points out, find may be preferred as it will ignore directory entries. Tweak it if you want symbolic links.
A -maxdepth, if your find supports it, is needed unless you want to recurse into any unexpected directories therein. Also throwing away stderr in case a directory is not present for some odd reason.
find /usr/bin /bin /usr/doc -maxdepth 1 -type f 2>/dev/null | wc -l

Related

How do I add numbers from result?

I want to make a bash script that counts how many files there are in specific folders.
Example:
#!/usr/bin/env bash
sum=0
find /home/user/Downloads | wc -l
find /home/user/Documents | wc -l
find /home/user/videos | wc -l
echo "files are $sum "
Downloads folder has 5 files, Documents has 10 files and videos has 10 files.
I want to add all files from above directories and print the number of files.
echo "files are $sum "
Please I would like to use "only" find command, because my script delete some files. My goal is how many files I deleted.
Anything that's piping to wc -l isn't counting how many files you have, it's counting how many newlines are present, and so it'll fail if any of your file names contain newlines (a very real possibility, especially given the specific directories you're searching). You could do this instead using GNU tools:
find \
/home/user/Downloads \
/home/user/Documents \
/home/user/videos \
-print0 |
awk -v RS='\0' 'END{print NR}'
I'm not sure about this, but you can try this code.
#!/usr/bin/env bash
sum=0
downloads=$(find /home/user/Downloads | wc -l)
((sum += downloads))
documents=$(find /home/user/Documents | wc -l)
((sum += documents))
videos=$(find /home/user/videos | wc -l)
((sum += videos))
echo "files are $sum "
You can save the individual results and sum them as as per FS-GSW's answer.
That's what I would do in Java or C, but shell scripts have their own patterns. Whenever you find yourself with lots of variables or explicit looping, take a step back. That imperative style is not super idiomatic in shell scripts.
Can you pass multiple file names to the same command?
Can that loop become a pipe?
In this case, I would pass all three directories to a single find command. Then you only need to call wc -l once on the combined listing:
find /home/user/Downloads /home/user/Documents /home/user/videos | wc -l
Or using tilde and brace expansion:
find ~user/{Downloads,Documents,videos} | wc -l
And if the list of files is getting long you could store it in an array:
files=(
/home/user/Downloads
/home/user/Documents
/home/user/videos
)
find "${files[#]}" | wc -l
Just use ..
find /home/user/ -type f | wc -l
.. to count the files inside the user directory recursively.
With tree you could also find/count (or whatever else you want to do) hidden files.
e.g. tree -a /home/user/ - for which the output would be: XXX directories, XXX files - which then wouldn't apply to your question though.

using "wc -l" on script counts more than using on terminal

I'm making a bash script and it's like this:
#!/bin/bash
DNUM=$(ls -lAR / 2> /dev/null | grep '^d' | wc -l)
echo there are $DNUM directories.
the problem is, that when I run this line directly on the terminal:
ls -lAR / 2> /dev/null | grep '^d' | wc -l
I get a number.
But when I run the script it displays me a greater number, like 30 to 50 more.
What is the problem here?
Why is the "wc" command counting more lines when running it from a script?
You may have different directory roots for the two runs. Instead of ls to find the directories only you can use this
find parent_directory -type d
and pipe to wc -l to count.
The /proc directory will have processes and treated as directories and will change from run to run. To exclude it from the count use
find / -path /proc -prune -o -type d | wc -l
To find the differences in your exact case I would suggest to run
#!/bin/bash
for r in 1 2; do
ls -lAR / 2> /dev/null | grep '^d' > run${r}.txt 1> out${r}.txt
done
diff -Nura out1.txt out2.txt
rm -f out1.txt out2.txt
But as the most ppl. already said it would make sense to exclude directories like sys,proc ...

Counting directories on UNIX

I need a bash script that can count directories that are inside other directories on a FreeBSD.
The case is like this:
The path is home/myuser/direct than inside this directory there are like 20 directories named only by one letter like A B C D E F and so on. Inside every A directory, B directory there are many other directories with different names, such as mydirectory1, mydirectory2 and so on. Inside mydirectory1 there are differnet files and directories, and I need to count only the directories that are under the mydirectory1, and not the files. I came up with this, but using this, I will have to do that manually for each directory:
home/myuser/direct# ls -l A/* | grep ^d | wc -l
than for the B directory I will have to:
home/myuser/direct# ls -l B/* | grep ^d | wc -l
and so on. Is there a way, to automatically do this, I mean change the letter A to B and so on?
P.S, sorry about the confusion as English is not my first language :(
This solution assumes you want the number of subfolders for each folder in the current directory. If you want to sum them all up into one value, that is a different question... It is not incredibly robust to variations in folder names, but should work for most cases when there is not strange punctuation:
for D in */; do echo "$D": $(ls -d "$D"*/| wc -l); done
Example output:
DATA/: 14
LOGS/: 2
PLOTS/: 3
SCRIPTS/: 2
ls: libraries//*/: No such file or directory
libraries/: 0
Here is a version which suppresses the error for empty folders:
for D in */; do echo "$D": $(ls -d "$D"*/ 2>/dev/null |wc -l); done
Try
find * -type d -print | wc -l

Finding all commands excluding "."

So far I have this:
ls /usr/bin | grep "^[\.]"
The cmd still gets files with a "." in there.
I have looked at [[:punct:]] but still returns the same thing.
There's grep -v to exclude things. So try
ls /usr/bin | grep -v \\.
man grep says
-v, --invert-match
Selected lines are those not matching any of the specified patterns.
It's generally considered a bad idea to parse ls.
If I understand you correctly, you want all files in /usr/bin that don't have a dot in the name. You can use find to do that:
find /usr/bin -not -name "*.*"
It is more portable (thanks #Adrian) to use a ! instead of -not:
find /usr/bin ! -name "*.*"
Not really clear, what you want:
your command:
ls /usr/bin | grep "^[\.]"
mean, filter the output from ls to show only files, what are start with a dot.
grep "^[\.]"
^ ^^ - escaped dot
+- at the begining of the line
If you want, exclude all files what contains dot, use
ls /usr/bin | grep -v '\.' #or see HenrikN's answer and comments (grep -vF .)
it you want exclude only entries what are starting with dot, use
grep '^[^\.]'
whats mean anything, but dot at the start
Ps: anyway, parsing output form ls is usually an very bad idea. (http://mywiki.wooledge.org/ParsingLs)
You can change your regex to exclude files starting with ".":
ls -a /usr/bin | grep "^[^.]"
This regex selects only files which do not have "." at the start. By the way only ls -a shows files that starts with ".". How did you manage to get them without "-a" ?
This can be achieved with pure bash, if the extglob shell option is enabled.
shopt -s extglob
echo /usr/bin/!(*.*)
# or alternatively:
echo /usr/bin/+([!.])
You may replace echo with ls -d if you want to pipe the list to another command line-wise.
I think you are referring to the current working directory and parent dirctory and not a command with "a dot" in it.
Try this as you probably have ls aliased:
/bin/ls /usr/bin

Get file path with find without giving filename

How can I get the path of a .pid file that is inside a directory.
the code below returns only the file
root#linux [/]# ls -l $(find /* -name dovecot | grep var/run) | grep pid
-rw------- 1 root root 5 Nov 28 15:22 master.pid
Guess this is what you are looking for:
find /var/run -name "*.pid" 2>/dev/null | grep dovecot | xargs ls -l
You can also narrow the matches down in the grep command when you specify (parts of) the path inside the filter expression.
I think the interpretation of the output must be that the find command finds a directory name such as:
/var/run/dovecot
and you do an ls -l on the directory, which lists the files in the directory without any path leading to it. What you need is to find a reliable way of listing the files in the directory with their full path names.
One way — not I think a good way — of doing it would be:
find $(find /* -name dovecot -type d | grep var/run) -type f -name '*.pid' \
-exec ls -l {} +
This uses your first find command to get the directories you're interested in, then runs find again to find .pid files and execs ls -l on them. The + notation means that find behaves a bit like xargs, bunching a lot of file names together into a single run of ls -l.
cat /var/run/dovecot/master.pid
?
Or :
# readlink -f /var/run/dovecot/*.pid
/var/run/dovecot/master.pid

Resources