A simple way to add pipes to tee-d file - shell

I'm trying to record the output of a command with post processing to clean things up
(like removing ansi escape codes to a file while outputing the command to screen)
(command is minicom which functions as a terminal among other things).
currently I have the following but it doesn't work(seems to block).
rm "${fifo}"
mkfifo "${fifo}"
cat "${fifo}"|filter_1 >"${log_file}" &
command |tee "${fifo}"
p.s.
command | tee "${log_file}"
works fine

Besides unbuffer, you can try
{ command ; printf "\n" ; } | tee "${log_file}"
I hope this helps.

Related

How to redirect stdin to a FIFO with bash

I'm trying to redirect stdin to a FIFO with bash. This way, I will be able to use this stdin on an other part of the script.
However, it doesn't seem to work as I want
script.bash
#!/bin/bash
rm /tmp/in -f
mkfifo /tmp/in
cat >/tmp/in &
# I want to be able to reuse /tmp/in from an other process, for example :
xfce4-terminal --hide-menubar --title myotherterm --fullscreen -x bash -i -c "less /tmp/in"
Here I would expect , when I run ls | ./script.bash, to see the output of ls, but it doesn't work (eg the script exits, without outputing anything)
What am I misunderstanding ?
I am pretty sure that less need additional -f flag when reading from pipe.
test_pipe is not a regular file (use -f to see it)
If that does not help I would also recommend to change order between last two lines of your script:
#!/bin/bash
rm /tmp/in -f
mkfifo /tmp/in
xfce4-terminal --hide-menubar --title myotherterm --fullscreen -x bash -i -c "less -f /tmp/in" &
cat /dev/stdin >/tmp/in
In general, I avoid the use of /dev/stdin, because I get a lot of surprises from what is exactly /dev/stdin, especially when using redirects.
However, what I think that you're seeing is that less finishes before your terminal is completely started. When the less ends, so will the terminal and you won't get any output.
As an example:
xterm -e ls
will also not really display a terminal.
A solution might be tail -f, as in, for example,
#!/bin/bash
rm -f /tmp/in
mkfifo /tmp/in
xterm -e "tail -f /tmp/in" &
while :; do
date > /tmp/in
sleep 1
done
because the tail -f remains alive.

Vim: Warning: Input is not from a terminal

