How to write info to the file from bash script and see the result immediately? - bash

I need help. I can't understand how I can write information to the file from bash script and see the result immediately.
For example:
#!/usr/bin/env bash
PID=$$
echo "PID is $PID"
echo $PID > my_script.pid
echo "Sleeping..."
sleep 5
echo "Finished"
PID number appears in console immediately, but in the file I see it after script finished.
I have Mac OS X Yosemite 10.10.3.
I tried a lot of stuff with flush buffering. NO result:(
Please, help!
Update.
My goal is to define if another instance of that script is still running. I decided to use pid file and condition:
PID=`cat $PID_FILE`
if ps -p $PID > /dev/null; then
echo "script already running"
exit 1
fi
Maybe there is a more efficient way?

You must be trying to read it too soon. To confirm that it's being written right away change the script to:
#!/usr/bin/env bash
PID=$$
echo "PID is $PID"
echo "$PID written to file." >> my_script.pid
echo "Sleeping..."
sleep 5
echo "Finished"
Then run:
touch my_script.pid
tail -F my_script.pid &
./my_script.sh
The tail -F command will run on the background and will output whatever is written to my_script.pid shortly after it's written. The delay you see is on tail, once echo returns it is written.

Sorry for misunderstanding. Actually it works fine. The problem in GUI tool (PyCharm) where I checked out file modification. It has interesting delay.
So when I check out previous PID from the same script it works fine:)
Thanks a lot fernan for help;)

Related

Why is the second bash script not printing its iteration?

I have two bash scripts:
a.sh:
echo "running"
doit=true
if [ $doit = true ];then
./b.sh &
fi
some-long-operation-binary
echo "done"
b.sh:
for i in {0..50}; do
echo "counting";
sleep 1;
done
I get this output:
> ./a.sh
running
counting
Why do I only see the first "counting" from b.sh and then nothing anymore? (Currently some-long-operation-binary just sleep 5 for this example). I first thought that due to setting b.sh in the background, its STDOUT is lost, but why do I see the first output? More importantly: is b.sh still running and doing its thing (its iteration)?
For context:
b.sh is going to poll a service provided by some-long-operation-binary, which is only available after some time the latter has run, and when ready, would write its content to a file.
Apologies if this is just rubbish, it's a bit late...
You should add #!/bin/bash or the like to b.sh that uses a Bash-like expansion, to make sure Bash is actually running the script. Otherwise there may be (indeed) only one loop iteration happening.
When you start a background process, it is usually a good practice to kill it and wait for it, no matter which way the script exits.
#!/bin/bash
set -e -o pipefail
declare -i show_counter=1
counter() {
local -i i
for ((i = 0;; ++i)); do
echo "counting $((i))"
sleep 1
done
}
echo starting
if ((show_counter)); then
counter &
declare -i counter_pid="${!}"
trap 'kill "${counter_pid}"
wait -n "${counter_pid}" || :
echo terminating' EXIT
fi
sleep 10 # long-running process

applescript blocks shell script cmd when writing to pipe

The following script works as expected when executed from an Applescript do shell script command.
#!/bin/sh
sleep 10 &
#echo "hello world" > /tmp/apipe &
cpid=$!
sleep 1
if ps -ef | grep $cpid | grep sleep | grep -qv grep ; then
echo "killing blocking cmd..."
kill -KILL $cpid
# non zero status to inform launch script of problem...
exit 1
fi
But, if the sleep command (line 2) is swaped to the echo command in (line 3) together with the if statement, the script blocks when run from Applescript but runs fine from the terminal command line.
Any ideas?
EDIT: I should have mentioned that the script works properly when a consumer/reader is connected to the pipe. It only block when nothing is reading from the pipe...
OK, the following will do the trick. It basically kills the job using its jobid. Since there is only one, it's the current job %%.
I was lucky that I came across the this answer or it would have driven me crazy :)
#!/bin/sh
echo $1 > $2 &
sleep 1
# Following is necessary. Seems to need it or
# job will not complete! Also seen at
# https://stackoverflow.com/a/10736613/348694
echo "Checking for running jobs..."
jobs
kill %% >/dev/null 2>&1
if [ $? -eq 0 ] ; then
echo "Taking too long. Killed..."
exit 1
fi
exit 0

get the number of seconds left for the sleep command to end in a shell script

