Yaerox
Yaerox

Reputation: 678

Count files in a directory with filename matching a string

The command:

ls /some/path/some/dir/ | grep some_mask_*.txt | wc -l

returns the correct number of files when doing this via ssh on bash. When I put this into a .sh Script

iFiles=`ls /some/path/some/dir/ | grep some_mask_*.txt | wc -l`
echo "iFiles: ${iFiles}"

it is always 0. Whats wrong here?

Solution:
When I worked on it I found out that my "wildcard-mask" seems to be the problem. using grep some_mask_ | grep \.txt instead of the single grep above helped me to solve the problem for the first.

I marked the answer as solution which pretty much describes exactly what I made wrong. I'm going to edit my script now. Thanks everyone.

Upvotes: 6

Views: 32621

Answers (9)

Rishabh Agarwal
Rishabh Agarwal

Reputation: 2644

Simple solution is (for bash)

find -name "*pattern*" | wc -l
  • "*" represent anything (prefix- anything before , postfix - anything after)
  • wc -l : give the count
  • find -name : will find file with given name in double quotes

Upvotes: 5

I was writing a shell script to count the files of same format in a directory. For that I have used the below command

LOCATION=/home/students/run_date/FILENAME #stored the location in a variable
DIRECTORYCOUNT=$(find $LOCATION -type d -print | wc -l) using find command 
DIRECTORYCOUNT=$(find $LOCATION -type f -print | wc -l) 

I have used above commands and enter code here it worked well

Upvotes: -2

Martin
Martin

Reputation: 87

This is quite similar to other answers, but with a bit more robustness

iFiles=$( find /some/path/ -name "some_mask_*.txt" -type f 2> /dev/null | wc -l )
echo "Number of files: $iFiles"

This limits the find to files and also pipes stderr to null, so if the find command doesn't work or has permission issues you don't get a bogus result.

Upvotes: 0

Stefan Farestam
Stefan Farestam

Reputation: 4631

The problem here is that grep some_mask_*.txt is expanded by the shell and not by grep, so most likely you have a file in the directory where grep is executed which matches some_mask_*.txtand that filename is then used by grep as a filter.

If you want to ensure that the pattern is used by grep then you need to enclose it in single quotes. In addition you need to write the pattern as a regexp and not as a wildcard match (which bash uses for matching). Putting this together your command line version should be:

ls /some/path/some/dir/ | grep 'some_mask_.*\.txt' | wc -l

and the script:

iFiles=`ls /some/path/some/dir/ | grep 'some_mask_.*\.txt' | wc -l`
echo "iFiles: ${iFiles}"

Note that . needs to be prefixed with a backslash since it has special significance as a regexp that matches a single character.

I would also suggest that you postfix the regexp with $ in order to anchor it to the end (thus ensuring that the regexp matches filenames that ends with ".txt"):

ls /some/path/some/dir/ | grep 'some_mask_.*\.txt$' | wc -l

Upvotes: 8

hek2mgl
hek2mgl

Reputation: 158020

I suggest to use find as shown below. The reason for that is that filenames may contain newlines which would break a script that is using wc -l. I'm printing just a dot per filename and count the dots with wc -c:

find /some/path/some/dir/ -maxdepth 1 -name 'some_mask_*.txt' -printf '.' | wc -c

or if you want to write the results to variable:

ifiles=$(find /some/path/some/dir/ -maxdepth 1 -name 'some_mask_*.txt' -printf '.' | wc -c)

Upvotes: 4

fedorqui
fedorqui

Reputation: 289765

Parsing ls is not a good thing. If you want to find files, use find:

find /some/path/some/dir/ -maxdepth 1 -name "some_mask_*.txt" -print0

This will print those files matching the condition within that directory and without going into subdirectories. Using print0 prevents weird situations when the file name contains not common characters:

-print0
      True; print the full file name on the standard output,  followed
      by  a  null  character  (instead  of  the newline character that
      -print uses).  This allows file names that contain  newlines  or
      other  types  of white space to be correctly interpreted by pro‐
      grams that process the find output.  This option corresponds  to
      the -0 option of xargs.

Then, just pipe to wc -l to get the final count.


By the way, note that

ls /some/path/some/dir/ | grep some_mask_*.txt

can be reduced to a simple

ls /some/path/some/dir/some_mask_*.txt

Upvotes: 7

Jean-Baptiste Yunès
Jean-Baptiste Yunès

Reputation: 36401

Your problem is due to shell expansion. You probably tested the command line in the original directory, but if you try it from another directory then it will not work anymore.

When you type:

grep *.txt

then the shell replace *.txt with all the file names that correspond to the pattern and then execute the command (something like grep a.txt dummy.txt). But you want the pattern to be interpreted by grep not expanded by the shell, so:

ls /tmp | grep '.*.cpp'

wille make it. Here the pattern is in the syntax of grep command (each command as its own syntax) and not expanded because it is protected with surroundings '.

Modify your command like:

a=`ls /tmp | grep '.*.cpp'`

Upvotes: 0

Dorleeze
Dorleeze

Reputation: 103

I think there wouldn't be the shell version problem.

try to use escape char on your command. It likes below.

ls /some/path/some/dir/ | grep some_mask_\*.txt | wc -l

Upvotes: 1

toxic_boi_8041
toxic_boi_8041

Reputation: 1482

Try this,

iFiles=$(ls /some/path/some/dir/ | grep some_mask_*.txt | wc -l)
echo "iFiles: ${iFiles}"

Upvotes: 0

Related Questions