Linux Top command with more than 20 commands - linux

I want to use top in order to monitor numerous processes by process name. I already know about doing $ top -p $(pgrep -d ',' <pattern>) but top only limits me to 20 pids. Is there a way to allow for more than 20 pids?
Do I have to use a combination of ps and watch to get similar results?

From top/top.c:
if (Monpidsidx >= MONPIDMAX)
error_exit(fmtmk(N_fmt(LIMIT_exceed_fmt), MONPIDMAX));
(where LIMIT_exceed_fmt is the error message you're getting).
And in top/top.h:
#define MONPIDMAX 20
I changed this number to 80, and that seems to work okay. Not sure why this hardcoded limit is so low.
So, if manually compiling procps-ng is an option, then you could do that. You don't need to replace the system top (or need root privileges), you can just put it in your homedir.
Another workaround might be using tmux or screen and multiple top instances.
Yet another possible solution might be using ps with a loop, ie.
#!/bin/sh
while :; do
clear
ps $*
sleep 1
done
Invoke it as: ./psloop.sh 42 666
You may want to add more flags to ps for additional info. Also be aware this is less efficient, since it will invoke 3 binaries every second.

A wrapper with watch. Tested with Ubuntu 11.04, Ubuntu 14.04, RHEL5, RHEL6 and RHEL7
Syntax: script.sh pid [ pid ...] # space separated
Example: script.sh $(pgrep -d ' ' <pattern>)
#!/bin/bash
i=10 # ~ interval in seconds
format()
{
a="$1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12}"
a="$a ${13} ${14} ${15} ${16} ${17} ${18} ${19} ${20}"
a="${a%%*( )}"; a="${a// /,}"
}
main()
{
format $#
top -b -n 1 -p $a
[ $# -gt 20 ] && shift 20 || shift $#
until [ $# -eq 0 ]; do
format $#
top -b -n 1 -p $a | sed '1,/PID/d;/^$/d'
[ $# -gt 20 ] && shift 20 || shift $#
done
}
if [ "$1" == "watch" ]; then
shift
shopt -s extglob
main $#
else
watch -t -n $i "$0 watch $#"
fi

Related

script that will monitor changes in at least 2 files/directories and execute a third script only when both are modified

I have two sensors that each create an entry in a text file when triggered. Now I need something to monitor these two files (i can also put them in 2 directories each if that helps in any way) and trigger a third script only when changes occur to both of the aforementioned files/directories. I need real-time (or near to it) between events and notification. I have found tools like inotify-wait, fswatch, entr and some others but all of these are triggered at any change.
At the moment I'm trying this but it does not work properly:
#!/bin/bash
while inotifywait -e modify /home/user/triggerdir/ ;
do
if [ "inotifywait -e modify /home/user/triggerdir2/" ];
then
echo Alert | mail -n -s "test-notify SCRIPT HUZAAAA" user#gmail.com
else
# Don't do anything unless we've found one of those
:
fi
done
I have looked for similar issues/solutions on the web, the closest would be this but it has no working answer.
Since you're having trouble with that, you might consider a simplistic approach.
Rather than a loop, I'd put your script in the crontab. Run it every day, every hour, every minute, whatever you need. If you need more often, you could loop, but make sure you at least sleep a second to be nice to the CPU.
If a minute or more between event and notification is ok this should be all you need:
#!/bin/bash
key=/some/safe/path/.hidden_filename
[[ -e "$key" ]] || touch "$key" # make sure it exists
if [[ file1 -nt "$key" && file2 -nt "$key" ]]; then
mail -n -s "test-notify SCRIPT HUZAAAA" user#gmail.com <<< "Alert!"
touch "$key"
fi
I have hacked something together which does work as I need it to though is terrible coding (probably shouldn't be called that)
3 scripts involved:
script 1:
#!/bin/bash
count=0
while :
do
{ inotifywait -e modify /home/user/triggerdir/ && let count="$count + 1"; } || exit 1
if [ "$count" -eq "2" ]; then
echo Alert | mail -n -s "Huzzah" user#gmail.com
/home/user/trigger2.sh &&
killall trigger.sh inotifywait
fi
done
script 2:
#!/bin/bash
count=0
while :
do
{ inotifywait -e modify /home/user/triggerdir/ && let count="$count + 1"; } || exit 1
if [ "$count" -eq "2" ]; then
echo Alert | mail -n -s "Huzzah" user#gmail.com
/home/user/trigger.sh &&
killall trigger2.sh #Do something.
# count=-250
fi
done
as the two scripts spawn bash/inotify processes I run once in 24 hours a cronjob two kill those using this script 3:
#!/bin/bash
killall trigger2.sh trigger.sh inotifywait bash
any help to improve is welcome, thanks :)

clear my script logs every 10 second

I have script with name : run.sh
This is my script code :
#!/usr/bin/env bash
install() {
sudo apt-get update
sudo apt-get upgrade
}
if [ "$1" = "install" ]; then
install
else
if [ ! -f ./tg/tgcli ]; then
echo "tg not found"
echo "Run $0 install"
exit 1
fi
#sudo service redis-server restart
#./tg/tgcli -s ./bot/bot.lua -l 1 -E $#
./tg/tgcli -s ./bot/bot.lua $#
fi
and when run this script give me output like this every second :
[09:54] 2014 Hello
[09:55] 2014 Hi
[09:57] 2014 How Are you ?
and many like this (thousands in hour !)
and my server get slow in 5 hour.
i check print commands in bot.lua but there are no way to remove print it.
can you add some codes to clear my script logs every 10 second ?
Thanks a lot.
My Script Output Doesn't Save Anywhere and Just Show me in terminal
I want a code such as clear command on linux terminal , clear my script logs every 10 minute or 5 minute.
After 5 day of script running i can (sometimes can't) login my server and my server get very slow and i must wait 3 or 5 minute to login my server and this amazing after login my server my server again get fast !
and i forgot say i use byobu screen for run my scripts and I think screen get my server slow down.
I don't think that something as simple as this would cause your server to slow down, but you can add a check to your script to calculate the size or line count of your log file every time it runs.
This function assumes you are redirecting your output to a log file. Set the variables to whatever makes the most sense.
log_check() {
line_count=$(wc -l $log_file | awk '{print $1}')
size_check=$(du -ax $log_file | awk '{print $1}')
max_file_size="1500"
max_file_length="1000"
if [[ $line_count >= $max_file_length || $size_check >= $max_file_size ]]; then
echo "" > $log_file
fi
}
I would also recommend using [[ ]] over [ ] since this is a bash script, as long as you don't plan in it being posix compliant and only plan on using it with bash [[]] is always better than [].
EDIT:
Since you are logging output to the terminal and not a file you can literally use the clear command in your script.
Try this out and see how the functionality works
for i in {1..20}; do
echo $i
if (( i == 10 )); then
clear
fi
done
I'm assuming your code has a loop somewhere, if not it will be a bit more complex to clear the terminal session. I'm not really sure what part of your code is actually printing anything to stdout, I'm guessing it's this piece here
./tg/tgcli -s ./bot/bot.lua $#
You could try something like this, which will background your initial process and then run clear every 60 seconds to clear the terminal window. Is there any reason you're not writing the output to a log file? That alone could solve some of your issues as well.
#!/bin/bash
./tg/tgcli -s ./bot/bot.lua $# &
pid="$!"
check_pid() {
ps -ef |grep "$pid"|grep -v 'grep' &>/dev/null
}
cnt=1
until ! check_pid; do
if (( cnt == 6 )); then
clear
cnt=1
fi
sleep 10
((cnt++))
done

pidof -x $0 returns 2 instead of expected 1, is there a one-line alternative?

In a bash script, I'm trying to use the following line to determine if another process is executing the current script:
/sbin/pidof -x $0 |wc -w
But the response is always 2, instead of the expected value of 1.
I've tried the following calls:
/sbin/pidof -x $0 returns 1 pid
/sbin/pidof -x $0 |wc -w returns 2
/sbin/pidof -x $0 |head returns 2 pids
/sbin/pidof -x $0 |head |wc -w returns 2
/sbin/pidof -x $0 |head |tail returns 2 pids
/sbin/pidof -x $0 |head |tail |wc -w returns 2
Can anyone suggest an alternative (or fix) to achieve what I'm trying to do, and explain why piping the output to anything causes pidof output to go "a bit funny"?
This is how the script currently uses pidof, which works fine:
RUNNING=`/sbin/pidof -x $0`
RUNNING=`echo -n ${RUNNING} | wc -w`
[[ ${RUNNING} -gt 1 ]] && return
If you are only trying to prevent a second running script the following will do the trick:
pid=/tmp/$(basename $0).pid
[ -e $pid ] && exit 0;
trap 'rm -f $pid >/dev/null 2>&1;' 0
trap 'exit 2' 1 2 3 15
touch $pid
Just start your script with those lines. Basically it checks for a file and stop execution if the file is present. Note that if you kill (-9) the running script you are responsible for cleaning up the pid file that was created. Kill (-9) bypasses the shell trap that would otherwise clean up the pid file.

Bash - Update terminal title by running a second command

On my terminal in Ubuntu, I often run programs which keep running for a long time. And since there are a lot of these programs, I keep forgetting which terminal is for which program, unless I tab through all of those. So I wanted to find a way to update my terminal title to the program name, whenever I run a command. I don't want to do it manually.
I use gnome-terminal, but answer shouldn't really depend on that. Basically, If I'm able to run a second command, then I can simply use gconftool command to update the title. So I was hoping to find a way to capture the command in bash and update the title after every command. How do I do that?
I have some answers for you :) You're right that it shouldn't matter that you're using gnome-terminal, but it does matter what command shell you're using. This is a lot easier in zsh, but in what follows I'm going to assume you're using bash, and that it's a fairly recent version (> 3.1).
First of all:
Which environment variable would
contain the current 'command'?
There is an environment variable which has more-or-less what you want - $BASH_COMMAND. There's only one small hitch, which is that it will only show you the last command in a pipe. I'm not 100% sure what it will do with combinations of subshells, either :)
So I was hoping to find a way to
capture the command in bash and update
the title after every command.
I've been thinking about this, and now that I understand what you want to do, I realized the real problem is that you need to update the title before every command. This means that the $PROMPT_COMMAND and $PS1 environment variables are out as possible solutions, since they're only executed after the command returns.
In bash, the only way I can think of to achieve what you want is to (ab)use the DEBUG SIGNAL. So here's a solution -- stick this at the end of your .bashrc:
trap 'printf "\033]0;%s\007" "${BASH_COMMAND//[^[:print:]]/}"' DEBUG
To get around the problem with pipes, I've been messing around with this:
function settitle () {
export PREV_COMMAND=${PREV_COMMAND}${#}
printf "\033]0;%s\007" "${BASH_COMMAND//[^[:print:]]/}"
export PREV_COMMAND=${PREV_COMMAND}' | '
}
export PROMPT_COMMAND=${PROMPT_COMMAND}';export PREV_COMMAND=""'
trap 'settitle "$BASH_COMMAND"' DEBUG
but I don't promise it's perfect!
Try this:
trap 'echo -ne "\033]2;$(history 1 | sed "s/^[ ]*[0-9]*[ ]*//g")\007"' DEBUG
Thanks to the history 1 it works even with complicated expressions like:
true && (false); echo $? | cat
For which approaches relying on $BASH_COMMAND or $# fail. For example simon's displays:
true | echo $? | cat
Thanks to Gilles and simon for providing inspiration.
I see what stoutie is trying to do, except it's a lot more work than needed. And doesn't cause all sorts of other potentially bad things that can occur as a result of redefining 'cd' and putting in all of that testing just to change directories. Bash has built in support for most of this.
You can put this in your .bashrc anywhere after you set your current PS1 prompt (this way it just prepends it)
# If this is an xterm set the titlebar to user#host:dir
case "$TERM" in
xterm*|rxvt*)
PS1="\[\e]0;\u#\h: \w\a\]$PS1"
;;
*)
;;
esac
The OP asked for bash, but others might be interested to learn that (as mentioned above) this is indeed a lot easier using the zsh shell. Example:
# Set window title to command just before running it.
preexec() { printf "\x1b]0;%s\x07" "$1"; }
# Set window title to current working directory after returning from a command.
precmd() { printf "\x1b]0;%s\x07" "$PWD" }
In preexec, $1 contains the command as typed (requires shell history to be enabled, which seems to be a fair assumption), $2 the expanded command (shell aliases etc.) and $3 the "very expanded" command (shell function bodies). (more)
I'm doing something like this, to show my pwd in the title, which could be modified to do whatever you want to do with the title:
function title { echo -en "\033]2;$1\007"; }
function cd { dir=$1; if [ -z "$dir" ]; then dir=~; fi; builtin cd "$dir" && title `pwd`; }
I just threw this in my ~/.bash_aliases.
Update
I ran into strange bugs with my original answer. I ended up picking apart the default Ubuntu PS1 and breaking it into parts only to realize one of the parts was the title:
# simple prompt
COLOR_YELLOW_BOLD="\[\033[1;33m\]"
COLOR_DEFAULT="\[\033[0m\]"
TITLE="\[\e]0;\u#\h:\w\a\]"
PROMPT="\w\n$ "
HUH="${debian_chroot:+($debian_chroot)}"
PS1="${COLOR_YELLOW_BOLD}${TITLE}${HUH}${PROMPT}${COLOR_DEFAULT}"
Without breaking into variables, it would look like this:
PS1="\[\033[1;33m\]\[\e]0;\u#\h:\w\a\]${debian_chroot:+($debian_chroot)}\w\n$ \[\033[0m\]"
I have tested three method, all is OK, use any one for your pleasure.
export PROMPT_COMMAND='echo -ne "\033]2;$(history 1 | sed "s/^[ ]*[0-9]*[ ]*//g")\007"'
trap 'echo -ne "\033]2;$(history 1 | sed "s/^[ ]*[0-9]*[ ]*//g")\007"' DEBUG
trap 'echo -ne "\e]0;"; echo -n $BASH_COMMAND; echo -ne "\a"' DEBUG
please note if use $BASH_COMMAND, it don't recognize bash alias, and use PROMPT_COMMAND show finished command, but use trap show running command.
Based on the the need to auto position putty windows I have modified my /etc/bash.bashrc file on a Debian/Ubuntu system. I have posted the full contents for completeness but the relevant bit to starts on the # Display command ... comment line.
# System-wide .bashrc file for interactive bash(1) shells.
# To enable the settings / commands in this file for login shells as well,
# this file has to be sourced in /etc/profile.
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize
# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
debian_chroot=$(cat /etc/debian_chroot)
fi
# set a fancy prompt (non-color, overwrite the one in /etc/profile)
PS1='${debian_chroot:+($debian_chroot)}\u#\h:\w\$ '
# Display command run in title which allows us to distinguish Kitty/Putty
# windows and re-position easily using AutoSizer window utility. Based on a
# post here: http://mg.pov.lt/blog/bash-prompt.html
case "$TERM" in
xterm*|rxvt*)
# Show the currently running command in the terminal title:
# http://www.davidpashley.com/articles/xterm-titles-with-bash.html
show_command_in_title_bar()
{
case "$BASH_COMMAND" in
*\033]0*)
# The command is trying to set the title bar as well;
# this is most likely the execution of $PROMPT_COMMAND.
# In any case nested escapes confuse the terminal, so don't
# output them.
;;
*)
echo -ne "\033]0;${USER}#${HOSTNAME}: ${BASH_COMMAND}\007"
;;
esac
}
trap show_command_in_title_bar DEBUG
;;
*)
;;
esac
# Commented out, don't overwrite xterm -T "title" -n "icontitle" by default.
# If this is an xterm set the title to user#host:dir
#case "$TERM" in
#xterm*|rxvt*)
# PROMPT_COMMAND='echo -ne "\033]0;${USER}#${HOSTNAME}: ${PWD}\007"'
# ;;
#*)
# ;;
#esac
# enable bash completion in interactive shells
if ! shopt -oq posix; then
if [ -f /usr/share/bash-completion/bash_completion ]; then
. /usr/share/bash-completion/bash_completion
elif [ -f /etc/bash_completion ]; then
. /etc/bash_completion
fi
fi
# if the command-not-found package is installed, use it
if [ -x /usr/lib/command-not-found -o -x /usr/share/command-not-found/command-not-found ]; then
function command_not_found_handle {
# check because c-n-f could've been removed in the meantime
if [ -x /usr/lib/command-not-found ]; then
/usr/bin/python /usr/lib/command-not-found -- "$1"
return $?
elif [ -x /usr/share/command-not-found/command-not-found ]; then
/usr/bin/python /usr/share/command-not-found/command-not-found -- "$1"
return $?
else
printf "%s: command not found\n" "$1" >&2
return 127
fi
}
fi
You can set up bash such that it sends a certain escape sequence to the terminal every time it starts an external program. If you use the escape sequence that terminals use to update their titles, your problem should be solved.
I have used that before, so I know it is possible. but I cannot remember it off the top of my head and do not have time to research the details right now, though.
Some of the old methods were removed from gnome-terminal 3.14 due to these two bugs (724110 and 740188).
In Ubuntu 20.04
PS1=$PS1"\[\e]0;New_Terminal_Name\a\]"
\[ begin a sequence of non-printing characters
\e]0; is the char sequence for setting the terminal title. Bash identifies this sequence and set the tile with the following characters. Number 0 turns out to be the value to reference the title property.
New_Terminal_Name is the tile we gave
\a is the ASCII bell character, also in this case, it marks the end of the tile to read from Bash.
\] end a sequence of non-printing characters
We can create a function for future use
function set_title(){
if [ -z "$PS1_BACK" ]; # set backup if it is empty
then
PS1_BACK="$PS1"
fi
TITLE="\[\e]0;$*\a\]"
PS1="${PS1_BACK}${TITLE}"
}
Open the ~/.bashrc file in your home directory with a text editor and append the above function at the end of it. Save and close.
To use it immediately source it to the current terminal.
source ~/.bashrc
We can use it then like this
set_title <New terminal tab title>
My terminal window titler script
This dynamic backgrounded script show all running command with pid number and elapsed time in seconds, like if I run du -h | less, this will build title looking like:
204640 6 du -h | 204641 6 less
Then when no command (other than himself) are running, don't change the terminal title, so standard behaviours works normaly.
First run start backgroud task. Second run in same terminal ask for kill previous backgrounded task.
Save this into a file, set execute flag then run it without argument:
cat <<"EOF" >titleWin.sh
#!/bin/bash
## Ask for kill process if already started
mapfile -t pids < <(ps -C ${0##*/} ho pid)
for pid in ${pids[#]} ;do
if [[ $pid != $$ ]] && [ -d /proc/$pid ]; then
echo -n "STARTED: [$pid]: ${0##*/}. Kill them (Y/n)? "
read -rsn 1 act
case $act in
n|N ) echo No;;
* ) echo Yes;kill $pid ;;
esac
exit
fi
done
## Title win for xterm or screen (or tmux).
case $TERM in
xterm*|rxvt* ) titleFmt='\e];%s\a';;
screen* ) titleFmt='\ek%s\e\\';;
* ) echo "Unable to title window.";exit 1;;
esac
tty=$(tty)
## Date to epochseconds converter
exec {dateout}<> <(:)
exec {datein}> >(exec stdbuf -o0 date -f - +%s >&$dateout)
DPID=$!
trap "echo TRAP;kill $DPID" 1 2 3 6 9 15
# Main loop
while :;do
string=""
while read -r pid wday mon day time year cmd; do
if [[ $pid != $$ ]] && [[ $pid != $PPID ]] && [[ $pid != $BASHPID ]] &&
[[ $pid != $DPID ]] && [ "${cmd#*pid,lstart,cmd}" ] &&
[ -d /proc/$pid ] ;then
echo >&${datein} $wday $mon $day $time $year
read -ru $dateout date
string+="$pid $((EPOCHSECONDS-date)) $cmd | "
fi
done < <(exec ps --tty ${tty#*/dev/} ho pid,lstart,cmd)
[[ "$string" ]] && printf "$titleFmt" "${string% | }"
sleep .333
done &
EOF
chmod +x titleWin.sh
./titleWin.sh

Bash Script to allow Nagios to report ping between two other Linux machines

I'm looking for alternatives to working out the ping between two machine (mA and mB) and report this back to Nagios (on mC).
My current thoughts are to write a BASH script that will ping the machines in a cron job, output the data to a file then have another bash script that Nagios can use to read that file. This doesn't feel like the best/right way to do this though?
Here's the script I plan to run in the cron job:
#!/bin/bash
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ]
then
echo $0: usage: $0 file? ip? pingcount? deadline?
exit 126
else
FILE=$1
IP=$2
PCOUNT=$3
DLINE=$4
while read line
do
if [[ $line == rtt* ]]
then
#replace forward slash with underscore
line=${line////_}
#replace spaces with underscore
line=${line// /_}
#get the 8 item when splitting string on underscore
#echo $line| cut -d'_' -f 8 >> $FILE #Append
#echo $line| cut -d'_' -f 8 > $FILE #Overwrite
echo $line| cut -d'_' -f 8
fi
done < <(ping $IP -c $PCOUNT -q -w $DLINE) #-q output summary / -w deadline / -c pint count
I though about using trace route, but I think this would be produces a slower ping?, is there another way to achieve what I want?
Note: I know Nagios can directly ping a machine, but this isn't what I want to do and won't tell me what I want. Also this is my second script ever, so it's probably rubbish. Also, what alternative would I have if ICMP was blocked?
Have you looked at NRPE and check_ping? This would allow the nagios machine (mC) to ask mA to ping mB and then mA would report the results to mC. You would need to install and configure NRPE and the nagios-plugins on mA for this to work.

Resources