You seem to have missed one fairly basic fact. Every pipeable program relies on the fact that it gets three standard file descriptors in a pre-opened state from whatever program invokes it. In most cases involving pipes, the invoking program will be the command shell.
These three file descriptors are:
- file descriptor #0: standard input stream, or
stdin
for short.
- file descriptor #1: standard output stream, or
stdout
for short.
- file descriptor #2: standard error output stream, or
stderr
for short.
When a program is not being piped or redirected to read/write a file, then all these three file descriptors will usually point to a TTY device the current login session is running on. If a program is deliberately started as a daemon or a background/non-interactive process, all of these might be directed to file(s) or /dev/null
, or possibly into some sort of logging facility.
Error messages are normally output into the stderr
stream, precisely so that they won't get mixed in with the possibly-piped data output. But in cases like your, when you wish to capture all the output, you need to explicitly tell the system to grab both streams.
If you want to pass all the output from your myapplication.sh
into a pipe, you might have to do something like this:
myapplication.sh 2>&1 | logger
The 2>&1
says "redirect file descriptor #2 to the same place #1 is going", and then you can pipe both of them at once.
You can test it yourself with a little script like this:
#!/bin/sh
echo "this is stdout"
echo "this is stderr" >&2
The >&2
means "take the standard output of this command and send it into the standard error stream instead". This is probably the easiest way to generate stderr
output in a script.
Now, if you run this script like ./test.sh | od -t x1z
to get its output in hex-dumped form, you'll get this:
$ ./test.sh | od -t x1z
this is stderr
0000000 74 68 69 73 20 69 73 20 73 74 64 6f 75 74 0a >this is stdout.<
0000017
So only the stdout
output got processed by the od
command.
If you add the 2>&1
to merge both streams, you'll get this instead:
$ ./test.sh 2>&1 | od -t x1z
0000000 74 68 69 73 20 69 73 20 73 74 64 6f 75 74 0a 74 >this is stdout.t<
0000020 68 69 73 20 69 73 20 73 74 64 65 72 72 0a >his is stderr.<
0000036
Now both outputs were sent into the pipeline, which is what you want in your case with | logger
.
Your second command line is capturing whatever your embedded device is receiving from the terminal program, not what the embedded device is sending to the terminal:
(stty raw; cat > received.txt) < /dev/ttyO0
You said you're working on an embedded system that uses the kernel parameter console=/dev/ttyO0
, so essentially /dev/console
will be a serial port, not a graphical display (which may not even exist in an embedded device).
If you are running that command on the embedded device, the commands inside the parentheses will run with their standard input stream (file descriptor #0, or stdin
for short) assigned to /dev/ttyO0
. But if you are entering that command over the serial line connected to ttyO0
, that's what it's probably going to be by default, so this input redirection may just be re-enforcing the situation as it is.
The /dev/ttyO0
device is the direct representation of the UART that is connected to your terminal program on the other computer. When you read from it, you get only what you are typing into the terminal program: there is no way to read back any output that was previously sent into it. And anything written into it goes directly to the terminal program at the end of the serial cable.
The Unix command line on a serial port normally works on a remote echo principle: in order to see what you are writing on the terminal program, the TTY driver on the embedded system must send a copy of every character you type right back to the terminal program. This behavior is not symmetric: the terminal program will not be also sending back a copy of anything it receives over the serial port connection.
The TTY driver is a complicated thing, because Unix heritage requires it to be capable of doing a lot of things - the remote echo feature is just one of them.
When you use stty raw
, you're effectively switching off all the normally-helpful features of the TTY driver, including the remote echo feature. So, from that point onwards, unless something explicitly reads from /dev/ttyS0
and writes it immediately right back into it, you won't see what you are writing on the terminal program.
Once the stty raw
command has exited, any input from the terminal program will go into the cat
command, which would just pass it into its standard output as-is when invoked with no parameters. But its standard output has now been redirected into a file, received.txt
. So what happens is that only what you're typing on the terminal program gets written into the file until the cat
command ends - and it only ends when it gets the end-of-file character (typically Control+D).
(Because of whatever buffering might be done by the shell doing the redirection, there might be no output into the received.txt
file unless you end your typing with the end-of-file character, and/or type more than one line of text.)
So, other than switching off the TTY driver features, the (stty raw; cat > received.txt) < /dev/ttyO0
on the embedded system does nothing at all to capture any output that is being written into /dev/ttyO0
. The only use for this command would be when you're troubleshooting the serial port connection: if you are suspicious that your terminal program is somehow mangling the characters you're sending out, this is the way to capture whatever the terminal program is sending to the embedded device in its most raw form.
If you want to record absolutely everything that happens over the UART connection, including e.g. kernel panic messages, then the best way to do it would probably be to tell your terminal program on the other computer to log all the traffic. Many terminal programs have this feature built-in.
This is because if something is going wrong with the kernel, it might not be actually possible to write anything into a disk file any more. It's much simpler to just send the error message text out of an UART and trust that whoever is receiving it will catch it.