skip to Main Content

bluetoothctl is an application that has a shell and accepts input commands. I would like to send command to it from the shell of the linux arbitrarily and I would also like to read its output without colours. I am on debian based linux (Raspbian buster) and would like to do it from bash shell if possible.

What I have tried so far:

Method one (sending commands via fd)

  • Run the process attached: $ bluetoothctl
  • Then from another terminal: $ echo "my command" > /proc/$(pidof bluetoothctl)/fd/0 and $ echo -e "my commandn" > /proc/$(pidof bluetoothctl)/fd/0 and $ echo -e "my command13" > /proc/$(pidof bluetoothctl)/fd/0 but my command just appears as an output of bluetoothctl and even pressing ‘enter’ key on my keyboard in the terminal of the program does not execute the command at all just gives a new line.

Method two (writing output to a text file)

  • Run the process detached: $ bluetoothctl > my.output &, but it exits from the application immediately

Method three (sending command via pipe)

  • Create a pipe mkfifo mypipe
  • Redirect the pipe to bluetoothctl cat mypipe | bluetoothctl > my.output
  • Write a command to the pipe echo "my command" > mypipe, but it closes the application immediately and the application has no time to process the command asynchronously. Hence, does not work.

I believe the feasible solution would be to start the application detached $ bluetoothctl & and then send command to it via /proc/$(pidof bluetoothctl)/fd/0 and read its stdout and stderr via /proc/$(pidof bluetoothctl)/fd/1 and /proc/$(pidof bluetoothctl)/fd/2 but I am totally lost.

One of the command I would like to execute and read it’s output is ‘scan on’ and ideally one bluetoothctl instance would process multiple commands not only one that the pipe can do.
Can someone please advise how a command can be sent via fd, and how the output can be read via the another fd of the process. All of this in bash shell, if possible.

2

Answers


  1. Chosen as BEST ANSWER

    For everyones benefit: Following the accepted answer, I have resolved my issue with coprocess, however, I have used the /proc/$(pidof bluetoothctl)/fd/0 to send commands because it better suited to my application due to some of its assynchronous nature. The output was then diverted to a file and the colours were removed with sed upon read.

    #!/bin/bash
    
    readOutput() {
        echo "$(cat $TEMP/bluetoothctl.out | sed 's/x1B[[0-9;]{1,}[A-Za-z]//g'"
    }
    
    executeCommandInExistingProcess() {
      if [ -z "$(pidof bluetoothctl)" ]
      then
        exit -1
      else
        echo "$1" >&/proc/$(pidof bluetoothctl)/fd/0
      fi
    }
    
    startParallelProcess() {
      output="$TEMP/bluetoothctl.out"
      
      if [ ! -z "$(pidof bluetoothctl)" ]
      then
        echo "Bluetooth process is running..."
        exit -1
      fi
      
      if [ -f "$output" ]
      then
        rm $output
      fi
    
      coproc bctl { bluetoothctl > $output; }
      [ "$#" -ge 1 -a ! -z "$1" ] && executeCommandInExistingProcess "$1"
    
      sleep 30
    
      if [ ! -z $(pidof bluetoothctl) ]
      then
        [ "$#" -ge 2 -a ! -z "$2" ] && executeCommandInExistingProcess "$2"
        executeCommandInExistingProcess "quit"
      fi
    }
    

  2. A coprocess is the appropriate tool for this job:

    #!/usr/bin/env bash
    case $BASH_VERSION in ''|[0-3].*|4.0*) echo "ERROR: bash 4.1+ required" >&2; exit 1;; esac
    
    coproc btctl { bluetoothctl; }
    
    echo "scan on" >&"${btctl[1]}"
    
    while IFS= read -r -u "${btctl[0]}" line; do
      echo "Read line from btctl: $line" >&2
    done
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search