Shell Script:How to get all the Process Id which are using a file-system/directory - bash

I am checking the processes which is using a file system. Now when I do fuser there are hundreds of processes are coming.
fuser -cu /xyz
Output truncated:
393ce(xyz) 1044c(root) 1068cm(oracle) 2065ce(xyz) 3729ce(xyz)
I want just process id in file separated by newline character so that I can run a loop to check the processes.

If you only want id instead of id(user) then don't use the -u option. Documentation of fuser -u:
-u, --user
Append the user name of the process owner to each PID.
For me, fuser -c / has a different format than your sample. Each id is followed by letters denoting the type of access. The letters are printed to stderr, therefore I will use 2>&- to hide them.
$ fuser -c /
/: 1717rce 1754rce 1765rce 1785rce ...
$ fuser -c / 2>&-
1717 1754 1765 1785 ...
You can use grep to print one id per line:
$ fuser -c / 2>&- | grep -o '[0-9]*'
1717
1754
1765
1785
...
However, to run a loop you don't need one id per line. Ids separated by spaces work as well:
for id in $(fuser -c / 2>&-); do
echo "id = $id"
done

Related

How to check if a specific executable has a live process

I want to write a script that checks periodically if a specific executable has a live process, something like this:
psping [-c ###] [-t ###] [-u user-name] exe-name
-c - limit amount of pings, Default is infinite
-t - define alternative timeout in seconds, Default is 1 sec
-u - define user to check process for. The default is ANY user.
For example, psping java will list all processes that are currently invoked by the java command.
The main goal is to count and echo the number of live processes for a user, whose executable file is exe-name, java in the above example.
I wrote a function:
perform_ping(){
ps aux | grep "${EXE_NAME}" | awk '{print $2}' | while read PID
do
echo $PID # -> This will echo the correct PID
# How to find if this PID was executed by ${EXE_NAME}
done
fi
sleep 1
}
I'm having a hard time figuring out how to check if a specific executable file has a live process.
To list all processes that opens a file, we can use the lsof command. Because an executable must be opened in order to be run, we may just use lsof for this purpose.
The next problem is that when we run a java file, we simply type java some_file, and if we issue lsof java it will coldly says that lsof: status error on java: No such file or directory because the java is actually /usr/bin/java.
To convert from java to /usr/bin/java we can use which java, so the command would be:
lsof $(which $EXE_FILE)
The output may looks like this:
lsof: WARNING: can't stat() tracefs file system /sys/kernel/debug/tracing
Output information may be incomplete.
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python3 26969 user txt REG 8,1 4526456 15409 /usr/bin/python3.6
In this case I searched python3 as lsof $(which python3). It will report the PID in the second field. But when there's another user that invokes python3 too, lsof will issue the warning on stderr like the first two lines because it cannot read other users info. Therefore, we modify the command as:
lsof $(which python3) 2> /dev/null
to suppress the warning. Then we're almost there:
lsof $(which python3) 2> /dev/null | awk 'NR > 1 { print $2 }'
Then you can use read to catch the PID.
Edit: how to list all processes for all users?
By default lsof doesn't read process for a specific file, but after further reading man lsof I found that there are options that meet your needs.
-a causes list selection options to be ANDed.
-c c selects the listing of files for processes executing the command that begins with the characters of c. Multiple commands may be specified, using multiple -c options.
-u s selects the listing of files for the user whose login names or user ID numbers are in the comma-separated set s.
Therefore, you can use
lsof -c java
to list all commands that are run by java. And to see a specific user, add -u option as
lsof -a -c java -u user
-a is needed for the AND operation. If you run this command you will see multiple entry for a process, to unique them, run
lsof -c java 2> /dev/null | sed 1d | sort -uk2,2
Also please notice that users may run their own java in their path and therefore you have to decide which one to monitor: java or /usr/bin/java.

pgrep command doesn't show bash script

When I run a simple bash script, say myscript.sh
#!/bin/bash
sleep 30
from the terminal, and then do pgrep myscript.sh I don't get any result. Why?
You're probably doing this:
pgrep myscript.sh
This won't show the process you're running because it is /bin/bash that is running your script.
You should be doing:
pgrep -fl myscript.sh
To list your process.
As per man pgrep:
-f Match the pattern anywhere in the full argument string of the process instead of just the executable name.
Your just running your bash script, u need to use -f flag, please check man page
Check pgrep is installed in your machine. Just do man pgrep, if you get command not found that install the utility.
pgrep looks through the currently running processes and lists the process IDs which matches the selection criteria to stdout. All the criteria have to match.
Example usage:
pgrep name | xargs kill
If you use pgrep name | kill, the ouput of pgrep name is feed to stdin of kill. Because kill does not read arguments from stdin, so this will not work.
Using xargs, it will build arguments for kill from stdin. Example:
$ pgrep bash | xargs echo
5514 22298 23079

how to extract the PID of a process by command line

I want to get the PID of a process namely "cron" by command line.
I tried the following script.
ps ax|grep 'cron'
but I am getting a part of a table,
1427 ? Ss 0:00 /usr/sbin/cron -f
24160 pts/5 S+ 0:00 grep --color=auto cron
How I extract the pid from this ?
The pgrep utility will return the process IDs for the currently running processes matching its argument:
$ pgrep cron
228
It may also be used to "grep for" things on the command line:
$ pgrep -f uerfale
69749
69752
$ pgrep -l -f uerfale
69749 slogin uerfale
69752 slogin: /home/kk/.ssh/sockets/uerfale-9022-kk.sock [mux] m
To kill a process by name, use pkill. It works in the same way as pgrep but will send a signal to the matched processes instead of outputting a process ID.
Just use pidof, rather to use other commands and apply post-processing actions on them.
$ pidof cron
22434
To make the command return only one PID pertaining to to the process, use the -s flag
-s
Single shot - this instructs the program to only return one pid.
Like this, for example:
ps -ef|grep 'cron'|grep -v grep|awk '{print $2}'
You can try this;
ps -o pid,sess,cmd afx | egrep "( |/)cron( -f)?$"
or
pstree -pas <cronPID>

Running commands remotely returns unexpected output

I want to kill a process remotely and I use ssh -i command like this:
ssh -i $app_rsa_file_path $app_server_user#$app_server_ip "ps aux | grep java | grep billing | awk '{print $2}' | xargs kill -9 > /dev/null 2>&1"
if I run command directly on the server, it works fine; but in remote version it seems that awk has no effect and the command passes the whole output of ps aux | grep java | grep billing to kill and I get this:
kill: invalid option -- 'D'
Usage:
kill [options] <pid> [...]
Options:
<pid> [...] send signal to every <pid> listed
-<signal>, -s, --signal <signal>
specify the <signal> to be sent
-l, --list=[<signal>] list all signal names, or convert one to a name
-L, --table list all signal names in a nice table
-h, --help display this help and exit
-V, --version output version information and exit
For more details see kill(1).
Any idea about the reason?
you use double-quotes " so you need to escape the $ special character:

How to access the PID from an lsof.

Given the following command lsof -i:1025 I get:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ruby 12345 john 11u IPv4 0xb2f4161230e18fd57 0t0 TCP localhost:foobar (LISTEN)
I am trying to write a script to get that PID (12345) and kill it. At the moment I have to run lsof -i:1025, get that PID and then run kill -9 12345.
The lsof(8) man page says:
-t specifies that lsof should produce terse output with process
identifiers only and no header - e.g., so that the output
may be piped to kill(1). -t selects the -w option.
You can use lsof -t -i:1025 | xargs kill -9.
Something like:
#!/bin/bash --
x=`lsof -Fp -i:1025`
kill -9 ${x##p}
Should do it. The 3rd line runs lsof using the -F option to get just the pid, with a leading p. The next line drops the leading p from the output of lsof and uses the result as the pid in a kill command.
Edit: At some point lsof was modified so the file descriptor preceded by an f is always output, whether you ask for it or not (which makes no sense to me, but what do I know). While you could put a | grep '^p' in the back quotes, an easier way is to use the -t option, as noted in fabianopinto's answer below.
man lsof says that you can use -F to specify fields to to be output for processing by other programs. So you can do something like
lsof -i:1025 -Fp | sed 's/^p//' | xargs kill -9
Further to #blm's answer, it didn't work for me exactly because the output of the lsof command was:
p4679
f33
So with the ${x##p} was
4679
f33
The solution
Grab only the first line with | head -n 1:
x=`lsof -Fp -i:"$1" | head -n 1`
kill -9 ${x##p}
And furthermore from #blm's and #Mosh Feu's answers:
lsof -i:1337 -Fp | head -n 1 | sed 's/^p//' | xargs kill
is what ended up doing the trick for me.
I recommend adding this as a bash function and aliasing it
alias kbp='killByPort'
killByPort() {
lsof -i:$1 -Fp | head -n 1 | sed 's/^p//' | xargs kill
}
This shortcut will kill the process quickly for you
kill -9 $(lsof -t -i :3000)
for fish shell users, simply remove the $ sign, so
kill -9 (lsof -t -i :3000)

Resources