I built a shell script that sleeps for a specified amount of minutes and shows notification when it is done.
TIME=$(zenity --scale --title="Next Session in (?) minutes")
sleep $TIME'm'
BEEP="/usr/share/sounds/freedesktop/stereo/complete.oga"
paplay $BEEP
notify-send "Next Session" "Press <Ctrl><Shift><s> to run the script again"
I prevented multiple instance of the program from executing using a file based approach at the beginning of the code. When a user wants to run the script while another instance is running, it shows a notification that the script is already running.
LOCKFILE=/tmp/lock.txt
if [ -e ${LOCKFILE} ] && kill -0 `cat ${LOCKFILE}`; then
notify-send "Already Running" $SECONDS
exit
fi
trap "rm -f ${LOCKFILE}; exit" INT TERM EXIT
echo $$ > ${LOCKFILE}
and finally remove the temporary file at the end of the script
rm -f ${LOCKFILE}
Now I want to add a text to the notification that tells how many seconds are left for the sleep command in my shell script to end. (changing the already running notification as follows)
notify-send "Already Running" $SECONDS
To implement the sleep command with my own controlled while loop would affect the overall performance of the computer. I think the sleep command is a better option as it optimizes the process by sending itself to a waiting state in the process queue.
Is there any way I can go around the problem?
Store the time when the script is supposed to end in the lock file.
if [ -e "$LOCKFILE" ]; then
read pid endtime < "$LOCKFILE"
if kill -0 "$pid"; then
notify-send "Already running" $(($(date +%s) - $endtime))
exit
fi
fi
trap "rm -f ${LOCKFILE}" EXIT # Use cascaded trap
trap 'exit 127' INT TERM
echo $$ $(($(date +%s) + (60 * $TIME))) >"$LOCKFILE"
There is a race condition here; if two scripts are started at almost the same time, the first could be inside the if but before the echo when the second starts. If you really need to prevent that, use a lock directory instead of a file -- directory creation is atomic, and either succeeds or fails at just a single point in time (but then you'll need to clean out the stale directory in the mystery scenario where the directory exists but is not owned by a file -- maybe after a careless OOM killer or something).
I think Triplee has a fine answer, another way to handle it that can be applied to any running process that may block is to bg the process briefly to grab and save the assigned pid $! to a file then fg the process back.
From there you can do the math and get the seconds via ps:
TIME=$(zenity --scale --title="Next Session in (?) minutes")
SLEEP_PID_FILE="/tmp/__session_ui_sleep_pid__"
sleep $TIME'm' &
echo $! >> "${SLEEP_PID_FILE}"
fg
BEEP="/usr/share/sounds/freedesktop/stereo/complete.oga"
paplay $BEEP
notify-send "Next Session" "Press <Ctrl><Shift><s> to run the script again"
Then afterward you can find the current elapsed time with something like:
notify-send "Already running for $(($(date +%s)-$(date -d"$(ps -o lstart= -p$(< "${SLEEP_PID_FILE}"))" +%s))) seconds..."

How to proceed in the script if file exists ?

how to proceed in the script if file exists?
#!/bin/bash
echo "Start"
# waiting to be exist file
echo "file already exists, continuing"
Do a while if a sleep X, so that it will check the existence of the file every X seconds.
When the file will exist, the while will finish and you will continue with the echo "file already exists, continuining".
#!/bin/bash
echo "Start"
### waiting to be exist file
while [ ! -f "/your/file" ]; # true if /your/file does not exist
do
sleep 1
done
echo "file already exists, continuing"
And goes instead of checking the file existence check if the script
has already completed the background?
Based on the code you posted, I did some changes to make it work completely:
#!/bin/bash
(
sleep 5
) &
PID=$!
echo "the pid is $PID"
while [ ! -z "$(ps -ef | awk -v p=$PID '$2==p')" ]
do
echo "still running"
sleep 1
done
echo "done"
There are OS-specific ways to perform blocking waits on the file system. Linux uses inotify (I forget the BSD equivalent). After installing inotify-tools, you can write code similar to
#!/bin/bash
echo "Start"
inotifywait -e create $FILE & wait_pid=$!
if [[ -f $FILE ]]; then
kill $wait_pid
else
wait $wait_pid
fi
echo "file exists, continuing"
The call to inotifywait does not exit until it receives notification from the operating system that $FILE has been created.
The reason for not simply calling inotifywait and letting it block is that there is a race condition: the file might not exist when you test for it, but it could be created before you can start watching for the creation event. To fix that, we start a background process that waits for the file to be created, then check if it exists. If it does, we can kill inotifywait and proceed. If it does not, inotifywait is already watching for it, so we are guaranteed to see it be created, so we simply wait on the process to complete.
To fedorqui: Is it so good? There is a problem?
#!/bin/bash
(
..
my code
..
) &
PID=$BASHPID or PID=$$
while [ ! ps -ef | grep $PID ]
do
sleep 0
done
Thank you

Close pipe even if subprocesses of first command is still running in background

Suppose I have test.sh as below. The intent is to run some background task(s) by this script, that continuously updates some file. If the background task is terminated for some reason, it should be started again.
#!/bin/sh
if [ -f pidfile ] && kill -0 $(cat pidfile); then
cat somewhere
exit
fi
while true; do
echo "something" >> somewhere
sleep 1
done &
echo $! > pidfile
and want to call it like ./test.sh | otherprogram, e. g. ./test.sh | cat.
The pipe is not being closed as the background process still exists and might produce some output. How can I tell the pipe to close at the end of test.sh? Is there a better way than checking for existence of pidfile before calling the pipe command?
As a variant I tried using #!/bin/bash and disown at the end of test.sh, but it is still waiting for the pipe to be closed.
What I actually try to achieve: I have a "status" script which collects the output of various scripts (uptime, free, date, get-xy-from-dbus, etc.), similar to this test.sh here. The output of the script is passed to my window manager, which displays it. It's also used in my GNU screen bottom line.
Since some of the scripts that are used might take some time to create output, I want to detach them from output collection. So I put them in a while true; do script; sleep 1; done loop, which is started if it is not running yet.
The problem here is now that I don't know how to tell the calling script to "really" detach the daemon process.
See if this serves your purpose:
(I am assuming that you are not interested in any stderr of commands in while loop. You would adjust the code, if you are. :-) )
#!/bin/bash
if [ -f pidfile ] && kill -0 $(cat pidfile); then
cat somewhere
exit
fi
while true; do
echo "something" >> somewhere
sleep 1
done >/dev/null 2>&1 &
echo $! > pidfile
If you want to explicitly close a file descriptor, like for example 1 which is standard output, you can do it with:
exec 1<&-
This is valid for POSIX shells, see: here
When you put the while loop in an explicit subshell and run the subshell in the background it will give the desired behaviour.
(while true; do
echo "something" >> somewhere
sleep 1
done)&

Resources