How to extract certain data from a line

Solution 1:

Here are a few:

$ acpi | grep -oP '\d+%'
99%
$ acpi | awk -F',' '{print $2}'
 99%
$ acpi | perl -pe 's/.*?(\d+%).*/$1/'
99%

Solution 2:

I'm not sure what you mean by saying “a solution in bash”, but awk can do the job:

awk -F", " '{print$2}'
  • -F", " – choose comma followed by space as the Field delimiter, this splits your example line in three columns where the second is 37%
  • '{print$2}' – print the second column

Or how about sed?

sed -E 's/.* ([0-9]+%).*/\1/'

Solution 3:

I was looking for a solution in bash that can extract particular information after a specified string.

After a specified string? Neither of the two answers previously posted do specifically that (because grabbing text after a string isn't the best way to get the information you said you wanted in your example).

Here are a couple of ways to get text after a string. I've used your example, although dessert's answer and terdon's answer both demonstrate more appropriate approaches to this particular case.

Use \K from Perl, for example in grep with -P (allow Perl regex) and -o (match only):

grep -Po 'string\Kdesired'

Where string is an expression matching whatever comes before what you want, and desired is an expression matching what you want to output. This is useful when the pattern you want occurs elsewhere in the file/line (for example, it's a number and the file/line contains other numbers). In your example, this could be something like:

$ acpi | grep -Po 'ing, \K[^,]+'
79%

[^,]+ means some characters that are not a comma, so this can grab text until a comma. We could also use ... to get any three characters, but as pointed out in a comment by PerlDuck, it's possible the pattern you want here will be more or less than 3 characters.

In sed, you can use capture groups with ( and ):

sed -r 's/.*string(desired).*/\1/' 

where \1 is what was saved with ( ). For your example:

$ acpi | sed -r 's/.*ing, ([^,]+).*/\1/'
89%

Here's a way to do it with only Bash on your example

$ output=$(acpi); string="${output#*ing, *}"; desired="${string%,*}"; echo "$desired"
96%

${var#string*} trims var before string (inclusive) and ${var%string*} trims var after string (inclusive).

This is by no means an exhaustive list. There are many ways to do this :)

Solution 4:

A bash solution, as requested, with no awkward awkisms or sedulous sedisms:

my_battery=( $(acpi) ); echo ${my_battery[3]}

This uses command substitution, makes an array of the command's output, and displays the 4th element of the array.

This works with the output of acpi, which seemingly always has the battery percentage as the 4th parameter. If you want to find the element of the array after "Discharging" you will not get a result if acpi tells you "Battery 0: Full, 100%".