Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I've a command that outputs data to stdout (command1 -p=aaa -v=bbb -i=4). The output line can have the following value:

rate (10%) - name: value - 10Kbps

I want to grep that output in order to store that 'rate' (I guess pipe will be useful here). And finally, I would like that rate to be a value of a parameter on a second command (let's say command2 -t=${rate})

It looks to be tricky on my side; I would like to know better how to use pipes, grep, sed and so on.

I've tried lots of combinations like that one but I'm getting confused of these:

$ command1 -p=aaa -v=bbb -i=4 | grep "rate" 2>&1 command2 -t="rate was "${rate}
share|improve this question

5 Answers 5

You are confusing two very different types of inputs.

  1. Standard input (stdin)
  2. Command line arguments

These are different, and are useful for different purposes. Some commands can take input in both ways, but they typically use them differently. Take for example the wc command:

  1. Passing input by stdin:

    ls | wc -l
    

    This will count the lines in the output of ls

  2. Passing input by command line arguments:

    wc -l $(ls)
    

    This will count lines in the list of files printed by ls

Completely different things.

To answer your question, it sounds like you want to capture the rate from the output of the first command, and then use the rate as a command line argument for the second command. Here's one way to do that:

rate=$(command1 | sed -ne 's/^rate..\([0-9]*\)%.*/\1/p')
command2 -t "rate was $rate"

Explanation of the sed:

  • The s/pattern/replacement/ command is to replace some pattern
  • The pattern means: start with "rate" followed by any two character (..), followed by 0 or more digits, followed by a %, followed by the rest of the text (.*)
  • \1 in the replacement means the content of the first expression captured within \(...\), so in this case the digits before the % sign
  • The -n flag of the sed command means to not print lines by default. The p at the end of the s/// command means to print the line if there was a replacement. In short, the command will print something only if there was a match.
share|improve this answer

To simulate the output of command1 I am using this echo statement:

$ echo -e "Foo\nrate (10%) - name: value - 10Kbps\nBar"
$ alias command1='echo -e "Blah\nrate (10%) - name: value - 10Kbps\nBlag"'

A quick test:

$ command1
Blah
rate (10%) - name: value - 10Kbps
Blag

Thats all good, so lets parse it out:

$ command1 | grep 'rate'
rate (10%) - name: value - 10Kbps

So we get the line we want out of command1, lets pass that into command2:

$ alias command2='echo'
$ command2 -t="rate was "$(command1 | grep 'rate')
-t=rate was rate (10%) - name: value - 10Kbps

I'm expecting "rate was "$(command1 | grep 'rate') to concatenate automatically. If that doesn't work because of whitespace, you should be able to pass the input like so instead:

$ alias command2='echo'
$ command2 -t=$(echo '"rate was ' $(command1 | grep 'rate') '"')
-t="rate was rate (10%) - name: value - 10Kbps "
share|improve this answer

Try this using & :

command | grep -oP '\$>\s+rate\s+\(\K[^\)]+'
share|improve this answer

The first task is to extract the rate from that line. With GNU grep (non-embedded Linux or Cygwin), you can use the -o option. The part you want is the one containing only digits, and followed by a % sign. If you don't want to extract the % itself, you need an additional trick: a zero-width lookahead assertion, which matches nothing but only if that nothing is followed by %.

command1 -p=aaa -v=bbb -i=4 | grep -o -P '[0-9]+(?=%)'

Another possibility is to use sed. To extract a part of a line in sed, use the s command, with a regex that matches the whole line (starting with ^ and ending with $), with the part to retain in a group (\(…\)). Replace the whole line by the content of the group(s) to keep. In general, pass the -n option to turn off default printing and put the p modifier to print lines where there is something to extract (here there's a single line so it doesn't matter). See Return only the portion of a line after a matching pattern and Extracting a regex matched with 'sed' without printing the surrounding characters for more sed tricks.

command1 -p=aaa -v=bbb -i=4 | sed 's/^.*rate(\([0-9]*\)%).*$/\1/'

More flexible again than sed, is awk. Awk executes instructions for each line in a small imperative language. There are many ways to extract the rate here; I select the second fields (fields are delimited by whitespace by default), and remove all the characters in it that are not a digit.

command1 -p=aaa -v=bbb -i=4 | awk '{gsub(/[^0-9]+/, "", $2); print $2}'

The next step, now that you've extracted the rate, is to pass it as an argument to command2. The tool for that is a command susbtitution. If you put a command inside $(…) (dollar-parenthesis), its output is substituted into the command line. The output of the command is split into separate words at each whitespace block, and each word is treated as a wildcard pattern; unless you want this to happen, put double quotes around the command substitution: "$(…)". With the double quotes, the output of the command is used directly as a single parameter (the only transformation is that newlines at the end of the output are removed).

command2 -t "$(command1 -p=aaa -v=bbb -i=4 |
               sed 's/^.*rate(\([0-9]*\)%).*$/\1/')"
share|improve this answer

You can use grep and it's PCRE - Perl Compatible Regular Expressions. This allows you to utilize a lookbehind to match the value of rate, without inclusion of the string "rate" within your results when grepping for it.

Example

$ echo "rate (10%) - name: value - 10Kbps" | grep -oP '(?<=^rate \()\d+'
10

Details

The above grep works as follows:

  • -o will return only what we're looking for, \d+, which is the digits within the parens.
  • -P enables grep's PCRE feature
  • (?<=^rate \() will only return strings that begin with "rate ("

command2

To catch the value of "rate" you can run command1 like so:

$ rate=$(command 1 | grep -oP '(?<=^rate \()\d+'

Then for your 2nd command you'd simply use that variable.

$ command2 -t=${rate}

You could get fancy and make that all one line:

$ command2 -t=$(command1 | grep -oP '(?<=^rate \()\d+')

This will execute command1 inside the $(..) execution block, take its results and include them in command2's -t=.. switch.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.