user10328066
user10328066

Reputation:

Find all files ending in extension in bash

Write a bash script accepting two arguments: directory and extension. The script must print to stdout the path of all files in directory (and, recursively, its sub-directories) with a name ending with extension.

So far I have:

find {directory} -type f -name ' *.extension *

when I give it 2 args it doesn't seem to find anything.

Upvotes: 9

Views: 14516

Answers (3)

bballdave025
bballdave025

Reputation: 1428

In this answer, I'm just giving some explanation for Viktor's code.

This explanation might help those of you who are learning bash. Viktor updated his answer after I posted the "space issue" part and before I finished writing and saving this explanation. That's what I hoped for.

It is very concise, and there are great explanations. The original code was:

#!/bin/bash
#@filename: script.sh

find $1 -type f -name "*.$2"

It sounds like the question came from some kind of assignment, and I imagine that this original code might fulfill the assignment's expectations. It will probably also work with most programs you will write, which is especially important if the question did not come from an assignment.

I often develop on Windows or Cygwin, so I run into paths and filenames that have spaces. If I use @Viktor's code on Cygwin (note that the same behavior happens when I use Viktor's code on linux)

$ pwd
/home/bballdave025

#Write the program
$ vim script.sh

#view the program
$ cat script.sh
#!/bin/bash

find $1 -type f -name "*.$2"


#Create a path with spaces in one of the directory names
#Name it after Viktor, for the great answer
bballdave025@MYMACHINE ~
$ mkdir -p viktor/dir\ with\ spaces/directory

#put some css files in that directory
$ touch viktor/dir\ with\ spaces/directory/a.css

$ touch viktor/dir\ with\ spaces/directory/b.css

$ touch viktor/dir\ with\ spaces/directory/c.css

# run Viktor's code, using the well-written instructions
$ /bin/bash ./script.sh 'victor/dir with spaces/directory' css
find: ‘viktor/dir’: No such file or directory
find: ‘spaces/directory’: No such file or directory

Note that I used ./script.sh rather than plain script.sh, because I had not set script.sh to be executable. I can run it as Viktor shows if I first run

$ chmod +x script.sh

The problem comes from the way a bash shell and the find command handle spaces. With the first argument given simply as $1, the spaces aren't treated a special way. That's probably not the best way to describe things. Perhaps a better way of explaining this concept is to show that bash will assign the following - i.e. bash will use an unintended assignment pattern - when the script is written as above.

$0='script.sh'
$1='viktor/dir'
$2='with'
$3='spaces/directory'
$4='css'

[Note: what we want is to have things interpreted as]

$0='script.sh'
$1='viktor/dir with spaces/directory'
$2='css'

[and we will get to how to accomplish this, later.]

This unintended assignment pattern will happen even if we tell bash that our directory has spaces

$ /bin/bash script.sh viktor/dir\ with\ spaces/directory css
find: ‘viktor/dir’: No such file or directory
find: ‘spaces/directory’: No such file or directory

This can be seen by adding to the script the lines, echo "\$0 is $0" , echo "\$1 is $1" , echo "\$2 is $2" , and further with other lines having $(insert_number_here_but_no_parentheses), if you'd like.

So, how do we fix this problem? Let's look at the man page for find

$ man find

# (press 'q' to get out of the display of the man page)

Okay, that was kind of scary.

Let's cut the original synopsis line down to

find  [starting-point...] [expression]

What the DESCRIPTON means in its 'starting-point' explanation is that [starting-point...] is a directory (or directories) from which you'll start your search. For now, just take it at face value that 'expression' is something that restricts the search -- in your case, you look for only files and then further filter the results by specifying that they have a certain file-extension.

Let's get back to the find $1 -type f -name "*.$2" command. The main point to know here is that the command as shown here will be expanded to

find viktor/dir with spaces/directory -type f -name "*.css"

The "space problem" is that it will be interpreted by find and by the shell as follows

find victor/dir with spaces/directory -type f -name "*.css"
 |     \         |      \             `--------------------'
 |      \        |       \              /
 |       |       |        \       Another '[expression]' (2)
 |       |       |         -- Another '[starting-point]'
 |       |        -- '[expression]' (1)
 |        -- '[starting-point]' (supposedly a directory)
  -- Command name

Note that our '[expression]' (1) and '[expression]' (2) don't factor into what happens because the two '[starting-place]' paths fail.

What's now shown in Viktor's code

#!/bin/bash
#@filename viktor_script.sh

find "$1" -type f -name "*.$2"

Leads to

find "viktor/dir with spaces/directory" -type f -name "*.css"
`--' `--------------------------------' `-------------------'
 |               |                             /
 |               |             valid '[expression]' to define
 |               |             a "filter" for files outputted
 |               |
 |                -- '[starting-point]' (a real directory)
 |                    bash knows to include the spaces because
  -- command name                    of the quotation marks

Let's run it, first making sure it's executable

$ chmod +x viktor_script.sh

$ /bin/bash ./viktor_script.sh css

$ /bin/bash viktor_script.sh 'viktor/dir with spaces/directory' css
viktor/dir with spaces/directory/a.css
viktor/dir with spaces/directory/b.css
viktor/dir with spaces/directory/c.css

Upvotes: 5

John Humphreys
John Humphreys

Reputation: 39254

It doesn’t look like you are using arguments here.

You should replace extension and directory with $1 and $2 to use arguments 1 and 2.

Also, it looks like you forgot to close that quote... and the braces around the directory look wrong... I think you need quotes around the braces.

(Can’t test the result unfortunately at the minute, sorry).

Upvotes: 1

Victor Wong
Victor Wong

Reputation: 3761

This script should do the trick:

#!/bin/bash

find "$1" -type f -name "*.$2"

Examples to run the script:

/bin/bash script.sh /srv/directory css
/bin/bash script.sh '/srv/directory with space' css

Upvotes: 6

Related Questions