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

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

Related

Getting a Process ID & Assigned to a variable and kill the process in Bash Script

I store in a variable an executed command like so:
pio device monitor -p COM22 -b 115200 --no-reconnect | grep -E -o -m 1 '[A-Za-z0-9]{12}'
pio device monitor is a command to watch custom logs from an esp32, I use these logs to extract the wanter value (something like C8F09E0AA13C).
I want to run this command in the background and when the command return something, kill the process.
The script works as expected. The command stored in IDFound run in the background and as soon it found something the loop stop and the process is killed and I get the wanted value.
# /bin/bash
regex='[A-Za-z0-9]{12}'
port=COM22
unset -v IDFound
if ! [[ $IDFound =~ $regex ]] ; then
echo 'Please press the reset button of the ESP-prog'
IDFound=$(pio device monitor -p $port -b 115200 --no-reconnect | grep -E -o -m 1 $regex &)
# Get PID of this command
pid=$(ps -ef | awk '/pio/{print $2}')
# kill the process with PID
kill $pid
fi
echo $IDFound
But my problem is that I get PID with the pio keyword and I run another similar command in my script. I don't want to kill the wrong process.
I want to get the PID with $! to avoid mistake. How could I achieve that ?

Check if bash script already running except itself with arguments

So I've looked up other questions and answers for this and as you can imagine, there are lots of ways to find this. However, my situation is kind of different.
I'm able to check whether a bash script is already running or not and I want to kill the script if it's already running.
The problem is that with the below code, -since I'm running this within the same script- the script kills itself too because it sees a script already running.
result=`ps aux | grep -i "myscript.sh" | grep -v "grep" | wc -l`
if [ $result -ge 1 ]
then
echo "script is running"
else
echo "script is not running"
fi
So how can I check if a script is already running besides it's own self and kill itself if there's another instance of the same script is running, else, continue without killing itself.
I thought I could combine the above code with $$ command to find the script's own PID and differentiate them this way but I'm not sure how to do that.
Also a side note, my script can be run multiple times at the same time within the same machine but with different arguments and that's fine. I only need to identify if script is already running with the same arguments.
pid=$(pgrep myscript.sh | grep -x -v $$)
# filter non-existent pids
pid=$(<<<"$pid" xargs -n1 sh -c 'kill -0 "$1" 2>/dev/null && echo "$1"' --)
if [ -n "$pid" ]; then
echo "Other script is running with pid $pid"
echo "Killing him!"
kill $pid
fi
pgrep lists the pids that match the name myscript.sh. From the list we filter current $$ shell with grep -v. It the result is non-empty, then you could kill the other pid.
Without the xargs, it would work, but the pgrep myscript.sh will pick up the temporary pid created for command substitution or the pipe. So the pid will never be empty and the kill will always execute complaining about the non-existent process. To do that, for each pid in pids, I check if the pid exists with kill -0. If it does, then it is outputted, effectively filtering all nonexistent pids.
You could also use a normal for loop to filter the pids:
# filter non-existent pids
pid=$(
for i in $pid; do
if kill -0 "$i" 2>/dev/null; then
echo "$i"
fi
done
)
Alternatively, you could use flock to lock the file and use lsof to list current open files with filtering the current one. As it is now, I think it will kill also editors that are editing the file and such. I believe the lsof output could be better filtered to accommodate this.
if [ "${FLOCKER}" != "$0" ]; then
pids=$(lsof -p "^$$" -- ./myscript.sh | awk 'NR>1{print $2}')
if [ -n "$pids" ]; then
echo "Other processes with $(echo $pids) found. Killing them"
kill $pids
fi
exec env FLOCKER="$0" flock -en "$0" "$0" "$#"
fi
I would go with either of 2 ways to solve this problem.
1st solution: Create a watchdog file lets say a .lck file kind of on a location before starting the script's execution(Make sure we use trap etc commands in case script is aborted so that .lck file should be removed) AND remove it once execution of script is completed successfully.
Example script for 1st solution: This is just an example a test one. We need to take care of interruptions in the script, lets say script got interrupted by a command or etc then we could use trap in it too, since at that time it would have not been completed but you may need to kick it off again(since last time it was not completed).
cat file.ksh
#!/bin/bash
PWD=`pwd`
watchdog_file="$PWD/script.lck"
if [[ -f "$watchdog_file" ]]
then
echo "Please wait script is still running, exiting from script now.."
exit 1;
else
touch $watchdog_file
fi
while true
do
echo "singh" > test1
done
if [[ -f "$watchdog_file" ]]
then
rm "$watchdog_file"
fi
2nd solution: Take pid of current running shell using $$ save it in a file. Then check if that process is still running come out of script if NOT running then move on to run statements in script.

