I found a solution to this here. Using the OP's example you basically run
stdbuf -oL /homedir/MyScript &> some_log.log
and then the buffer gets flushed after each line of output. I often combine this with nohup to run long jobs on a remote machine.
stdbuf -oL nohup /homedir/MyScript &> some_log.log
This way your process doesn't get cancelled when you log out.
Answer from Martin Wiebusch on Stack OverflowI found a solution to this here. Using the OP's example you basically run
stdbuf -oL /homedir/MyScript &> some_log.log
and then the buffer gets flushed after each line of output. I often combine this with nohup to run long jobs on a remote machine.
stdbuf -oL nohup /homedir/MyScript &> some_log.log
This way your process doesn't get cancelled when you log out.
script -c <PROGRAM> -f OUTPUT.txt
Key is -f. Quote from man script:
-f, --flush
Flush output after each write. This is nice for telecooperation: one person
does 'mkfifo foo; script -f foo', and another can supervise real-time what is
being done using 'cat foo'.
Run in background:
nohup script -c <PROGRAM> -f OUTPUT.txt
It looks like you could use FIFOs for this:
mkfifo tasks
Producer:
for i in {1..10}; do echo "task$i"; done > tasks
Consumer:
while read task; do echo "received $task"; done <tasks
With this, you don't have to take care of synchronization or deletion, and you don't waste any CPU time on polling -- if there's no data or if the producer hasn't opened the FIFO yet, the consumer will block (and thereby save CPU time). If the FIFO is full or the consumer hasn't opened the FIFO, the producer will block.
Try to put sync between your cp command and touch ready.txt.
How do I flush stdout in a `%%script bash` cell?
What are the ways to flush a Linux command's stdout as a child process?
linux - Force a shell script to fflush - Stack Overflow
Set flushing mode for output stream
IF one were truly wanting that data, I'd suggest attaching the gdb debugger to the python interpreter, momentarily stopping the task, calling fsync(1) (stdout), detach from it (resuming the process) and go peruse the output file.
Look in /proc/$(pidof python)/fd to see valid file descriptors. $(pidof x) returns the PID of process named 'x'.
# your python script is running merrily over there.... with some PID you've determined.
#
# load gdb
gdb
#
# attach to python interpreter (use the number returned by $(pidof python))
attach 1234
#
# force a sync within the program's world (1 = stdout, which is redirected in your example)
call fsync(1)
#
# the call SHOULD have returned 0x0, sync successful. If you get 0xffffffff (-1), perhaps that wasn't stdout. 0=stdin, 1=stdout, 2=stderr
#
# remove our claws from poor python
detach
#
# we're done!
quit
I've used this method to change working dir's, tweak settings on the fly... many things. Alas, you can only call functions which are defined in the running program, fsync works nicely though.
(gdb command 'info functions' will list all of the functions available. Be careful though. You're operating LIVE on a process.)
There is also the command peekfd (found in psmisc package on Debian Jessie and others) which will allow you to see what's hiding in buffers of a process. Again, /proc/$(pidof python)/fd will show you valid file descriptors to give as arguments to peekfd.
If you don't remember -u for python, you can always prefix a command with stdbuf (in coreutils, already installed) to set stdin/stdout/stderr to unbuffered, line buffered or block buffered as desired:
stdbuf -i 0 -o 0 -e 0 python myscript.py > unbuffered.output
Of course, man pages are your friends, hey! perhaps an alias might be useful here too.
alias python='python -u'
Now your python always uses -u for all your command line endeavors!
First make sure you have the debugging symbols for Python (or at least glibc). On Fedora1 you can install them with:
dnf debuginfo-install python
Then attach gdb to the running script and run the following commands:
[user@host ~]$ pidof python2
9219
[user@host ~]$ gdb python2 9219
GNU gdb (GDB) Fedora 7.7.1-13.fc20
...
0x00007fa934278780 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81
81 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
(gdb) call fflush(stdout)
$1 = 0
(gdb) call setvbuf(stdout, 0, 2, 0)
$2 = 0
(gdb) quit
A debugging session is active.
Inferior 1 [process 9219] will be detached.
Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2, process 9219
This will flush stdout and also disable buffering. The 2 from the setvbuf call is the value of _IONBF on my system. You'll need to find out what's on yours (a grep _IONBF /usr/include/stdio.h should do the trick).
Based on what I've seen in the implementation of PyFile_SetBufSize and PyFile_WriteString in CPython 2.7, it should work pretty well, but I can't make any guarantees.
1 Fedora includes a special type of RPMs called debuginfo rpms. These automatically created RPMs contain the debugging information from the program files, but moved into an external file.
If comands use stdio and are connected to a terminal they'll be flushed per line. Otherwise you'll need to use something like stdbuf on commands in a pipe line http://www.pixelbeat.org/programming/stdio_buffering/
tl;dr: instead of printf ... try to put to the script stdbuf -o0 printf .., or stdbuf -oL printf ...
If you force the file to be read, it seems to cause the buffer to flush. These work for me.
Either read the data into a useless variable:
x=$(<$logfile)
Or do a UUOC:
cat $logfile > /dev/null
The caching is handled by the libc. You can use the stdbuf command to change the buffer size:
stdbuf -o0 ./my-script.sh > file.txt &
-o0 sets the buffer size for stdout to 0. Probably you also want -e0 for stderr.
You can inspect the /proc/ filesystem and alter the file descriptor of stdout. For example:
gerard@droole ~$ bash -c '
while [ true ]; do echo "."; sleep .5; done
' > ./myfile.txt &
[1] 3816
gerard@droole ~$ ls -la /proc/3816/fd/1
l-wx------ 1 gerard gerard 64 May 30 14:55 /proc/3816/fd/1 -> /home/gerard/myfile.txt
You can see that stdout is symlinked to the file I specified on the command line. If you want to change it, you can simply link it to something else.
If you want to reroute this output, you can start a tee process, symlink the stdout of the process you're watching to a the stdin of the new process. You can reroute basically anything you want this way.
However, this is not very stable, as your programs output will be broken if you do not carefully restore its stdout file descriptor before the tee process is terminated.
But it is not impossible ;)
Have a script that runs a custom program within bash, the exe takes a while to run, about 5 minutes, but when you run it there are heartbeats printed to let the user know that it is still running, on my bash, those messages appear after the script has finished running, also, it seems the buffer is full and not every message gets printed, I tried redirecting to a file but still the output is not "real time"
something like
# !/bin/bash test_executable --flag=1 | tee "/tmp/test_output.txt"
this is on macos, also tried to output to /dev/tty but it says its not configured.
This thread on nonblocking I/O in bash might help.
It suggests using stty and dd.
Or you could use the bash read builtin with the -t 0 option.
# do your stuff
# discard rest of input before exiting
while read -t 0 notused; do
read input
echo "ignoring $input"
done
If you only want to do it if the user is at a terminal, try this:
# if we are at a terminal, discard rest of input before exiting
if test -t 0; then
while read -t 0 notused; do
read input
echo "ignoring $input"
done
fi
This is the one-liner I settled on similar to ones here:
while read -t 1 discard; do echo "ignoring input..."; done # Flush input
gdb -p PID -batch -ex 'p fflush(stdout)'
As with any debugging and hacking, YMMV.
Do you have access to the source of the running programs?
Forcing a flush of an arbitrary executable is, while not theoretically impossible, very difficult. You would need to find the fflush function in the code and the stdout argument, then interrupt the execution of the program, arrange for the call to fflush, then continue execution. If the program is using a shared library, that at least makes the first part easier, finding fflush and stdout, but you still need to simulate the call. Also, for an unknown binary, you can't know whether it used stdio or whether it implements its own buffering mechanism. Knowing the path of the output file will not help you.
You can try to use gdb, then use the attach command to attach to the process. Maybe gdb can call the fflush function.
If you have the source to the program, implement a signal handler that flushes the buffer and just send the signal when you want the buffer flushed.
You can try a pipe, maybe the programs don't buffer if the output is a pipe. Change it to
./program | your_program > out.bin
Your program can accept the input, buffer it and flush the buffer when it receives a signal. It would still add CPU overhead, but not disk overhead.