7

Problem

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

Example

For example (from running acpi):

Battery 0: Discharging, 37%, 01:33:20 remaining

How can I extract the percentage after discharge? In this case it would be 37%...

Notes

I am looking for a simple solution that does not require a bunch of command arguments.

9

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/'
| improve this answer | |
  • If you could explain the awk example that would be great. I was planning on just messing around with it, but an explanation would be better. – NerdOfCode Jan 4 '18 at 17:18
  • The only thing I do not understand is the brackets needed in the ending statement --> {} from --> '{print$2}' – NerdOfCode Jan 4 '18 at 17:34
  • @NerdOfCode That's because it's an action statement, see PATTERNS AND ACTIONS in man awk. – dessert Jan 4 '18 at 17:44
13

Here are a few:

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

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

| improve this answer | |
  • Wow, this is a really detailed answer with great explanations, thanks, but I'm afraid @dessert already beat you to it... – NerdOfCode Jan 4 '18 at 19:25
  • Matching three characters (as in (...)) might be inappropriate when the percentage is below 10. Or 100. – PerlDuck Jan 4 '18 at 19:33
  • 1
    @PerlDuck good point! edited :) – Zanna Jan 4 '18 at 19:40
6

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%".

| improve this answer | |
3

You can also use substring extraction with parameter expansion

input=$(acpi)
expr "${input#*,[[:space:]]}" : '\([^,]*\)'
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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