How to stop a running script which calls a infinite loop

I'm writing a bash script for kicking up an uncertain program. The run time of the program is unknown. The script will also kick up a while loop for using linux commands or perf to record something in a 1 second manner.
./my_app &
$i=1
while true;
do
perf stat -a -A -e writeback:writeback_dirty_page sleep $i >> out
done
How can I stop the while loop while my_app is finished? Thank you.
Make your while loop conditional on the process id of the app existing:
./my_app &
app_pid=$!
i=1
while ps -p $app_pid >/dev/null 2>&1
do
perf stat -a -A -e writeback:writeback_dirty_page sleep $i >> out
done
Get the pid using
echo $!
then
kill
you can send kill signal from my_app to the process that spawn my_app
Here is the real example
test.sh
#!/bin/bash
./my_app.sh $$ &
while [ 1 ]
do
echo running....
sleep 2
done
my_app.sh
#!/bin/bash
sleep 10
kill -9 $1

bash. while loop with xargs kill -9

I have a list of IP addresses and I have to run a command for every single IP address.
I did it with this code:
array=($(</tmp/ip-addresses.txt))
for i in "${array[#]}"; do
./command start $i &
done
Now, the list of IP addresses is constantly refreshed every 2 minutes and I need to kill every command that is no longer with the new IP addresses.
Practically, the command needs to be executed again every 2 minutes with the refreshed IP addresses and all the another old IP needs to be killed.
How can I do that?
A simple workaround: (Not tested)
sleep_delay=120 # 2 mins
while true; do
(
array=($(</tmp/ip-addresses.txt))
for i in "${array[#]}"; do
./command start $i &
done
sleep $(( sleep_delay + 2 )) # 2 can be any number >0
) & PPID=$!
sleep $sleep_delay
pkill -9 -p $PPID
done
Note: I have not optimized your code, just added a wrapper around your code.
EDIT:
Edited code to satisfy requirement that the old processes should not be killed, if the IP is still same.
NOTE: I haven't tested the code myself, so be careful while using the kill command. You can test by putting echo before the kill statement. If it works well, you can use the script...
declare -A pid_array
while true; do
array=($(</tmp/ip-addresses.txt))
for i in `printf "%s\n" ${!pid_array[#]} | grep -v -f <(printf "%s\n" ${array[#]})`; do
kill -9 ${pid_array[$i]} # please try to use better signal to kill, than SIGKILL
unset pid_array[$i]
done
for i in "${array[#]}"; do
if [ -z "${pid_array[$i]}" ]; then
./command start $i & pid_array[$i]=$!
fi
done
sleep 120
done
Your script would be changed like:
#!/bin/bash
GAP=120
while :
do
#Do your stuff here
sleep $GAP
done
exit 0
After two minutes it would read from refreshed file

Check if Mac process is running using Bash by process name

