573

I'm trying to find all files with a specific extension in a directory and its subdirectories with my Bash (Ubuntu 10.04 LTS (Lucid Lynx) release).

This is what's written in a script file:

#!/bin/bash

directory="/home/flip/Desktop"
suffix="in"

browsefolders ()
  for i in "$1"/*;
  do
    echo "dir :$directory"
    echo "filename: $i"
    #    echo ${i#*.}
    extension=`echo "$i" | cut -d'.' -f2`
    echo "Erweiterung $extension"
    if     [ -f "$i" ]; then

        if [ $extension == $suffix ]; then
            echo "$i ends with $in"

        else
            echo "$i does NOT end with $in"
        fi
    elif [ -d "$i" ]; then
    browsefolders "$i"
    fi
  done
}
browsefolders  "$directory"

Unfortunately, when I start this script in a terminal, it says:

[: 29: in: unexpected operator

(with $extension instead of 'in')

What's going on here, and where's the error? But this curly brace.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
flip
  • 5,739
  • 3
  • 15
  • 3

10 Answers10

947
find "$directory" -type f -name "*.in"

is a bit shorter than that whole thing (and safer. It deals with whitespace in filenames and directory names).

Your script is probably failing for entries that don't have a . in their name, making $extension empty.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mat
  • 202,337
  • 40
  • 393
  • 406
  • 22
    yes, `find` is recursive by default. you can limit the depths if you want (see the man page). – Mat May 08 '11 at 12:27
  • 1
    I'd like to pass all found files as arguments to a jar-file. How can this be performed? – flip May 08 '11 at 12:31
  • 8
    @flip: that's a different question. Post a new question, detailing exactly what you'd like to do and what you've tried so far. – Mat May 08 '11 at 12:33
  • 1
    One little correction: use '\*.in' or \\*.in instead of "*.in" because double quotes don't prevent shell expansion. I.e. your script will not work properly if there's a file with .in extension in the current directory. – Shnatsel Apr 19 '13 at 09:23
  • 5
    @Shnatsel: double quotes do prevent shell expansion. Try it out. – Mat Apr 19 '13 at 12:25
  • To follow symbolic links, add `-L` before the path specification, such as: `find -L $directory -type f -name "*.in"` – Abdull Dec 26 '13 at 19:55
  • Thanks..I would like to know if there is way to list both *.in and *.ini files .. i.e. can we search for multiple extentions.. Also I don't get why ' ' or " " need to be used.. otherwise it says -- no match – Chandan Choudhury Oct 27 '14 at 07:57
  • @ChandanChoudhury: `find . -type f -a \( -name '*.in' -o -name '*.ini' \)` ; you need quotes to prevent shell globbing. – Mat Oct 27 '14 at 08:38
  • Works nicely. Can I somehow tell it to search in multiple paths? or should I call it for each path separately? – android developer May 05 '15 at 18:12
  • @androiddeveloper: `man find` is your friend. Put all your paths in the command, works just fine. – Mat May 06 '15 at 05:37
  • @Mat I don't have Linux. I want to try it on Android, but maybe "man" will work there too. What should be the delimiter between the paths? – android developer May 06 '15 at 06:56
  • `man` is the command you use to read the docs. You can also type that command in google's search box, works just fine :-) – Mat May 06 '15 at 06:57
  • @Mat Is it this one: http://unixhelp.ed.ac.uk/CGI/man-cgi?find ? seems like a lot of options... – android developer May 06 '15 at 09:58
272
find {directory} -type f -name '*.extension'

Example: To find all csv files in the current directory and its sub-directories, use:

find . -type f -name '*.csv'
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mohammad AlQanneh
  • 3,227
  • 1
  • 16
  • 14
64

The syntax I use is a bit different than what Mat suggested:

find $directory -type f -name \*.in

(it's one less keystroke).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Scott C Wilson
  • 19,102
  • 10
  • 61
  • 83
  • 1
    Matt's script also won't work if there's a file with .in extension in the current directory, while yours would still work. See http://stackoverflow.com/questions/5927369/recursively-look-for-files-with-a-specific-extension#comment22990599_5927391 – Shnatsel Apr 19 '13 at 09:25
  • 4
    @Shnatsel this comment (and hence yours) is plain wrong. – gniourf_gniourf Feb 19 '15 at 12:46
  • 1
    @gniourf_gniourf You should provide some reference for your statement, otherwise one could simply argue: "No, you are wrong". But in fact you're right: https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html – Murmel Jun 14 '16 at 08:46
  • @user1885518: I think that it should be the guy who claims that the script doesn't work who should provide some examples where the script fails. That's what I do when I leave comments where there are broken scripts: it's usually about quotes and filenames containing spaces, newlines, globs, etc., and I specifically explain why it's broken. – gniourf_gniourf Jun 14 '16 at 09:05
  • 2
    Providing reference is always a good way in a discussion, it does not depend on who was the first. He should, you should. – Murmel Jun 14 '16 at 11:22
  • @user1885518: this guy clearly doesn't know anything about Bash and claims the stupidest things. Instead of asking, he just states facts that are obviously wrong (instead of asking). It's just like if I went on a sewing Q&A site and claimed something stupid about needles or threads or buttons or fabric (I don't know anything about sewing). People would just tell me to go away and learn basic stuff about sewing. And they would probably say that in a somehow aggressive way. Now please leave me alone and stop telling me what I should do; I won't bother reading anything about this anymore. – gniourf_gniourf Jun 14 '16 at 13:30
15

Without using find:

du -a $directory | awk '{print $2}' | grep '\.in$'
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
rtrn
  • 153
  • 1
  • 5
  • 4
    The `grep` isn't really necessary here. `awk` has regular expressions and could limit its output to values matching a pattern. – Kenster Mar 05 '16 at 17:57
  • 1
    This method is extremely useful if your going through 100s of terabyte. Find command takes too much time to process. This starts immediately. – Protonova Feb 11 '17 at 00:59
  • 1
    `awk|grep` is an anti-pattern. Let awk do the grepping. – Jens Jun 30 '17 at 16:50
13

Though using find command can be useful here, the shell itself provides options to achieve this requirement without any third party tools. The bash shell provides an extended glob support option using which you can get the file names under recursive paths that match with the extensions you want.

The extended option is extglob which needs to be set using the shopt option as below. The options are enabled with the -s support and disabled with he -u flag. Additionally you could use couple of options more i.e. nullglob in which an unmatched glob is swept away entirely, replaced with a set of zero words. And globstar that allows to recurse through all the directories

shopt -s extglob nullglob globstar

Now all you need to do is form the glob expression to include the files of a certain extension which you can do as below. We use an array to populate the glob results because when quoted properly and expanded, the filenames with special characters would remain intact and not get broken due to word-splitting by the shell.

For example to list all the *.csv files in the recursive paths

fileList=(**/*.csv)

