When tee command redirect to subshell, the last two lines missing - bash

I have below solution to record a command and its output executed on a remote machine:
rexec:// -t -t /usr/bin/ssh -q -x -o StrictHostKeyChecking=no -2 \
-l ${SSHUserName} -p 22 ${mainHost} \
| tee >(/opt/oss/clilogging/bin/clilogging.sh para1 para2)
clilogging.sh will record each command and its output into a log file.
However, sometimes the last exited command and its output message "logout" is not written into the log file.
clilogging.sh is as follows:
#!/bin/bash
{
while read R || [ -n "$R" ];do
#e.g. 2013-08-19T09:58:08+0300
timestamp=`date +%FT%T%z`;
echo $timestamp $R;
done
} > /tmp/xxx.log
Could anybody help me?
Thanks a lot!

Thanks thom's comment and thank you all.
I have found the solution of this issue.
Need add following code at the begining of clilogging.sh
trap "" HUP
The meaning of code is to handle SIGHUP signal, here I ignore this signal, then clilogging.sh
will not quit immediately and have the chance to handle all buffer.
man 7 signal
Signal Value Action Comment
-------------------------------------------------------------------------
SIGHUP 1 Term Hangup detected on controlling terminal
or death of controlling process

Related

How to exit minicom via scripting

I have a minicom script which sends some commands via serial and expects something back(which works) but im having trouble exiting the minicom screen.
Below is the minicom script:
success1:
print \nSuccessfully received running!
send "exit"
exit 0
success2:
print \nSuccessfully received degrading!
! killall -9 minicom
exit
I was using ! killall -9 minicom which is recommended on their documentation but unfortunately, when running the script on Jenkins, it fails due to exit code 137 (Another process sent a signal 9). However this does exit minicom, just not successfully.
On the other hand, the 'send "exit"' just logs out of the device, and doesnt exit minicom.
How can i exit minicom and receive a 0 exit code?
you need to feed <stdin> with three characters: <ctrl-A>x<enter>
prepare the file escape.txt using vi in order to write ^Ax^M
launch minicom script
/bin/rm -f capture.txt; ( minicom -D /dev/ttyUSB0 -S test_minicom.macro -C capture.txt < escape.txt ) ; cat capture.txt
To build on what Diego shared, if you just need to exit minicom without error and don't care about capturing the exit code, build escape.txt as Diego described then you only need to run:
( minicom -D /dev/ttyUSB0 -S test_minicom.macro -C capture.txt < escape.txt )
This proves to be very helpful for automatic provisioning like with Ansible!
As an alternative to creating the escape.txt file, you can use echo to send the exit sequence.
Building on the above answers:
$ /bin/rm -f capture.txt; ( echo -ne "\x01x\r" ) | minicom -D /dev/ttyUSB0 -S test_minicom.macro -C capture.txt; cat capture.txt
To break down the echo command a little,
-n removes the default line-feed \n
-e tells echo to interpret escape sequences
\x01 is the escape sequence for ^A (start-of-heading)
x tells minicom to exit
\r sends a ^M carriage-return
Hex output from echo:
$ echo -ne "\x01x\r" | od -A x -t x1a -v
000000 01 78 0d
soh x cr
000003
Note: If you just want to send some text and don't need the full minicom scripting you can add an extra echo. Sleep may not be needed depending on command run and if you care about output in capture.txt...
$ /bin/rm -f capture.txt; ( echo "poweroff"; sleep 1; echo -ne "\x01x\r" ) | minicom -D /dev/ttyUSB0 -C capture.txt; cat capture.txt

How would i get a process pid which is running in a bash script and know when its done