I wrote simple jsonview script to view json files:
#!/bin/bash
tmp_file=/tmp/jsonview.json
cat "${#}" | python -m json.tool > $tmp_file
[[ -f $tmp_file ]] && vim $tmp_file
I am not using less because I need syntax highlighting.
That useless use of cat cat ${#} | ... is so that script can be used as a filter:
jsonview t.json
and:
cat t.json | jsonview
If jsonview used as in second, pipe case - despite the fact that vim is invoked not on pipe but on concrete file, I am getting that warning in subject. I can view json file, but after exit, it messes up terminal. Why is this warning? Why vim thinks that it reads from a pipe?
Vim doesn't like it when standard input is redirected unless you invoke it as vim -. In that case it knows stdin is redirected and handles it. As a side benefit it also lets you get rid of the temp file.
#!/bin/bash
cat "$#" | python -m json.tool | vim +'set syntax=javascript' -R -
Always quote "$#" to ensure file names with whitespace won't mess your script up.
-R gets rid of the prompt to save the buffer when exiting Vim.
you can also do vim $tmp_file </dev/tty to tell vim input is from the terminal and not from standard input.

Copy *unbuffered* stdout to file from within bash script itself

I want to copy stdout to a log file from within a bash script, meaning I don't want to call the script with output piped to tee, I want the script itself to handle it. I've successfully used this answer to accomplish this, using the following code:
#!/bin/bash
exec > >(sed "s/^/[${1}] /" | tee -a myscript.log)
exec 2>&1
# <rest of script>
echo "hello"
sleep 10
echo "world"
This works, but has the downside of output being buffered until the script is completed, as is also discussed in the linked answer. In the above example, both "hello" and "world" will show up in the log only after the 10 seconds have passed.
I am aware of the stdbuf command, and if running the script with
stdbuf -oL ./myscript.sh
then stdout is indeed continuously printed both to the file and the terminal.
However, I'd like this to be handled from within the script as well. Is there any way to combine these two solutions? I'd rather not resort to a wrapper script that simply calls the original script enclosed with "stdbuf -oL".
You can use a workaround and make the script execute itself with stdbuf, if a special argument is present:
#!/bin/bash
if [[ "$1" != __BUFFERED__ ]]; then
prog="$0"
stdbuf -oL "$prog" __BUFFERED__ "$#"
else
shift #discard __BUFFERED__
exec > >(sed "s/^/[${1}] /" | tee -a myscript.log)
exec 2>&1
# <rest of script>
echo "hello"
sleep 1
echo "world"
fi
This will mostly work:
if you run the script with ./test, it shows unbuffered [] hello\n[] world.
if you run the script with ./test 123 456, it shows [123] hello\n[123] world like you want.
it won't work, however, if you run it with bash test - $0 is set to test which is not your script. Fixing this is not in the scope of this question though.
The delay in your first solution is caused by sed, not by tee. Try this instead:
#!/bin/bash
exec 6>&1 2>&1>&>(tee -a myscript.log)
To "undo" the tee effect:
exec 1>&6 2>&6 6>&-

Hook all command output within bash

Just for fun I want to pipe all output text in a terminal to espeak. For example, after this is set up I should be able to type echo hi and hear "hi" spoken, or ls and hear my directory contents listed.
The only promising method to capture output I've found so far is from here: http://www.linuxjournal.com/content/bash-redirections-using-exec
This is what I have so far:
npipe=/tmp/$$.tmp
mknod $npipe p
tee /dev/tty <$npipe | espeak &
espeakpid=$!
exec 1>&-
exec 1>$npipe
trap "rm -f $npipe; kill $espeakpid" EXIT
It works (also printing a bunch of "Done" jobs), but creating the named pipe, removing with trap and printing the output with tee all just seems a bit messy. Is there a simpler way?
This is one way:
exec > >(exec tee >(exec xargs -n 1 -d '\n' espeak -- &>/dev/null))
If you want to be able to restore back to original output stream:
exec 3>&1 ## Store original stdout to fd 3.
exec 4> >(exec tee >(exec xargs -n 1 -d '\n' espeak -- &>/dev/null)) ## Open "espeak" as fd 4.
exec >&4 ## Redirect stdout to "espeak".
exec >&3 ## Redirect back to normal.
I use xargs -n 1 because espeak doesn't do anything until EOF is reached so we summon an instance of it per line. This can be customized of course, but there's your answer. And of course a while read loop can also be an option for this.
I also use exec's in process substitution to make sure we get rid of unnecessary subshells.
Seems like it's way easier than that - I just tested this and it works:
$ echo "these are not the droids you are looking for" | espeak --stdin
The --stdin flag is the key. From espeak's man page:
--stdin
Read text input from stdin instead of a file
And if what you want to hear is a very long output, I guess you can use xargs if you run into Argument list too long errors...

Copy stderr and stdout to a file as well as the screen in ksh

I'm looking for a solution (similar to the bash code below) to copy both stdout and stderr to a file in addition to the screen within ksh on Solaris.
The following code works great in the bash shell:
#!/usr/bin/bash
# Clear the logfile
>logfile.txt
# Redirect all script output to a logfile as well as their normal locations
exec > >(tee -a logfile.txt)
exec 2> >(tee -a logfile.txt >&2)
date
ls -l /non-existent/path
For some reason this is throwing a syntax error on Solaris. I assume it's because I can't do process substitution, and I've seen some posts suggesting the use of mkfifo, but I've yet to come up with a working solution.
Does anyone know of a way that all output can be redirected to a file in addition to the default locations?
Which version of ksh are you using? The >() is not supported in ksh88, but is supported in ksh93 - the bash code should work unchanged (aside from the #! line) on ksh93.
If you are stuck with ksh88 (poor thing!) then you can emulate the bash/ksh93 behaviour using a named pipe:
#!/bin/ksh
# Clear the logfile
>logfile.txt
pipe1="/tmp/mypipe1.$$"
pipe2="/tmp/mypipe2.$$"
trap 'rm "$pipe1" "$pipe2"' EXIT
mkfifo "$pipe1"
mkfifo "$pipe2"
tee -a logfile.txt < "$pipe1" &
tee -a logfile.txt >&2 < "$pipe2" &
# Redirect all script output to a logfile as well as their normal locations
exec >"$pipe1"
exec 2>"$pipe2"
date
ls -l /non-existent/path
The above is a second version to enable stderr to be redirected to a different file.
How about this:
(some commands ...) 2>&1 | tee logfile.txt
Add -a to the tee command line for subsequent invocations to append rather than overwrite.
In ksh, the following works very well for me
LOG=log_file.$(date +%Y%m%d%H%M%S).txt
{
ls
date
... whatever command
} 2>&1 | tee -a $LOG

Resources