The option ** is to recurse through the sub-folders and *.csv is glob expansion to include any file of the extensions mentioned. Now for printing the actual files, just do

printf '%s\n' "${fileList[@]}"

Using an array and doing a proper quoted expansion is the right way when used in shell scripts, but for interactive use, you could simply use ls with the glob expression as

ls -1 -- **/*.csv

This could very well be expanded to match multiple files i.e. file ending with multiple extension (i.e. similar to adding multiple flags in find command). For example consider a case of needing to get all recursive image files i.e. of extensions *.gif, *.png and *.jpg, all you need to is

ls -1 -- **/+(*.jpg|*.gif|*.png)

This could very well be expanded to have negate results also. With the same syntax, one could use the results of the glob to exclude files of certain type. Assume you want to exclude file names with the extensions above, you could do

excludeResults=()
excludeResults=(**/!(*.jpg|*.gif|*.png))
printf '%s\n' "${excludeResults[@]}"

The construct !() is a negate operation to not include any of the file extensions listed inside and | is an alternation operator just as used in the Extended Regular Expressions library to do an OR match of the globs.

Note that these extended glob support is not available in the POSIX bourne shell and its purely specific to recent versions of bash. So if your are considering portability of the scripts running across POSIX and bash shells, this option wouldn't be right.

Inian
  • 80,270
  • 14
  • 142
  • 161
13

Use:

find "$PWD" -type f -name "*.in"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
kip2
  • 6,473
  • 4
  • 55
  • 72
10
  1. There's a { missing after browsefolders ()
  2. All $in should be $suffix
  3. The line with cut gets you only the middle part of front.middle.extension. You should read up your shell manual on ${varname%%pattern} and friends.

I assume you do this as an exercise in shell scripting, otherwise the find solution already proposed is the way to go.

To check for proper shell syntax, without running a script, use sh -n scriptname.

Jens
  • 69,818
  • 15
  • 125
  • 179
7

To find all the pom.xml files in your current directory and print them, you can use:

find . -name 'pom.xml' -print
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Bharat Yadav
  • 79
  • 1
  • 1
0

Use:

find $directory -type f -name "*.in" | grep $substring
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sergiu
  • 9
  • 1
0
for file in "${LOCATION_VAR}"/*.zip
do
  echo "$file"
done 
rollstuhlfahrer
  • 3,988
  • 9
  • 25
  • 38
  • 3
    While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. – rollstuhlfahrer Apr 10 '18 at 06:56