I need to know an another process pid which is executed as a command in the bash script like a nmap scan etc as an when the process is created I can display a bar or a spinner
#!/bin/sh
G='\033[0;32m'
B='\033[0m'
${G}Enter IP TAIL LIKE 0.1 OR 1.1${B}"
read v
${G}ENTER Device NAME ${B}"
read k
nmap -A -Pn -sV 192.168.$v -oN /tmp/op
//here i want a while loop for displaying a spinner
edit code is updated Thts the code in case tht was not visible properly ^^
As the pid dies when its completed I can use it as a count or a flag or something similar to end the spinner.
I'm working on Kali nethunter.
P.S This is my first time on stack overflow pls forgive me if its too stupid or tell me if I missed something.
A starting point:
#!/bin/bash
echo "Enter IP:"
read -r ip
# `&` runs in parallel
# always quote variables
nmap "$ip" &
# `$!` get's the background process PID
pid=$!
...
# you can see if a pid is running by checking exit status of `kill -0`
while kill -0 "$pid" 2>&1 >/dev/null; do
printf ...
...
done
nmap -A -Pn -sV 192.168.0.1 -p -oN /tmp/op >/dev/null & pid=$!
i=1
sp="/-\|"
echo -n ' '
while kill -0 "$pid" 2>&1 >/dev/null;
do
printf "\b${sp:i++%${#sp}:1}"
done
*Problem solved thanks to * kamil cuk

bash script to accept log on stdin and email log if inputting process fails

I'm a sysadmin and I frequently have a situation where I have a script or command that generates a lot of output which I would only like to have emailed to me if the command fails. It's pretty easy to write a script that runs the command, collects the output and emails it if the command fails, but I was thinking I should be able to write a command that
1) accepts log info on stdin
2) waits for the inputting process to exit and see what it's exit status was
3a) if the inputting process exited cleanly, append the logging input to a normal log file
3b) if the inputting process failed, append the logging input to the normal log and also send me an email.
It would look something like this on the command line:
something_important | mailonfail.sh me#example.com /var/log/normal_log
That would make it really easy to use in crontabs.
I'm having trouble figuring out how to make my script wait for the writing process and evaluate how that process exits.
Just to be exatra clear, here's how I can do it with a wrapper:
#! /bin/bash
something_important > output
ERR=$!
if [ "$ERR" -ne "0" ] ; then
cat something_important | mail -s "something_important failed" me#example.com
fi
cat something_important >> /var/log/normal_log
Again, that's not what I want, I want to write a script and pipe commands into it.
Does that make sense? How would I do that? Am I missing something?
Thanks Everyone!
-Dylan
Yes it does make sense, and you are close.
Here are some advises:
#!/bin/sh
TEMPFILE=$(mktemp)
trap "rm -f $TEMPFILE" EXIT
if [ ! something_important > $TEMPFILE ]; then
mail -s 'something goes oops' -a $TEMPFILE you#example.net
fi
cat $TEMPFILE >> /var/log/normal.log
I won't use bashisms so /bin/sh is fine
create a temporary file to avoid conflicts using mktemp(1)
use trap to remove file when the script exit, normally or not
if the command fail
then attach the file, which would or would not be preferred over embedding it
if it's a big file you could even gzip it, but the attachment method will change:
# using mailx
gzip -c9 $TEMPFILE | uuencode fail.log.gz | mailx -s subject ...
# using mutt
gzip $TEMPFILE
mutt -a $TEMPFILE.gz -s ...
gzip -d $TEMPFILE.gz
etc.

Cannot terminate a shell command with Ctrl+c

