Perl fails to kill self pid when running from bash script - perl

Following code behaves as expected when running from terminal:
perl -e 'kill -2, $$; warn HERE, $/'
It sends itself SIGINT and dies before reaching "HERE":
~# perl -e 'kill -2, $$; warn HERE, $/'
~# echo $?
130
~#
The problem: same code fails to kill self PID when running from shell script:
~# cat 1.sh
perl -e 'kill -2, $$; warn HERE, $/'
~#
~# sh 1.sh
HERE
~#
~# echo $?
0
~#
On the other hand, replacing perl's kill by a shell's one works OK:
~# cat 2.sh
perl -e 'qx/kill -2 $$/; warn HERE, $/'
~#
~# sh 2.sh
~#
~# echo $?
130
~#
Not really understand what is happening here, please help..

First of all,
kill -2, $$
is better written as
kill 2, -$$
An even better alternative is
kill INT => -$$
These send SIGINT to the specified process group.
Your main question appears to be why the two shells behave differently. This section explains that.
The process group represents an application.
When you launch a program from an interactive shell, it's not part of a larger application, so the shell creates a new process group for the program.
However, processes created by a script (i.e. a non-interactive shell) are part of the same application as the script itself, so the shell doesn't create a new process group for them.
You can visualize this using the following:
sh -i <<< 'perl -e '\''system ps => -o => "pid,ppid,pgrp,comm"'\''' outputs the following:
$ perl -e 'system ps => -o => "pid,ppid,pgrp,comm"'
PID PPID PGRP COMMAND
8179 8171 8179 bash
14654 8179 14654 sh
14655 14654 14655 perl
14656 14655 14655 ps
$ exit
In interactive mode, perl is at the head of perl and ps's program group.
sh <<< 'perl -e '\''system ps => -o => "pid,ppid,pgrp,comm"'\''' outputs the following:
PID PPID PGRP COMMAND
8179 8171 8179 bash
14584 8179 14584 sh
14585 14584 14584 perl
14586 14585 14584 ps
In non-interactive mode, sh is at the head of perl and ps's program group.
Your failures are the result of not sending the signal to the head of the process group (i.e. the application). Had you checked, the error kill reported was ESRCH ("No such process").
ESRCH The pid or process group does not exist. [...]
To kill the current process's process group, replace the improper
kill INT => -$$ # XXX
with
kill INT => -getpgrp() # Kill the application
You can make your perl the head of its own process group by simply calling the following:
setpgrp();
Test:
$ sh <<< 'perl -e '\''system ps => ( -o => "pid,ppid,pgrp,comm" )'\'''
PID PPID PGRP COMMAND
8179 8171 8179 bash
16325 8179 16325 sh
16326 16325 16325 perl
16327 16326 16325 ps
$ sh <<< 'perl -e '\''setpgrp(); system ps => ( -o => "pid,ppid,pgrp,comm" )'\'''
PID PPID PGRP COMMAND
8179 8171 8179 bash
16349 8179 16349 sh
16350 16349 16350 perl
16351 16350 16350 ps
That's not something you normally want to do.
Finally, the Perl code
kill INT => -$pgrp
is equivalent to the following call of the kill command-line utility:
kill -s INT -$pgrp
kill -INT -$pgrp
kill -2 -$pgrp
You were missing - in your qx// program, so it was sending SIGINT to the identified process rather than the identified program group.

