Romain Vincent
Romain Vincent

Reputation: 3541

Filter lines of file matching pattern and only print a part of it

I have personal files where I declare functions like so:

function funcname {
    # ...
}

I would like a command that outputs every funcname in the file, like so:

funcname1
funcname2
...

I don't understand why this command does not work:

cat filename | sed -ne '/^function.*{$/p' -e 's/function \([0-9a-zA-Z_]+\) {/(\1)/'

while this one does (it gives the one branch that is currently active):

git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1) /'

Right now I could use this instead:

cat filename | grep '^function' | cut -d' ' -f 2

But I would really like to understand what's wrong with the command above (the 1st one).

Upvotes: 0

Views: 789

Answers (1)

sjsam
sjsam

Reputation: 21955

Edited to incorporate the case mentioned in this comment.

Bash function definitions can be written with our without function keyword like below

# cat 48261735 
function testfun1
{
echo "testfun1"
}
testfun2()
{
echo "testfun2"
}
function testfun3{
echo "testfun3"
}

Script to get all function names

# sed -nE -- '/function/s/^.*function[[:blank:]]*([^[:blank:]{}]*).*$/\1/p;/\(\)/s/^[[:blank:]]*([^(]*).*$/\1/p' 48261735
testfun1
testfun2
testfun3

Now, what went wrong with your script.

To start with you did useless use of cat. The file can be directly fed into the script using -- to notify the sed that we are finished with the parameters. The -- is a workaround for the corner case that your filenames start with a -. Below is an example

sed 'your parameters and script here' -- filename

Then, your first expression

-e '/^function.*{$/p'

id out of place. Do you really wish to print stuff before you actually make the substitution? Instead you could add a p flag after the s command to tell sed that whatever is the result of the substitution should be print to the stdout

Now, coming to the substitution part, you are not accounting the multiple spaces that can come before and after the function keyword and function name.

More importantly, since you're not using the extended regular expression, you're missing the escape character before the + in your selection part. So you could come up with something like

sed -ne '/^[[:blank:]]*function.*/s/^[[:blank:]]*function[[:blank:]]*\([0-9a-zA-Z_]\+\)[[:blank:]]*{*/\1/p' -- filename
testfun1
testfun3

Note the p flag after the s command which overrides the -n option. Evenn then your script would miss the testfun2 because it is not accounting for the functon_name() format.

I suggest you use the extended regex option of sed ie -E which will help you avoid a lot of escape sequences.

Hope things are clear. :-)

Upvotes: 2

Related Questions