Would someone please tell me why below bash statement cannot be terminated by Ctrl+c properly?
$( { ( tail -fn0 /tmp/a.txt & )| while read line; do echo $line; done } 3>&1 )
I run this statement, then two bash processes and one tail process are launched(got from ps auxf), then input Ctrl+c, and it won't quit to the bash prompt, at this moment, I see the two bash processes stopped, while the tail is still running, then I input something into /tmp/a.txt, then we could get into bash prompt.
What I want is, input Ctrl+c, then just quit into bash prompt without any relevant process left.
It will be more appreciative that someone explains the exact process of this statement, like a pipe causes the bash fork, something redirect to somewhere, etc.
Updated at Oct 9 2014:
Here provide some update in case it's useful to you.
My adopt solution is alike with 2 factors:
use a tmp pid file
( tail -Fn0 ${monitor_file} & echo "$!" >${tail_pid} ) | \
while IFS= read -r line; do
xxxx
done
use trap like: trap "rm ${tail_pid} 2>/dev/null; kill 0 2>/dev/null; exit;" INT TERM to kill relevant processes and remove remain files.
Please note, this kill 0 2 is bash specific, and 0 means all processes in the current process group.
This solution used a tmp pid file, while I still expect other solution without tmp pid file.
It works to trap the INT signal (sent by Ctrl-C) to kill the tail process.
$( r=$RANDOM; trap '{ kill $(cat /tmp/pid$r.pid);rm /tmp/pid$r.pid;exit; }' SIGINT EXIT; { ( tail -fn0 /tmp/a.txt & echo $! > /tmp/pid$r.pid )| while read line; do echo $line; done } 3>&1 )
(I use a random value on the PID file name to at least mostly allow multiple instances to run)

Ending Timestamp not printing on Shell Script: Using trap

I have a shell script I use for deployments. Since I want to capture the output of the entire process, I've wrapped it in a subshell and tail that out:
#! /usr/bin/env ksh
# deploy.sh
########################################################################
(yadda, yadda, yadda)
########################################################################
# LOGGING WRAPPER
#
dateFormat=$(date +"%Y.%m.%d-%H.%M.%S")
(
print -n "EXECUING: $0 $*: "
date
#
########################################################################
(yadda, yadda, yadda)
#
# Tail Startup
#
trap 'printf "Stopping Script: ";date;exit 0"' INT
print "TAILING LOG: YOU MAY STOP THIS WITH A CTRL-C WHEN YOU SEE THAT SERVER HAS STARTED"
sleep 2
./tailLog.sh
) 2>&1 | tee "deployment.$dateFormat.log"
#
########################################################################
Before I employed the subshell, the trap command worked. When you pressed CNTL-C, the program would print Stopping Script: and the date.
However, I wanted to make sure that no one forgets to save the output of this script, so I employed the subshell to automatically save the output. And, now trap doesn't seem to be working.
What am I doing wrong?
NEW INFORMATION
A little more playing around. I now see the issue isn't the shell or subshell. It's the damn pipe!
If I don't pipe the output to tee, the trap works fine. If I pipe the output to tee, the trap doesn't work.
So, the real question is how do I tee the output and still be able to use trap?
TEST PROGRAM
Before you answer, please, please, try these test programs:
#! /bin/ksh
dateFormat=$(date +"%Y.%m.%d-%H:%M:%S")
(
trap 'printf "The script was killed at: %s\n", "$(date)"' SIGINT
echo "$0 $*"
while sleep 2
do
print -n "The time is now "
date
done
) | tee somefile
And
#! /bin/ksh
dateFormat=$(date +"%Y.%m.%d-%H:%M:%S")
(
trap 'printf "The script was killed at: %s\n", "$(date)"' SIGINT
echo "$0 $*"
while sleep 2
do
print -n "The time is now "
date
done
)
The top one pipes to somefile..... The bottom one doesn't. The bottom one, the trap works. The top one, the trap doesn't. See if you can get the pipe to work and the "The script was killed at" line to print into the teed out file.
The pipe does work. The trap doesn't, but only when I have the pipe. You can move the trap statement all around and put in layers and layers of sub shells. There's some minor thing I am doing that's wrong, and I have no idea what it is.
Since trap stops the running process – logShell.sh – I think the pipe doesn't get executed at all. You can't do it this way.
One solution could be editing logShell.sh to write line by line in your log file. Maybe you could post it and we can discuss how you manage it.
OK, now I've got it. You have to use tee with -i to ignore interrupt signals.
#! /bin/ksh
dateFormat=$(date +"%Y.%m.%d-%H:%M:%S")
(
trap 'printf "The script was killed at: %s\n", "$(date)"' SIGINT
echo "$0 $*"
while sleep 2
do
print -n "The time is now "
date
done
) | tee -i somefile
this one works fine!

Resources