Bash: number of children in cron? - linux

The sample should look if another instance of itself is already running. I do not like a pid-file solution.
Why is n=2 if I start in the console and n=3 if I let cron do it?
#!/bin/bash
count ()
{
ps aux | grep -v grep | grep -v tail | grep $0 | wc -l
}
n=$(count)
echo "`date` n=$n" >> /root/test.log

Related

Is it possible to put a pipeline in a variable

I have a long pipeline that I'm constantly reusing in my script, and to make it easy to read I want to put the pipeline in a variable. Is it possible?
cat miami.tmp | grep -A5 "$date" | grep -A3 "$nexthour" | grep "celsius" | grep -E -o '[-]?[0-9].[0-9]' | head -n 1 >> miami.txt
I have tried
temperature=$( | grep -A5 "$date" | grep -A3 "$nexthour" | grep "celsius" | grep -E -
o '[-]?[0-9].[0-9]' | head -n 1 )
or
temperature="| grep -A5 "$date" | grep -A3 "$nexthour" | grep "celsius" | grep -E -o '[-]?[0-9].[0-9]' | head -n 1"
but get errors saying the commands weren't found.
This is a good case for using bash's shell functions. You can define a function like this:
function temperature() { grep foo | grep bar | grep baz; }
just make sure that the last command ends with a semicolon. You call the function with
cat file.tmp | temperature
Functions can also have parameters, accessed with the usual $1, $2 etc. notation, that can be passed in (space-separated) to the function.
$ function hello() { echo "Hello $1!"; }
$ hello world
Hello world!
You should put it in a function.
temperature () {
grep -A5 "$date" |
grep -A3 "$nexthour" |
grep "celsius" |
grep -E -o '[-]?[0-9].[0-9]' |
head -n 1
}
Maybe you want to make the date and the hour into parameters.
temperature () {
grep -A5 "$1" |
grep -A3 "$2" |
grep "celsius" |
grep -E -o '[-]?[0-9].[0-9]' |
head -n 1
}
Separately, this looks like it desperately wants to be refactored to Awk.
temperature () {
awk -v date="$1" nexthour="$2" '
$0 ~ date { p=5 }
p && p-- && ($0 ~ nexthour) { p=3 }
p && p-- && /celsius/ { n = split($0, a, /[^-.0-9]/, a);
for(i=1; i<=n; ++i) if (a[i] ~ /^-?[0-9]\.[0-9]$/)
{ print(a[i]); exit }'
}
(Untested, as you don't supply test data. I had to guess some things. If you are calling it by systematically looping over dates and hours, probably refactor that into the Awk script, too.)
Usage:
temperature 2022-11-24 04 <miami.tmp >>miami.txt
Probably see also https://mywiki.wooledge.org/BashFAQ/050

Linux CLI, using var into echo with pipes

I'm trying to use the result of a pipe into a "var" and then use it on a echo like this way:
ls -al --time-style=+%D |
grep `date +%D` |
wc -l |
HERE I DON'T KNOW HOW TO ASSING THE VALUE OF THE PREVIOUS PIPE INTO var |
echo "the number is %var"
The idea is to print "the number is 3" (because ls -al --time-style=+%D | date +%D | wc -l is 3)
How can I do it?
One way, using command substitution:
$ var=$(ls -al --time-style=+%D | grep `date +%D` | wc -l)
$ echo "the number is $var"
the number is 3
$

"if" conditions not working as expected

I am seeing two issues with my Perl script:
My if condition that has ==, ., != etc. is failing
The ps -p $pid -o etime= throws an error sh: line 1: -o: command not found
I am trying to check whether a process is already running, and if so
If it has been running for more than 40 minutes then kill the process
If it has been running for 30-40 minutes then issue a notification
If it has been running for less than 30 minutes then exit
A simple if test fails too. I have attached example code at the end.
Could some one please let me know the cause of these issues?
if ( `ps -ef | grep example | grep -v grep` ) {
print "Process is already running\n";
my $pid = `ps -ef | grep example | grep -v grep | awk '{print \$2}'`;
if ( `ps -p $pid -o etime= | sed -e "s/:/\\n/g" | wc -l | grep 3` ) {
print "1. Running for more than 40 mins\n";
`ps -ef | grep example | grep -v grep | awk '{print \$2}' | xargs kill -9`;
}
elsif ( `ps -p $pid -o etime= | sed -e "s/:/\\n/g" | wc -l|grep "2"` ) {
my $pmin = `ps -p $pid -o etime= | awk -F: '{print \$1}'`;
if ( $pmin < 30 ) {
print "Process running for 15 mins. Exiting";
exit;
}
elsif ( $pid >= 40 ) {
print "2.Running for more than 40 mins\n";
`ps -ef | grep example | grep -v grep | awk '{print \$2}' | xargs kill -9`;
}
else {
print "Process running for 30 mins. Notify";
}
}
}
my $psc = `ps -ef | grep example | grep -v grep >/dev/null 2>&1 && echo "Yes" || echo "No"`;
print "PSC - $psc";
if ( $psc eq "Yes" ) {
print "running";
}
else {
print "not running";
}
./test.pl
PSC - Yes
not running
I think this might be the root of your problem:
elsif ($pid >= 40) {
Because that's the process ID. Not $pmin. So you're basically killing and process ID >40, which will be almost any process, apart from occasionally if it randomly gets a low pid.
But pretty fundamentally - shelling out to ps and grep is painful. Replacing : with \n and then counting lines is a bit of a nasty thing to do - and then using grep to match a string is also pretty dirty.
Why not rewrite using something like Proc::ProcessTable instead?
Here's an example of how you'd read the process table, find a particular process id (or set of) and then query the time:
#!/usr/bin/env perl
use strict;
use warnings;
use Proc::ProcessTable;
use Data::Dumper;
my $ps = Proc::ProcessTable->new;
my #target_processes = grep { $_->pid eq $$ } #{ $ps->table };
print Dumper \#target_processes;
sleep 10;
while (1) {
foreach my $process ( grep { $_->cmndline =~ m/perl/ } #{ $ps->table } ) {
sleep 5;
print $process ->cmndline, " has been running for ",
$process->time / 10000, "s\n";
print Dumper \$process;
}
}
Note - the time is a high res time
Try the below change
ps -A | grep firefox >/dev/null 2>&1 && echo "Yes" || echo "No"

Command substitution as a variable in one-liner

I get the following error:
> echo "${$(qstat -a | grep kig):0:7}"
-bash: ${$(qstat -a | grep kig):0:7}: bad substitution
I'm trying to take the number before. of
> qstat -a | grep kig
1192530.perceus- kigumen lr_regul pbs.sh 27198 2 16 -- 24:00:00 R 00:32:23
and use it as an argument to qdel in openPBS so that I can delete all process that I started with my login kigumen
so ideally, this should work:
qdel ${$(qstat -a | grep kig):0:7}
so far, only this works:
str=$(qstat -a | grep kig); qdel "${str:0:7}"
but I want a clean one-liner without a temporary variable.
The shell substring construct you're using (:0:7) only works on variables, not command substitution. If you want to do this in a single operation, you'll need to trim the string as part of the pipeline, something like one of these:
echo "$(qstat -a | grep kig | sed 's/[.].*//')"
echo "$(qstat -a | awk -F. '/kig/ {print $1}')"
echo "$(qstat -a | awk '/kig/ {print substr($0, 1, 7)}')"
(Note that the first two print everything before the first ".", while the last prints the first 7 characters.) I don't know that any of them are particularly cleaner, but they do it without a temp variable...
qstat -u palle | cut -f 1 -d "." | xargs qdel
Kills all my jobs... normally I grep out the jobname(s) before cut'ing...
So I use a small script "idlist":
qstat -u palle | grep -E "*.in" | grep -E "$1" | cut -f 1 -d "." | xargs
To see all my "map_..." jobs:
idlist "map_*"
For killing all my "map_...." jobs:
idlist "map_*" | xargs qdel
yet another ways :
foreach m1 in $(qstat -a );do
if [[ $m1 =~ kig ]];then
m2=${m1%.kig}
echo "kig found $m2 "
break
fi
done

How to sleep for 1 second between each xargs command?

For example, if I execute
ps aux | awk '{print $1}' | xargs -I {} echo {}
I want to let the shell sleep for 1 second between each echo.
How can I change my shell command?
You can use the following syntax:
ps aux | awk '{print $1}' | xargs -I % sh -c '{ echo %; sleep 1; }'
Be careful with spaces and semicolons though. After every command in between brackets, semicolon is required (even after the last one).
Replace echo by some shell script named sleepecho containing
#!/bin/sh
sleep 1
echo $*
If your awk supports it:
ps aux | awk '{ system("sleep 1"); print $1 }' | xargs -I {} echo {}q
or skip awk and xargs altogether
ps aux | while read -r user rest;
echo $user
sleep 1;
done

Resources