I have tty device in /dev , where I send AT commands. I want to read line by line and stop reading file after timeout.
You can use the program stty to configure the tty device. To see the settings for terminal /dev/ttyS0, try
stty -a -F /dev/ttyS0
The default settings regarding timeout are min = 1; time = 0, which means that the reading program will read until at least one character has been read and there is no timeout. Using e.g.
stty -F /dev/ttyS0 min 0 time 10
the reading program (e.g. cat) will finish reading after one second whether anything has been read or not. The unit for the parameter time is tenths of a second; you can check out man stty for more information.
Compiling some info from here, you can have a script in the sorts of:
#!/bin/bash
#SPECIFYING THE SERIAL PORT
SERIAL=ttyS0
#SETTING UP AN ERROR FLAG
FLAG="GO"
#OPENING SERIAL PORT FOR READING
exec 99</dev/${SERIAL}
#READING FROM SERIAL
while ["${FLAG}" == "GO" ]
do
#IF NO INPUT IS READ AFTER 5 SECONDS, AN ERROR FLAG IS RAISED
read -t 5 INPUT <&99
STATUS=$?
if test $STATUS -ne 0;
then
FLAG="ERROR"
fi
done
#CLOSING SERIAL PORT
exec 99>&-
While FLAG==GO, the script will read one line at a time from the serial port. The STATUS variable gets the return of READ command. According to the manual READ will return anything different than 0 if the specified timeout is reached; when that happens, FLAG is updated, exiting the read loop.
Related
On an embedded Linux system running Busybox I am trying to receive bytes over a serial port and echo back everything received.
The system setup is like this:
Linux <-USB-> FTDI chip <-UART-> MCU
On the UART line I have a logic analyser monitoring the data between the FTCI chip and the MCU. Both the MCU and Linux have the same UART configuration.
The script I have written runs on the Linux system and is supposed to send back all data it received from the MCU.
So far I have this simple Bash script which is to receive bursts of data 62 bytes long. The timeout is set to 5 seconds as a sort of alive signal.
#!/bin/bash
# Enable debugging
set -x
# Set the baudrate of the port
stty -F /dev/ttyUSB0 1500000
while true
do
# Read 62 bytes with a timout of 5 seconds to variable RESP.
read -N62 -t5 RESP < /dev/ttyUSB0
# Print out how many bytes we received
echo ${#RESP}
# Send back the data, -n for no trailing new line
echo -n $RESP > /dev/ttyUSB0
done
There are a few problems I have with this script:
Not all bytes are received consistently. I've been testing now for some time an only seen the full number of bytes once.
It outputs only 0xFF values on the UART bus, this is observed through the logic analyser.
What am I missing here in order to receiving the correct data and send it back correctly?
Through the suggestions made by #sawdust I was able to get a working script.
In the end I stopped using the read command. I was unable to get it to work in raw mode. I could not use termios as this in to available on my Busybox system. I got a working setup using dd:
#!/bin/bash
stty -F /dev/ttyUSB0 raw
stty -F /dev/ttyUSB0 1500000
dd if=/dev/ttyUSB0 count=62 of=/dev/ttyUSB0
I'm currently working on a script which interact with another process.
If it is relevant, the said process is a simdebug console. What I want is exiting it properly because when I kill the process itself, it creates a lock file .lck.
The Simdebug console is waiting for inputs and closes on receiving quit, then q and n, both sperated by an enter keypress to validate the command.
I managed to send some commands to the Simdebug using
echo quit > /proc/< PID >/fd/1
But it only print the results of the echo and I can't find how to send a enter keypress, only new lines '\n' .
I can't aswell manage to send a quit command which would execute directly in the Simdebug and not the terminal where it is sent from.
My question would be resolved if one of those two points is answered:
Is it possible to simulate a validate keypress as in :
Term 1 : echo ifconfig ; echo < enter keypress>
Which would then execute what's in the read buffer of the Term 2
Is there a way to already execute a commande in another process as in
Term 1 : < unknown syntax > pwd
Term 2 < shows pwd of term2 not term1>
Which would not be working only from terminal to terminal but with an already opened process in read mode.
This is actually a hard thing to do. If you send characters to the /proc/self/fd/0 device or similar stdin device link from a different master terminal then it will just output the characters to the output side of the master terminal of the other process.
With tools like expect or pdip or screen you can send anything you want to a process encapsulated in their pseudoterminals as if it comes from their master terminal. But if a process is running then it will already have it's own terminal.
You can be in luck if your console can be persuaded for a controlling terminal transfer with reptyr.
For example if your console has process id 999999 (and you have screen and reptyr installed and maybe did something to appease selinux or apparmor/yama protections):
screen -dmS automateconsole
screen -S automateconsole -p 0 -X stuff 'reptyr 999999^M'
screen -S automateconsole -p 0 -X stuff 'quit^M'
sleep 1s
screen -S automateconsole -p 0 -X stuff 'q^M'
sleep 1s
screen -S automateconsole -p 0 -X stuff 'n^M'
sleep 1s
screen -S automateconsole -p 0 -X stuff 'exit^M'
But note:
You probably should cleanup the program that init'ed the console.
On Ubuntu at least I could not reptyr processes from other SSH sessions.
https://github.com/nelhage/reptyr
http://theterminallife.com/sending-commands-into-a-screen-session/
I have an expect bash to enter BIOS setup when machine bootup via serial redirection of that target machine. The following is my script where i use spawn to read & write the serial port, but the keystroke (Del) used to enter BIOS is not respond in that script
set timeout -1
match_max 100000
set port /dev/ttyUSB0
set rate 115200
exec stty -F $port $rate
spawn -open [open $port w+]
expect "American Megatrends"
sleep 1
send -- "^\[\[3~"
send_user "\nok\n"
The last line "ok" is printed that's means my keyword is expected but it doesn't go into BIOS setting
Meanwhile in another script where it read and write with screen /dev/ttyUSB0 115200 able to send the keystroke and enter BIOS setup successfully.
Anyone could help on this?
Thanks.
^[ means CTRL-[ which is the ESC char (0x1b or 033) so you can try like this:
send -- "\033\[3~"
Considering the following example, emulating a command which gives output after 10 seconds: exec 5< <(sleep 10; pwd)
In Solaris, if I check the file descriptor earlier than 10 seconds, I can see that it has a size of 0 and this tells me that it hasn't been populated with data yet. I can simply check every second until the file test condition is met (different from 0) and then pull the data:
while true; do
if [[ -s /proc/$$/fd/5 ]]; then
variable=$(cat <&5)
break
fi
sleep 1
done
But in Linux I can't do this (RedHat, Debian etc). All file descriptors appear with a size of 64 bytes no matter if they hold data or not. For various commands that will take a variable amount of time to dump their output, I will not know when I should read the file descriptor. No, I don't want to just wait for cat <&5 to finish, I need to know when I should perform the cat in the first place. Because I am using this mechanism to issue simultaneous commands and assign their output to corresponding file descriptors. As mentioned already, this works great in Solaris.
Here is the skeleton of an idea :
#!/bin/bash
exec 5< <(sleep 4; pwd)
while true
do
if
read -t 0 -u 5 dummy
then
echo Data available
cat <&5
break
else
echo No data
fi
sleep 1
done
From the Bash reference manual :
If timeout is 0, read returns immediately, without trying to read and
data. The exit status is 0 if input is available on the specified file
descriptor, non-zero otherwise.
The idea is to use read with -t 0 (to have zero timeout) and -u 5 (read from file descriptor 5) to instantly check for data availability.
Of course this is just a toy loop to demonstrate the concept.
The solution given by User Fred using only bash builtins works fine, but is a tiny bit non-optimal due to polling for the state of a file descriptor. If calling another interpreter (for example Python) is not a no-go, a non-polling version is possible:
#! /bin/bash
(
sleep 4
echo "This is the data coming now"
echo "More data"
) | (
python3 -c 'import select;select.select([0],[],[])'
echo "Data is now available and can be processed"
# Replace with more sophisticated real-world processing, of course:
cat
)
The single line python3 -c 'import select;select.select([0],[],[])' waits until STDIN has data ready. It uses the standard select(2) system call, for which I have not found a direct shell equivalent or wrapper.
So I have a bash command to start a server and it outputs some lines before getting to the point where it outputs something like "Server started, Press Control+C to exit". How do I pipe this output so when this line occurs i put this process in the background and continue with another script/function (i.e to do stuff that needs to wait until the server starts such as run tests)
I want to end up with 3 functions
start_server
run_tests
stop_server
I've got something along the lines of:
function read_server_output{
while read data; do
printf "$data"
if [[ $data == "Server started, Press Control+C to exit" ]]; then
# do something here to put server process in the background
# so I can run another function
fi
done
}
function start_server{
# start the server and pipe its output to another function to check its running
start-server-command | read_server_output
}
function run_test{
# do some stuff
}
function stop_server{
# stop the server
}
# run the bash script code
start_server()
run_tests()
stop_tests()
related question possibly SH/BASH - Scan a log file until some text occurs, then exit. How?
Thanks in advance I'm pretty new to this.
First, a note on terminology...
"Background" and "foreground" are controlling-terminal concepts, i.e., they have to do with what happens when you type ctrl+C, ctrl+Z, etc. (which process gets the signal), whether a process can read from the terminal device (a "background" process gets a SIGTTIN that by default causes it to stop), and so on.
It seems clear that this has little to do with what you want to achieve. Instead, you have an ill-behaved program (or suite of programs) that needs some special coddling: when the server is first started, it needs some hand-holding up to some point, after which it's OK. The hand-holding can stop once it outputs some text string (see your related question for that, or the technique below).
There's a big potential problem here: a lot of programs, when their output is redirected to a pipe or file, produce no output until they have printed a "block" worth of output, or are exiting. If this is the case, a simple:
start-server-command | cat
won't print the line you're looking for (so that's a quick way to tell whether you will have to work around this issue as well). If so, you'll need something like expect, which is an entirely different way to achieve what you want.
Assuming that's not a problem, though, let's try an entirely-in-shell approach.
What you need is to run the start-server-command and save the process-ID so that you can (eventually) send it a SIGINT signal (as ctrl+C would if the process were "in the foreground", but you're doing this from a script, not from a controlling terminal, so there's no key the script can press). Fortunately sh has a syntax just for this.
First let's make a temporary file:
#! /bin/sh
# myscript - script to run server, check for startup, then run tests
TMPFILE=$(mktemp -t myscript) || exit 1 # create /tmp/myscript.<unique>
trap "rm -f $TMPFILE" 0 1 2 3 15 # arrange to clean up when done
Now start the server and save its PID:
start-server-command > $TMPFILE & # start server, save output in file
SERVER_PID=$! # and save its PID so we can end it
trap "kill -INT $SERVER_PID; rm -f $TMPFILE" 0 1 2 3 15 # adjust cleanup
Now you'll want to scan through $TMPFILE until the desired output appears, as in the other question. Because this requires a certain amount of polling you should insert a delay. It's also probably wise to check whether the server has failed and terminated without ever getting to the "started" point.
while ! grep '^Server started, Press Control+C to exit$' >/dev/null; do
# message has not yet appeared, is server still starting?
if kill -0 $SERVER_PID 2>/dev/null; then
# server is running; let's wait a bit and try grepping again
sleep 1 # or other delay interval
else
echo "ERROR: server terminated without starting properly" 1>&2
exit 1
fi
done
(Here kill -0 is used to test whether the process still exists; if not, it has exited. The "cleanup" kill -INT will produce an error message, but that's probably OK. If not, either redirect that kill command's error-output, or adjust the cleanup or do it manually, as seen below.)
At this point, the server is running and you can do your tests. When you want it to exit as if the user hit ctrl+C, send it a SIGINT with kill -INT.
Since there's a kill -INT in the trap set for when the script exits (0) as well as when it's terminated by SIGHUP (1), SIGINT (2), SIGQUIT (3), and SIGTERM (15)—that's the:
trap "do some stuff" 0 1 2 3 15
part—you can simply let your script exit at this point, unless you want to specifically wait for the server to exit too. If you want that, perhaps:
kill -INT $SERVER_PID; rm -f $TMPFILE # do the pre-arranged cleanup now
trap - 0 1 2 3 15 # don't need it arranged anymore
wait $SERVER_PID # wait for server to finish exit
would be appropriate.
(Obviously none of the above is tested, but that's the general framework.)
Probably the easiest thing to do is to start it in the background and block on reading its output. Something like:
{ start-server-command & } | {
while read -r line; do
echo "$line"
echo "$line" | grep -q 'Server started' && break
done
cat &
}
echo script continues here after server outputs 'Server started' message
But this is a pretty ugly hack. It would be better if the server could be modified to perform a more specific action which the script could wait for.