Home > Mobile >  Bash - Reading from file descriptor simultaneously
Bash - Reading from file descriptor simultaneously

Time:03-08

I am working with Linux file descriptor at the moment to communicate with an external device via serial port, and I wonder if it is possible for me to duplicate the result returned the device.

In details, I used file descriptor number 5 to read and write to the serial port /dev/ttyACM0 in a raspbian device.

exec 5<>"/dev/ttyACM0"

In the background, I ran a function to continuously read from the port whenever a response was present to decide the next step.

function __processing__(){
   local line
   while read line<&5; do 
      ... some processing ...
      ... echo $line > "log.txt"
   done
}

And it has been working alright if I just sent single command to the serial port and let the background function to process the response. The issue appeared when I start working with other software to transfer data using XMODEM protocol, in the foreground.

sx -vv -k "./firmware_update.bin" >&5 <&5

The process kept timing out on ACK, despite the ACK responses recorded in the log file. So I suspected that it was because of the background process that read from FD5, leaving sx nothing. I then tried to remove the background process and run only the sx command, and my suspicion was confirmed when it successfully sent all data packages.

But then I don't have any recorded response from the serial port to actually know what to do next.

So my question is: Is there a way to rewrite the background function so that it can still read the response, without interfering with sx? I know that we can duplicate data from stdout using tee command, can I achieve something similar with FD5 instead?

Thank you all in advance.

CodePudding user response:

Try

sx -vv -k "./firmware_update.bin" >&5 < <(__processing__)

that uses Bash process substitution (see ProcessSubstitution - Greg's Wiki) to cause sx to take its input from the output of the __processing__ function. You'll also need to modify the function to copy its input (from FD5) to its standard output. Something along the lines of:

function __processing__
{
    local line
    while IFS= read -r line<&5; do 
        # ... some processing ...
        printf '%s\n' "$line" > "log.txt"
        printf '%s\n' "$line"    # Copy input to standard output
   done
}
  • That code is Shellcheck-clean.
  • See function BashFAQ/001 (How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?) for an explanation of the changes that I made on the while ... line.
  • See the accepted, and excellent, answer to Why is printf better than echo? for an explanation of why I replaced echo with printf.
  • Note that, even with the changes that I made, the __processing__ function won't work reliably if the data coming from FD5 is binary. ASCII NUL characters will be lost (Bash strings can't contain them), and it depends on chunks of input being terminated by newline characters. I don't know what kind of data you are dealing with in this case. It may not be something that can be handled by Bash code.
  • Related