How do you check if a process on Mac OS X is running using the process's name in a Bash script?
I am trying to write a Bash script that will restart a process if it has stopped but do nothing if it is still running.
Parsing this:
ps aux | grep -v grep | grep -c [-i] $ProcessName
...is probably your best bet.
ps aux lists all the currently running processes including the Bash script itself which is parsed out by grep -v grep with advice from Jacob (in comments) and grep -c [-i] $ProcessName returns the optionally case-insensitive integer number of processes with integer return suggested by Sebastian.
Here's a short script that does what you're after:
#!/bin/bash
PROCESS=myapp
number=$(ps aux | grep -v grep | grep -ci $PROCESS)
if [ $number -gt 0 ]
then
echo Running;
fi
EDIT: I initially included a -i flag to grep to make it case insensitive; I did this because the example program I tried was python, which on Mac OS X runs as Python -- if you know your application's case exactly, the -i is not necessary.
The advantage of this approach is that it scales with you -- in the future, if you need to make sure, say, five instances of your application are running, you're already counting. The only caveat is if another application has your program's name in its command line, it might come up -- regular expressions to grep will resolve that issue, if you're crafty (and run into this).
Research the Darwin man pages for ps, grep, and wc.
A shorter solution:
if pgrep $PROCESS_NAME; then
echo 'Running';
fi
Explanation:
pgrep exits with 0 if there is a process matching $PROCESS_NAME running, otherwise it exist with 1.
if checks the exit code of pgrep, and, as far as exit codes go, 0 is success.
Another way is to use (abuse?) the -d option of the killall command. The -d options won't actually kill the process, but instead print what will be done. It will also exit with status 0 if it finds a matching process, or 1 if it does not. Putting this together:
#!/bin/bash
`/usr/bin/killall -d "$1" &> /dev/null`
let "RUNNING = ! $?" # this simply does a boolean 'not' on the return code
echo $RUNNING
To give credit where its due, I originally pulled this technique from a script in the iTunes installer.
This simple command will do the trick. The brackets around the process name prevent the grep command from showing in the process list. Note there is no space after the comma. There may be some portability issues as ps on some unix systems may require a dash before the options:
ps axo pid,command | grep "[S]kype"
The advantage is that you can use the results in an if statement like this:'
if [[ ! $(ps axo pid,command | grep "[i]Tunes.app") ]]; then
open -a iTunes
fi
Or if you prefer this style:
[[ ! $(ps axo pid,command | grep "[S]kype") ]] && open -a Skype || echo "Skype is up"
Another advantage is that you can get the pid by adding a pipe to awk '{print $1}'.
echo "iTunes pid: $(ps axo pid,command | grep "[i]Tunes.app" | awk '{print $1}')"
You can use either killall or kill, depending on if you are trying to find the task by PID or by name.
By Name:
if ! killall -s -0 $PROCESS_NAME >/dev/null 2>&1; then
# Restart failed app, or do whatever you need to prepare for starting the app.
else
at -f $0 +30seconds # If you don't have this on cron, you can use /usr/bin/at
fi
By PID:
if ! kill -0 $PID 2>/dev/null; then
# Restart app, do the needful.
else
at -f $0 +30seconds
fi
If you look at the OSX Manual you will see a different set of process management commands; since it's not the linux kernel, it makes sense that they would manage processes differently.
https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/killall.1.html
A sample output from my terminal (striking out the user and hostname, of course):
user#localhost:~$ kill -0 782 # This was my old, stale SSH Agent.
bash: kill: (782) - No such process
user#localhost:~$ echo $?
1
user#localhost:~$ kill -0 813 # This is my new SSH agent, I only just created.
user#localhost:~$ echo $?
0
The return code from a kill -0 will always result in a safe way to check if the process is running, because -0 sends no signal that will ever be handled by an application. It won't kill the application, and "kill" is only called "kill" because it's usually used to stop an application.
When you look at the interfaces it uses in the source, you'll see that it's actually interacting with the process table directly (and not grepping a potentially loaded output from ps), and just sending a signal to an application. Some signals indicate the application should shutdown or stop, while other signals tell it to restart services, or re-read configuration, or re-open file descriptors to log files that have been recently rotated. There are a plethora of things that "kill" and "killall" can do that doesn't terminate the application, and it's used regularly to simply send a signal to the application.
I lack the reputation to comment on the killall answer above, but there's killall -s for doing it without sending any signals:
killall -s "$PROCESSNAME" &> /dev/null
if [ $? -eq 0 ]; then
echo "$PROCESSNAME is running"
# if you also need the PID:
PID=`killall -s "$PROCESSNAME" | awk '{print $3}'`
echo "it's PID is $PID"
fi
It has for sure!
pgrep, pkill and pfind for OpenBSD and Darwin (Mac OS X)
http://proctools.sourceforge.net
(also available via MacPorts: port info proctools )
pidof by nightproductions.net
I've extended a pidof script found on the net to use regular expressions (usually substrings) and be case insensitive
#!/bin/sh
ps axc |awk "BEGIN{ n=tolower(\"$1\")}\
tolower(\$5) ~n {print \$1}";
just create a script named "pidof" with this content, and put it in you path, i.e. in one of the dirs in
echo $PATH
and make it executable (maybe using sudo)
chmod 755 /usr/local/bin/pidof
and use it like this, of course
kill -9 `pidof pyth`
does Mac have pidof? ...
if pidof $processname >/dev/null ; then echo $processname is running ; fi
Perhaps too late for the OP but this may help others who find this thread.
The following modification of the amrox theme above works well for restarting applications on my OS X:
killall -d TextEdit &> /dev/null && killall TextEdit &> /dev/null; open -a TextEdit
I use the following AppleScript to update and restart daemons:
tell application "System Events" to set pwd to POSIX path of container of (path to me)
do shell script "launchctl unload -w /Library/LaunchDaemons/time-test.plist; cp -f " & quoted form of pwd & "/time-test.plist /Library/LaunchDaemons; launchctl load -w /Library/LaunchDaemons/time-test.plist" with administrator privileges
It assumes the original or updated plist file is in the same directory as the AppleScript.

Resources