Reputation:
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
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
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
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