Hedge
Hedge

Reputation: 16766

Function to process input from pipe

I'm trying to write a bash-function to return a specific line of piped output by its number. Currently the full command looks like this:

mdfind 'my_search_string' | sed "2q;d"

That would return the 2nd line of output from the mdfind-command.

I'm trying to turn sed "$1q;d" into a function that gets assigned as an alias.

How can I process the piped input?

Upvotes: 4

Views: 6917

Answers (4)

William Pursell
William Pursell

Reputation: 212664

You just need to protect the variable name:

To define a function named search:

search() { ... | sed -n "${1}p;q"; }

but note that this is not portable sed. It would be better to do

search() { ... | sed -n "${1}{p; q;}"; }

Indeed, the original sed is not just non-portable, but does not do what you want! It should always quit after processing line 1. If you have a sed that behaves as you describe in the question statement, you might want to double check!

Upvotes: 2

konsolebox
konsolebox

Reputation: 75618

To return the second line of output, do this:

... | sed -ne 2p

And to use it as a function:

function print_2nd_line {
    sed -ne 2p
}

mdfind 'my_search_string' | print_2nd_line

You could also choose shorter names like p2 at your option.

The function can also be customized to be capable of printing second line from specified files like:

function print_2nd_line {
    sed -ne 2p -- "$@"
}

print_2nd_line file
... | print_2nd_line  ## Still could be used like this.

By the way the more efficient version would be

sed -ne '2{p;q}'

UPDATE

As suggested by Charles Duffy, you could also use this format for POSIX compatibility. Actually it's also compatible with all shells based from the original System V sh.

print_2nd_line() {        
    sed -ne '2{p;q}' -- "$@"
}

Also, if you want to pass a custom line number to your function, you could have:

print_2nd_line() {
    N=$1; shift
    sed -ne "${N}{p;q}" -- "$@"
}

Where you can run it as:

... | print_2nd_line 2  ## Still could be used like this.

Or

print_2nd_line 2 file

Upvotes: 7

Charles Duffy
Charles Duffy

Reputation: 295954

As an exercise -- this version is in pure bash, not using any external tools at all:

return_line() {
  local lineno=$1
  while (( lineno > 1 )); do read; (( lineno-- )); done
  read -r && printf '%s\n' "$REPLY"
}

# ...to test...
return_line 3 <<<$'one\ntwo\nthree\nfour'

# ...and yes, this works with pipelines...
printf 'one\ntwo\nthree\nfour' | return_line 3

The pure-bash version will typically be faster with short input streams (or when picking an early line from a long stream), but slower with long ones.

Upvotes: 0

rook
rook

Reputation: 6270

My awked bash alias:

alias second="awk 'FNR==2'"

cat file | second

or

second file

So, I don't understand properly, do you want to write a function which gets number of line to filter as parameter? And also you want to process piped input in it to filter out that line? Your piped input will be arguments for your function and you could get all of them by $@.

I'm sorry, I understand what the OP wanted :)
As I got it, he wants processing std input by shell function with parameters, i.e.

cmd | func args

So the following works:

function sift { awk "FNR==$1" ;}

Example:

cat file | sift 2

(See comment about awk vs sed)

Upvotes: 0

Related Questions