I am trying to write a script that allows me to change Bluetooth speaker volume from the shell. I can hard code the value of the sink and it works just fine:

#!/usr/bin/env bash
for level in "$@"
pactl set-sink-input-volume 267 $level

I get the "267" value by running:

pactl list sink-inputs

Which provides output like:

Sink Input #267
    Driver: protocol-native.c
    Owner Module: 10
    Client: 151
    Sink: 7
    Sample Specification: float32le 2ch 44100Hz
    Channel Map: front-left,front-right

All I care about is that three digit value in the first line of out put after Sink Input #

Like I said, if I hardcode that 267 value in the script, everything works fine until I restart the Bluetooth speaker, then the value changes.

Is there a way I can extract that value via the script and pass it as a variable to the pactl set-sink-input-volume 267 $level bit of the script?

  • Monospaced text blocks (like for code and terminal output) can be done in the question editor by selecting the block and pressing the {} button in the editor. – Chai T. Rex Sep 26 '18 at 17:03
  • You may find that the pactl output is easier to parse if you add the short modifier i.e. pactl list short sink-inputs – steeldriver Sep 26 '18 at 17:14

There are two ways to do it. This is all assuming that you want to pass one number to the script in the arguments and to change the volume of the first sink to the argument.

Your original suggested way (remove the echo after you verify it works):


pactl list sink-inputs | grep -hPo '^Sink Input #\K\d+$' | head -n 1 | xargs -n 1 -I{} echo pactl set-sink-input-volume "{}" "$1"

Remove the echo once you've verified that the commands that would be executed are correct.

Or, @steeldriver points out in the question's comments, there's a short form that's faster and easier to parse (again, remove the echo):


pactl list short sink-inputs | head -n 1 | awk '{print$1}' | xargs -n 1 -I{} echo pactl set-sink-input-volume "{}" "$1"

There probably is a better way to get the exact pulse audio ID you are after, but, since you asked for how to parse it within a bash script, here goes!

What you're requesting is to run another bash command and capture the output, like this:

sinkid=$(pactl list sink-inputs | egrep "Sink Input #" | egrep -o [0-9]+)

Broken down into pieces:

  • sinkid is a variable we create (take note that Bash is a language that does not tolerate spaces between x=y in variable assignments)
  • = tells bash we are assigning it's value to something
  • $( ... ) means we are running a command. You may see others use the grave/backtick character but some (myself included) argue this is clearer.
  • pactl list sink-inputs is the existing command you are running.
  • | says we are "piping" the output of that into another command
  • grep "Sink Input #" finds the line we are interested in
  • | again pipes the output so we can further process it using another grep command.
  • egrep -o [0-9]+ finds only the number from the line "Sink Input #123 outputting 123 only.

Now you can substitute in your original command the sinkid like this:

pactl set-sink-input-volume "$sinkid" "$level"
  • Thanks to everyone that helped! I used the original suggestion that @ChaiT.Rex provided and created this script: '#!/usr/bin/env bash for level in "$@" do pactl list sink-inputs | grep -hPo '^Sink Input #\K\d+$' | xargs -n 1 -I{} pactl set-sink-input-volume {} $level done' Works perfectly! – Robert Baker Sep 26 '18 at 17:31
  • sorry for the poor formatting - not sure how to markup in comments. – Robert Baker Sep 26 '18 at 17:37
  • @RobertBaker In comments, just use `s surrounding it. – Chai T. Rex Sep 26 '18 at 17:39
  • Now I need to figure out why it works so I can use this technique in other scripts. – Robert Baker Sep 26 '18 at 17:44
  • 1
    @KristopherIves I will study your hints carefully, thank you for the detailed response. – Robert Baker Sep 26 '18 at 18:03

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.