From your interactive terminal, the perl process kills the process group of which it is a part. (The shell runs perl in its own process group.) The shell reports this unusual termination in $?:
t0 interactive shell (pid=123, pgrp=123)
|
t1 +------> perl -e (pid=456, pgrp=456, parent=123)
| |
t2 (wait) kill(-2, 456) (in perl, same as kill pgrp 456 w/ SIGINT)
| |
t3 (wait) *SIGINT*
|
t4 report $?
From your shell script, the perl process kills a (likely) non-existent process group and then exits successfully. Your interactive shell makes a new process group in which to run your shell script, and that script then runs perl as a child in the same process group.
t0 shell (pid=123, pgrp=123)
|
t1 +-------> shell:1.sh (pid=456, pgrp=456, parent=123)
| |
t2 (wait) +-------------> perl -e (pid=789, pgrp=456, parent=456)
| | |
t3 (wait) (wait) kill pgrp 789 with SIGINT (error: no such pgrp)
| | |
t4 (wait) (wait) exit success
| |
t5 (wait) exit success
|
t6 report $?
In your backticked (qx//) example, your interactive shell starts a shell process with a new process group. (Not that it matters here, but that process runs perl in its same process group.) Perl then runs as its own child the system kill command, the semantics of which differ from that of the perl kill. This grandchild command sends a SIGINT to the perl PID directly, rather than a SIGINT to a process group. Perl terminates, and that exit code is conveyed as the script's exit code, since it was the last command in the script.
This diagram is a little busier than the previous:
t0 shell (pid=123, pgrp=123)
|
t1 +-------> shell:2.sh (pid=456, pgrp=456, parent=123)
| |
t2 (wait) +----------> perl -e (pid=789, pgrp=456, parent=456)
| | |
t3 (wait) (wait) +---------> /bin/kill SIGINT 789
| | | |
t4 (wait) (wait) *SIGINT* exit success
| |
t5 (wait) return $?
|
t6 report $?

It works fine in this way:
perl -E 'say "kill "INT", $$; warn HERE, $/'
perl -E 'say "kill 2, $$; warn HERE, $/'
kill man page says:
A negative signal name is the same as a negative signal number,
killing process groups instead of processes. For example, kill
'-KILL', $pgrp and kill -9, $pgrp will send SIGKILL to the entire
process group specified. That means you usually want to use positive
not negative signals.

Related

How do I automate killing a job in cron? [duplicate]

This question already has answers here:
Find and kill a process in one line using bash and regex
(30 answers)
Closed 1 year ago.
Sometimes when I try to start Firefox it says "a Firefox process is already running". So I have to do this:
jeremy#jeremy-desktop:~$ ps aux | grep firefox
jeremy 7451 25.0 27.4 170536 65680 ? Sl 22:39 1:18 /usr/lib/firefox-3.0.1/firefox
jeremy 7578 0.0 0.3 3004 768 pts/0 S+ 22:44 0:00 grep firefox
jeremy#jeremy-desktop:~$ kill 7451
What I'd like is a command that would do all that for me. It would take an input string and grep for it (or whatever) in the list of processes, and would kill all the processes in the output:
jeremy#jeremy-desktop:~$ killbyname firefox
I tried doing it in PHP but exec('ps aux') seems to only show processes that have been executed with exec() in the PHP script itself (so the only process it shows is itself.)
pkill firefox
More information: http://linux.about.com/library/cmd/blcmdl1_pkill.htm
Also possible to use:
pkill -f "Process name"
For me, it worked up perfectly. It was what I have been looking for.
pkill doesn't work with name without the flag.
When -f is set, the full command line is used for pattern matching.
You can kill processes by name with killall <name>
killall sends a signal to all
processes running any of the specified
commands. If no signal name is
specified, SIGTERM is sent.
Signals can be specified either by
name (e.g. -HUP or -SIGHUP ) or by number (e.g.
-1) or by option -s.
If the command name is not regular
expression (option -r) and contains a
slash (/), processes executing that
particular file will be selected for
killing, independent of their name.
But if you don't see the process with ps aux, you probably won't have the right to kill it ...
A bit longer alternative:
kill `pidof firefox`
The easiest way to do is first check you are getting right process IDs with:
pgrep -f [part_of_a_command]
If the result is as expected. Go with:
pkill -f [part_of_a_command]
If processes get stuck and are unable to accomplish the request you can use kill.
kill -9 $(pgrep -f [part_of_a_command])
If you want to be on the safe side and only terminate processes that you initially started add -u along with your username
pkill -f [part_of_a_command] -u [username]
Kill all processes having snippet in startup path. You can kill all apps started from some directory by for putting /directory/ as a snippet. This is quite usefull when you start several components for the same application from the same app directory.
ps ax | grep <snippet> | grep -v grep | awk '{print $1}' | xargs kill
* I would prefer pgrep if available
Strange, but I haven't seen the solution like this:
kill -9 `pidof firefox`
it can also kill multiple processes (multiple pids) like:
kill -9 `pgrep firefox`
I prefer pidof since it has single line output:
> pgrep firefox
6316
6565
> pidof firefox
6565 6316
Using killall command:
killall processname
Use -9 or -KILL to forcefully kill the program (the options are similar to the kill command).
On Mac I could not find the pgrep and pkill neither was killall working so wrote a simple one liner script:-
export pid=`ps | grep process_name | awk 'NR==1{print $1}' | cut -d' ' -f1`;kill $pid
If there's an easier way of doing this then please share.
To kill with grep:
kill -9 `pgrep myprocess`
more correct would be:
export pid=`ps aux | grep process_name | awk 'NR==1{print $2}' | cut -d' ' -f1`;kill -9 $pid
I normally use the killall command.
Check this link for details of this command.
I was asking myself the same question but the problem with the current answers is that they don't safe check the processes to be killed so... it could lead to terrible mistakes :)... especially if several processes matches the pattern.
As a disclaimer, I'm not a sh pro and there is certainly room for improvement.
So I wrote a little sh script :
#!/bin/sh
killables=$(ps aux | grep $1 | grep -v mykill | grep -v grep)
if [ ! "${killables}" = "" ]
then
echo "You are going to kill some process:"
echo "${killables}"
else
echo "No process with the pattern $1 found."
return
fi
echo -n "Is it ok?(Y/N)"
read input
if [ "$input" = "Y" ]
then
for pid in $(echo "${killables}" | awk '{print $2}')
do
echo killing $pid "..."
kill $pid
echo $pid killed
done
fi
kill -9 $(ps aux | grep -e myprocessname| awk '{ print $2 }')
If you run GNOME, you can use the system monitor (System->Administration->System Monitor) to kill processes as you would under Windows. KDE will have something similar.
The default kill command accepts command names as an alternative to PID. See kill (1). An often occurring trouble is that bash provides its own kill which accepts job numbers, like kill %1, but not command names. This hinders the default command. If the former functionality is more useful to you than the latter, you can disable the bash version by calling
enable -n kill
For more info see kill and enable entries in bash (1).
ps aux | grep processname | cut -d' ' -f7 | xargs kill -9 $
awk oneliner, which parses the header of ps output, so you don't need to care about column numbers (but column names). Support regex. For example, to kill all processes, which executable name (without path) contains word "firefox" try
ps -fe | awk 'NR==1{for (i=1; i<=NF; i++) {if ($i=="COMMAND") Ncmd=i; else if ($i=="PID") Npid=i} if (!Ncmd || !Npid) {print "wrong or no header" > "/dev/stderr"; exit} }$Ncmd~"/"name"$"{print "killing "$Ncmd" with PID " $Npid; system("kill "$Npid)}' name=.*firefox.*

Perl script is returning incorrect output of zombie process on Linux box

Perl script is returning incorrect value of zomibie process on Linux box
my $threshold = 5;
$number_of_defuncts = `ps -ef | grep defunct |grep -v grep|wc -l`;
if ( $number_of_defuncts > $threshold )
{
print("number of defunct is [$number_of_defuncts] \n");
}
WHen manually checked via ps command then zombie processes are always zero but using perl script is giving the erroneous output of 7, 8 or similar high number.
(linux only)
$zombie_count = do { local *ARGV; #ARGV=</proc/[0-9]*/stat>; grep /Z[^)]*$/, <> }
Just grepping for defunct in the ps output is broken, because a process may put defunct in its command line just to break your script. More robust (but yet not portable [1]) solutions are ps -eo state | grep Z or ps -eo s | grep Z.
In your case, your perl script is probably creating the extra zombies, which disappear when it terminates. Unlike the shell, perl will not greedily reap its children as soon as they die; it's up to you to wait() for them, either directly or indirectly:
$ perl -e 'my $pid = open my $fh, "echo yup|"; exec "ps", $pid'
PID TTY STAT TIME COMMAND
6840 pts/11 Z+ 0:00 [echo] <defunct>
$ perl -e 'my $pid = open my $fh, "echo yup|"; undef $fh; exec "ps", $pid'
PID TTY STAT TIME COMMAND
$ perl -e 'my $pid = open my $fh, "echo yup|"; wait; exec "ps", $pid'
PID TTY STAT TIME COMMAND
$ perl -e 'my $pid = open FH, "echo yup|"; close FH; exec "ps", $pid'
PID TTY STAT TIME COMMAND
[1] no, ps -ef is not portable, either.

How to pipe into grep using backticks in Linux shell script?

I am trying to execute, what I thought would be, a simple shell command within a script. When I execute this from the command prompt, it works well:
$ sudo cat /etc/sysconfig/network-scripts/ifcfg-en0 | grep "IPADDR"
IPADDR=192.168.1.10
However, if I put this into a shell script:
#!/usr/bin/sh
my_command=`sudo cat /etc/sysconfig/network-scripts/ifcfg-en0 | grep "IPADDR"`
${my_command}
echo $?
I get this error:
$ sudo ./myscript.sh
./myscript.sh: line 3: IPADDR=192.168.1.10: command not found
So, how can I successfully execute this line within my shell script?
Thanks!
The problem in your case is that you are executing the result of the command...
This line executes the code as it's between "``" that are special characters for executing the given string as a command:
my_command=`sudo cat /etc/sysconfig/network-scripts/ifcfg-en0 | grep "IPADDR"`
as a result, $my_command is "IPADDR=192.168.1.10"
Then you are trying to execute it for the second time:
${my_command}
Thats why you are getting this error. There is no such a command as "IPADDR=192.168.1.10".
Just use $my_command as a result that contains your desired grepped part and skip the ${my_command} line:
#!/usr/bin/sh
my_command=`sudo cat /etc/sysconfig/network-scripts/ifcfg-en0 | grep "IPADDR"`
echo $my_command

Perl processes disappears after some days

I have a perl file (test.pl).
It will work in recurring manner.
The purpose of the file is send emails from DB
Following is the code in test.pl
sub send_mail{
$db->connect();
# Some DB operations #
# Send mail #
$db->disconnect();
sleep(5);
send_mail();
}
send_mail();
Iam executing 5 instance of this file ,like as below
perl test.pl >> /var/www/html/emailerrorlog/error1.log 2>&1 &
perl test.pl >> /var/www/html/emailerrorlog/error2.log 2>&1 &
perl test.pl >> /var/www/html/emailerrorlog/error3.log 2>&1 &
perl test.pl >> /var/www/html/emailerrorlog/error4.log 2>&1 &
perl test.pl >> /var/www/html/emailerrorlog/error5.log 2>&1 &
if i execute the command ps -ef | grep perl | grep -v grep
I can see 5 instances of above mentioned perl file
That file will work perfectly for some days
But after some days, the perl processes will start to disappear one by one .
After some days all process will disappear.
Now. if i execute the command ps -ef | grep perl | grep -v grep ,I can't see any process,
I can't see any error log in the log files.
So, what may be the chances for disappearing the perl processes?
How can i debugg it ?
Where can i see the perl error log?
It has the same issue in Centos and Red Hat Linux
Any one have idea?
I'm not 100% sure if that is the problem but it would probably help if you avoid recursion in a permanently executing process... That slowly increases the stack use and will eventually kill the process when the stack size limit is reached.
try something like this instead:
sub send_mail{
$db->connect();
# Some DB operations #
# Send mail #
$db->disconnect();
}
while (1) {
send_mail();
sleep(5);
}

Can't find the process after invoking this Perl script in shell

t:
#!/usr/bin/perl
exec("perl -Ilib -d" . $ARGV[0]);
It's invoked as t perl_script.
But after that I can't find it by ps,and can't terminate it by ^C
What's wrong ?
http://perldoc.perl.org/functions/exec.html
You're exec'ing perl with the args and perl_script you pass in. This means the current script t ceases to exist and is replaced by perl -Ilib -dperl_script.
The process you're looking for with ps will be the one you passed in (perl_script)
Edit for comment from OP below:
The actual process is perl since that's what you exec'd, but you can certainly find it via the perl_script you passed in using grep:
$ ps -ef |grep perl_script
broach 13039 2264 0 01:08 pts/0 00:00:00 perl -Ilib -dperl_script
Do you need to include a space after -d? Otherwise you are exec'ing
perl -Ilib -dperl_script
instead of
perl -Ilib -d perl_script
Cleaner still:
exec("perl","-Ilib","-d",$ARGV[0]);
exec($^X, "-Ilib", "-d", $ARGV[0]);