Skip to Content
EngineeringBash ScriptingUsing File Descriptors In Bash For Background Monitoring

Using File Descriptors In Bash For Background Monitoring

Suppose you want to write a script that interfaces with a serial port and allows you to run commands remotely on the device connected to the serial port.

One approach (incorrect) that you could take is to do it like this:

#!/bin/bash # Ensure proper usage if [ "$#" -lt 2 ]; then echo "Usage: $0 <serial_port> <command>" echo "Example: $0 /dev/ttyUSB0 'wifi status'" exit 1 fi # Arguments SERIAL_PORT="$1" shift; COMMAND="$@" # Serial port configuration BAUD_RATE=115200 # Modify based on your device's baud rate TIMEOUT=2 # Seconds to wait for response # Ensure the serial port exists if [ ! -e "$SERIAL_PORT" ]; then echo "Error: Serial port $SERIAL_PORT not found!" exit 1 fi # Open the serial port with proper settings stty -F "$SERIAL_PORT" $BAUD_RATE cs8 -cstopb -parenb -ixon -ixoff # Function to send a command and read response send_command() { local port="$1" local cmd="$2" # Send the command followed by newline echo "$cmd" > "$port" # Read the response from the serial port while IFS= read -r -t $TIMEOUT line < "$port"; do echo "$line" # Exit the loop if we encounter the device prompt [[ "$line" == "uart:*" ]] && break done } # Send the command and read the response send_command "$SERIAL_PORT" "$COMMAND"

We should now be able to execute a remote command and print its output and then exit the script like this:

$ ./serial.sh /dev/ttyUSB0 'wifi status'

This script is designed to stop and exit when it encounters Zephyr RTOS shell prompt. You may need to modify it yourself if your device responds differently.

However you will notice that the first lines of the output are missing. This is because the script is reading the output from the serial port too late.

What we want to do instead is to somehow open the serial port for both reading and writing first, then write the command and then read the response.

This is where file descriptors can help us. We modify our script as follows:

- # Send the command followed by newline - echo "$cmd" > "$port" + # Open file descriptor 3 for reading and writing + exec 3<> "$port" + # Write the command to the file descriptor + echo "$cmd" >&3 # Read the response from the serial port - while IFS= read -r -t $TIMEOUT line < "$port"; do + while IFS= read -r -t $TIMEOUT line <&3; do echo "$line"

What we do is open the file and assign it a file descriptor number 3 (since 0 to 2 are stdin, stdout, and stderr respectively). We then write the command to the file descriptor and then read the response from the same file descriptor.

Notice the use of exec when opening the file descriptor. Normally exec is a command that would replace the current shell process with another one, but when we use it with file descriptors, it opens the descriptor in the current shell process so that we can then access it in the rest of our script.

Now our script will not be skipping any lines. We would get both the echo of the command itself and the response.

Last updated on