Shell script output, how to scroll up? - shell

I have a shell script that runs on Debian. It contains lots of functions and conditions. There is a menu and the user may choose different options and different outputs will be displayed. The user can always go back to the main menu and choose again some options then another output will be displayed. Of course each time the screen is cleared with "clear".
However when then output contains too many lines, I will be able to scroll up a little bit, but it will stop and I won't be able to scroll all the way to the first line I need to see. Being able to scroll up with the mousse wheel is the king of behavior I would like...
It looks like the problem comes from the xterm window, because it is fine with the normal terminal. However xterm is nice because I can setup the height and the width, as well as changing the colors...
Is there a way to increase this limitation from the script itself as I won't have the permission to change anything in the Debian environment...
I read that some people actually pipe the entire script to "less", I tried that, the problem is that I can't use the menu anymore...
Please find below the first script that is used to run the main one:
xterm -fg ivory -bg darkblue -fn 8x13bold -geometry 76x110+1700+0 -T "QC CHECK" -e /tests/SCRIPTS/QC/qc.sh
Below is a little sample of my script, but, it contains much more:
#!/bin/sh
stty erase ^H
function water
{
clear
echo -e "Current Survey : ${proj}"
echo -e "Current Sequence : ${seq}"
echo -e ""
echo -e " [ Trace QC Water Column ]"
echo -e ""
if [ $mincable -eq $maxcable ]
then
echo -e " Cable checked : $maxcable"
else
echo -e " Cables checked : ${mincable}-${maxcable}"
fi
echo -e " Max noise level : ${maxnoise}uB"
if [ ${skiptrace} -eq 0 ]
echo -e " Traces skipped : ${skiptrace}"
else
echo -e " Traces skipped : 1-${skiptrace}"
fi
echo -e ""
#############
water=`awk --field-separator=";" '($4>'$maxnoise') {print int(a=(($1-1)/'$nb_traces')+1) " " ($1-((int(a)-1)*'$nb_traces')) " " $4}' ${seq}_TraceAverages.txt | grep -v "USER_AVRMS_WC1" | grep -v "R32" | awk '{printf $1 " " $2 " " ("%*.*f\n"), 1, 2, $3}' | awk '($2>'$skiptrace')&&($1>='$mincable')&&($1<='$maxcable') {print $1 " - " $2 " - " $3}' | awk '{printf("%16s%6s%8s%6s%10s\n"), $1, $2, $3, $4, $5}' | awk '(NR>1) && (old != $1) {printf("%65s\n"), "'$sep_cable'"} {print; old=$1}'`
#############
count_water=`awk --field-separator=";" '($4>'$maxnoise') {print int(a=(($1-1)/'$nb_traces')+1) " " (b=($1-((int(a)-1)*'$nb_traces'))) " " $3}' ${seq}_TraceAverages.txt | grep -v "USER_AVRMS_WC1" | grep -v "R32" | awk '($2>'$skiptrace')&&($1>='$mincable')&&($1<='$maxcable') {print $3}' | wc -l`
#############
echo -e " ------------------------------------------------------"
echo -e ""
echo -e " Cable - Trace - RMS_WC"
echo -e ""
echo -e " ------------------------------------------------------"
echo -e ""
if [ $count_water -ge 1 ]
then
echo -e "$water"
else
setterm -term linux -back red -fore white
echo -e " Wow! No traces? Maybe decrease your values..."
setterm -term linux -default
fi
echo -e ""
setterm -term linux -back blue -fore white
echo -e " RMS_WC > ${maxnoise}uB = $count_water"
setterm -term linux -default
echo -e ""
echo -e " ------------------------------------------------------"
echo -e ""
}
# check for latest project in /tests
proj_temp
# if config file is missing go to config menu
if [ ! -e /tests/$proj/SCRIPTS/QC/config ]
then
config
fi
# force choice=1 and config_ok=1 to return to main menu when loop has run once (no problem when more than one)
choice=1
config_ok=1
while :
do
# do it all
if [ ${choice} -eq 1 2>/dev/null ]
then
choice=X
config_ok=1
# read configuration file
readconfig
main
# config menu and help
if [ ${seq} = "c" ]
then
config
elif [ ${seq} = "h" ]
then
help
elif [ ${seq} = "q" ]
then
clear
setterm -term linux -back magenta -fore white
echo ""
echo -e "\t Try me next time :*"
sleep 0.65
exit
fi
# config_ok=1 when configuration is done, meaning user returns to main menu after exiting config menu
if [ ${config_ok} -eq 1 ]
then
cd $input_dir
# check if file for requested sequence is valid
testline
# this function updates the awk script for signal QC check
awkscript
doall
choice
fi
# let the user choose what QC is wanted
elif [ ${choice} -eq 2 2>/dev/null ]
then
choice=X
# initialize values so that user can choose its own
init
menu
option
fi
case ${menu} in
1) deep
choice ;;
2) water
choice ;;
3) awkscript
signal
choice ;;
4) readconfig
awkscript
doall
choice ;;
esac
if [ ${choice} = "q" 2>/dev/null ]
then
exit
fi
done
As you can see I have many functions and many variables that are called is some "echo" which makes it hard for me to scroll up when there is too many lines, and, the user got also to scroll up and down to see everything and to choose and action.

Pipe the output thru less or more. There are options (hot keys) to go forth, back, search etc.

I could not find a way to scroll through the length of my output so what I did is a loop that is gradually increasing by increments of 0.1 the value of ${maxnoise} (with a condition on the number of line output) because this variable is actually the one conditioning how big is the output. It works fine this way so I consider my question answered.

Related

update value in bash script

i have stuck code. i make bash script to send Notification If file Size changed
#!/bin/bash
#File Embeded
test_file=/home/optimus/bot_test/dump.txt
msg_caption=/tmp/telegram_msg_caption.txt
#initialize
initCount=0
#checkLog
#cmd="ls /var/log/snort/* | wc -l"
cmd=$(wc -c "dump.txt" | awk '{print $1}')
#Chat ID and TOKEN Telegram
chat_id="xxxx"
token="xxx"
#Send Alert
function sendAlert
{
curl -s -F chat_id=$chat_id -F caption="$caption" -F document=#$test_file https://api.telegram.org/bot$token/sendDocument > /dev/null 2&>1
}
#Monitoring DoS Server
while true
do
#GetLastCount
echo "Start Execute"
lastCount=$cmd
echo before_last $lastCount #ex 100 #after reset 0
echo before_init $initCount #ex 0
echo "--------------------"
if(($(($lastCount)) > $initCount));
then
echo "Running Script..." #debug
echo -e "Halo Admin\nTerjadi Indikasi Penyerangan DoS!!!" > $msg_caption #set Caption / Pesan
caption=$(<$msg_caption) #set Caption
sendAlert #Panggil Fungsi
#error disini
initCount=$lastCount
lastCount=0 #reset
echo after_last $lastCount
echo after_Init $initCount
echo "==================="
rm -f $msg_caption
sleep 5 #delay Proses jika ada indikasi
fi
sleep 5 #delay proses jika tidak ada indikasi
done
when code first running, send First Notification, and if file size changed , value in code not updated
example i use dump.txt to object size changed
how to get Last Size from dump.txt after
echo "Start Execute"
conclusion every 5 second, lastCount get last size File updated
The problem in your code is that the cmd=$(wc -c "dump.txt" | awk '{print $1}') line executes only once.
Instead, you should use:
#....
do
#...
lastCount=$(wc -c "dump.txt" | awk '{print $1}')
#...
Or make the cmd a function or store in a string and evaluate the string. The way you wrote, it is only evaluated in the beginning of your script and you use the same value afterward.

Shell script - Log file not updating

I'm trying to get my shell script to alert me if the 2 log files is not updating. I am trying to get the current size... wait 10 seconds and if the file did not change size send an email alert. any help would be great!
My Code:
#! /bin/bash
logFiles="lg.log lg2.log"
logLocation="/user/file/logs"
sleepTime=10
subject="issue - file not updated"
failMessage="::no recent updates "
successMessage="OK"
not_updated_time=0
arr=($logFiles)
arrlen=${#arr[#]}
arrcount=()
date
for ((count=0; count<arrlen; count++)) ; do
arrcount[$count]=`ls -l $logLocation${arr[$count]} |awk '{print $5}'`
echo "${arr[$count]} Original size :: ${arrcount[$count]}"
done
echo
while [ "e" == "e" ] ; do
sleep $sleepTime
date
for ((count=0; count<arrlen; count++)) ; do
nc=`ls -l $logLocation${arr[$count]} |awk '{print $5}'`
echo -n "${arr[$count]} "
if [ $nc == ${arrcount[$count]} ] ; then
echo 'error' | mailx -s "issue" cat#gmail.com
else
arrcount[$count]=$nc
echo $successMessage
not_updated_time=0
fi
done
echo
done
Fixed the issue with comma, script still not working. any ideas?
There appears to be a / missing either on the initialization of logLocation or when you're using that variable as $logLocation${arr[$count]}.
Try one of these two changes:
logLocation="/user/file/logs/"
Or:
`ls -l $logLocation/${arr[$count]} |awk '{print $5}'`

bash script overwrite current line completely

I have been playing with a script to test the speed of various VoIP servers by pinging, I then found a progress bar script and incorporated that... because cool!
now I'm trying to display the current server being tested below the status bar, I have the line overwriting but if the next server name is shorter it does not overwrite completely. I have tried various suggestions I've found but non work or they screw up my progress bar.
Im running osx but may also use this on various linux distros.
any suggestions would be great!
#!/bin/bash
HOSTS=("atlanta.voip.ms" "atlanta2.voip.ms" "chicago.voip.ms" "chicago2.voip.ms" "chicago3.voip.ms" "chicago4.voip.ms" "dallas.voip.ms" "denver.voip.ms" "denver2.voip.ms" "houston.voip.ms" "houstonnew1.voip.ms" "houstonnew2.voip.ms" "losangeles.voip.ms" "losangeles2.voip.ms" "newyork.voip.ms" "newyork2.voip.ms" "newyork3.voip.ms" "newyork4.voip.ms" "sanjose.voip.ms" "sanjose2.voip.ms" "seattle.voip.ms" "seattle2.voip.ms" "seattle3.voip.ms" "tampa.voip.ms" "tampanew1.voip.ms" "tampanew2.voip.ms" "washington.voip.ms" "washington2.voip.ms" "montreal.voip.ms" "montreal2.voip.ms" "montreal3.voip.ms" "montreal4.voip.ms" "toronto.voip.ms" "toronto2.voip.ms" "toronto3.voip.ms" "toronto4.voip.ms" "vancouver.voip.ms" "vancouver2.voip.ms" "amsterdam.voip.ms" "london.voip.ms" "melbourne.voip.ms" "paris.voip.ms")
Smallest="200000"
Server=""
tLen=${#HOSTS[#]}
# Slick Progress Bar
# Created by: Ian Brown (ijbrown#hotmail.com)
# Please share with me your modifications
# Functions
PUT(){ echo -en "\033[${1};${2}H";}
DRAW(){ echo -en "\033%";echo -en "\033(0";}
WRITE(){ echo -en "\033(B";}
HIDECURSOR(){ echo -en "\033[?25l";}
NORM(){ echo -en "\033[?12l\033[?25h";}
function showBar {
percDone=$(echo 'scale=2;'$1/$2*100 | bc)
halfDone=$(echo $percDone/2 | bc)
barLen=$(echo ${percDone%'.00'})
halfDone=`expr $halfDone + 6`
tput bold
PUT 7 28; printf "%4.4s " $barLen%
PUT 5 $halfDone; echo -e "\033[7m \033[0m"
tput sgr0
}
# Start Script
clear
HIDECURSOR
echo -e ""
echo -e ""
DRAW
echo -e " PLEASE WAIT WHILE SCRIPT IS IN PROGRESS"
echo -e " lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk"
echo -e " x x"
echo -e " mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj"
echo -e ""
echo -e ""
WRITE
for (( i=0; i<${tLen}; i++ ))
do
showBar $i ${tLen}
serl=${HOSTS[$i]}
seru=$(echo "$serl" | tr '[:lower:]' '[:upper:]')
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
#this line needs to overwrite completley
echo -ne "" '\r " TESTING:" $seru
Current1=` ping -c 4 -q -i .2 ${HOSTS[$i]} | grep avg | awk -F'/' '{print $5 }'`
Current=${Current1/./}
if [ "$Current" -lt "$Smallest" ]
then
Server=${HOSTS[$i]}
Smallest=$Current
fi
done
clear
Smallestd=$(echo "$Smallest" | sed 's/...$/.&/')
echo "Fastest Server = $Server # $Smallestd ms"
Here is an example of controlling clearing to end-of-line both during display of the meter, and in restoring the cursor after completion. I have reversed the loop to show the meter progressing from 100% (full) to 1% cleaning up after itself as it goes:
#!/bin/bash
## string of characters for meter (60 - good for 120 char width)
str='▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒'
tput civis # make cursor invisible
for i in `seq 100 -1 1`; do # for 1 to 100, save cursor, restore, output, restore
printf "\033[s\033[u Progress: %s %3d %% \033[u" "${str:0:$(((i+1)/2))}" "$i"
sleep 0.1 # small delay
done
sleep 0.5
printf "\033[K" # clear to end-of-line
tput cnorm # restore cursor to normal
exit 0
Note: cursor control and clearing during display of the meter is provided by the ANSI escapes for save cursor position and restore cursor position. After completion, clear to end of line is used to clean up. tput is used to control cursor visibility, but can also be used to save, restore and clear to end of line.
You can pad your with spaces so you always write the same number of characters. That way you'll overwrite the extra characters from before with spaces. For example you could something like
echo -en "\r"; echo -n $(printf " TESTING: %-40s" $seru)
Thanks to David C. Rankin I have an easy working answer
echo -ne "" '\r' " TESTING:" $seru '\033[K'

Bash Script - Will not completely execute

I am writing a script that will take in 3 outputs and then search all files within a predefined path. However, my grep command seems to be breaking the script with error code 123. I have been staring at it for a while and cannot really seem the error so I was hoping someone could point out my error. Here is the code:
#! /bin/bash -e
#Check if path exists
if [ -z $ARCHIVE ]; then
echo "ARCHIVE NOT SET, PLEASE SET TO PROCEED."
echo "EXITING...."
exit 1
elif [ $# -ne 3 ]; then
echo "Illegal number of arguments"
echo "Please enter the date in yyyy mm dd"
echo "EXITING..."
exit 1
fi
filename=output.txt
#Simple signal handler
signal_handler()
{
echo ""
echo "Process killed or interrupted"
echo "Cleaning up files..."
rm -f out
echo "Finsihed"
exit 1
}
trap 'signal_handler' KILL
trap 'signal_handler' TERM
trap 'signal_handler' INT
echo "line 32"
echo $1 $2 $3
#Search for the TimeStamp field and replace the / and : characters
find $ARCHIVE | xargs grep -l "TimeStamp: $2/$3/$1"
echo "line 35"
fileSize=`wc -c out.txt | cut -f 1 -d ' '`
echo $fileSize
if [ $fileSize -ge 1 ]; then
echo "no"
xargs -n1 basename < $filename
else
echo "NO FILES EXIST"
fi
I added the echo's to know where it was breaking. My program prints out line 32 and the args but never line 35. When I check the exit code I get 123.
Thanks!
Notes:
ARCHIVE is set to a test directory, i.e. /home/'uname'/testDir
$1 $2 $3 == yyyy mm dd (ie a date)
In testDir there are N number of directories. Inside these directories there are data files that have contain data as well as a time tag. The time tag is of the following format: TimeStamp: 02/02/2004 at 20:38:01
The scripts goal is to find all files that have the date tag you are searching for.
Here's a simpler test case that demonstrates your problem:
#!/bin/bash -e
echo "This prints"
true | xargs false
echo "This does not"
The snippet exits with code 123.
The problem is that xargs exits with code 123 if any command fails. When xargs exits with non-zero status, -e causes the script to exit.
The quickest fix is to use || true to effectively ignore xargs' status:
#!/bin/bash -e
echo "This prints"
true | xargs false || true
echo "This now prints too"
The better fix is to not rely on -e, since this option is misleading and unpredictable.
xargs makes the error code 123 when grep returns a nonzero code even just once. Since you're using -e (#!/bin/bash -e), bash would exit the script when one of its commands return a nonzero exit code. Not using -e would allow your code to continue. Just disabling it on that part can be a solution too:
set +e ## Disable
find "$ARCHIVE" | xargs grep -l "TimeStamp: $2/$1/$3" ## If one of the files doesn't match the pattern, `grep` would return a nonzero code.
set -e ## Enable again.
Consider placing your variables around quotes to prevent word splitting as well like "$ARCHIVE".
-d '\n' may also be required if one of your files' filename contain spaces.
find "$ARCHIVE" | xargs -d '\n' grep -l "TimeStamp: $2/$1/$3"

Conditional variables in bash script?

I'm not used to writing code in bash but I'm self teaching myself. I'm trying to create a script that will query info from the process list. I've done that but I want to take it further and make it so:
The script runs with one set of commands if A OS is present.
The script runs with a different set of commands if B OS is present.
Here's what I have so far. It works on my Centos distro but won't work on my Ubuntu. Any help is greatly appreciated.
#!/bin/bash
pid=$(ps -eo pmem,pid | sort -nr -k 1 | cut -d " " -f 2 | head -1)
howmany=$(lsof -l -n -p $pid | wc -l)
nameofprocess=$(ps -eo pmem,fname | sort -nr -k 1 | cut -d " " -f 2 | head -1)
percent=$(ps -eo pmem,pid,fname | sort -k 1 -nr | head -1 | cut -d " " -f 1)
lsof -l -n -p $pid > ~/`date "+%Y-%m-%d-%H%M"`.process.log 2>&1
echo " "
echo "$nameofprocess has $howmany files open, and is using $percent"%" of memory."
echo "-----------------------------------"
echo "A log has been created in your home directory"
echo "-----------------------------------"
echo " "
echo ""$USER", do you want to terminate? (y/n)"
read yn
case $yn in
[yY] | [yY][Ee][Ss] )
kill -15 $pid
;;
[nN] | [n|N][O|o] )
echo "Not killing. Powering down."
echo "......."
sleep 2
;;
*) echo "Does not compute"
;;
esac
Here's my version of your script. It works with Ubuntu and Debian. It's probably safer than yours in some regards (I clearly had a bug in yours when a process takes more than 10% of memory, due to your awkward cut). Moreover, your ps are not "atomic", so things can change between different calls of ps.
#!/bin/bash
read percent pid nameofprocess < <(ps -eo pmem,pid,fname --sort=-pmem h)
mapfile -t openfiles < <(lsof -l -n -p $pid)
howmany=${#openfiles[#]}
printf '%s\n' "${openfiles[#]}" > ~/$(date "+%Y-%m-%d-%H%M.process.log")
cat <<EOF
$nameofprocess has $howmany files open, and is using $percent% of memory.
-----------------------------------
A log has been created in your home directory
-----------------------------------
EOF
read -p "$USER, do you want to terminate? (y/n) "
case $REPLY in
[yY] | [yY][Ee][Ss] )
kill -15 $pid
;;
[nN] | [n|N][O|o] )
echo "Not killing. Powering down."
echo "......."
sleep 2
;;
*) echo "Does not compute"
;;
esac
First, check that your version of ps has the --sort flag and the h option:
--sort=-pmem tells ps to sort wrt decreasing pmem
h tells ps to not show any header
All this is given to the read bash builtin, which reads space-separated fields, here the fields pmem, pid, fname and puts these values in the corresponding variables percent, pid and nameofprocess.
The mapfile command reads standard input (here the output of the lsof command) and puts each line in an array field. The size of this array is computed by the line howmany=${#openfiles[#]}. The output of lsof, as stored in the array openfiles is output to the corresponing file.
Then, instead of the many echos, we use a cat <<EOF, and then the read is use with the -p (prompt) option.
I don't know if this really answers your question, but at least, you have a well-written bash script, with less multiple useless command calls (until your case statement, you called 16 processes, I only called 4). Moreover, after the first ps call, things can change in your script (even though it's very unlikely to happen), not in mine.
You might also like the following which doesn't put the output of lsof in an array, but uses an extra wc command:
#!/bin/bash
read percent pid nameofprocess < <(ps -eo pmem,pid,fname --sort=-pmem h)
logfilename="~/$(date "+%Y-%m-%d-%H%M.process.log")
lsof -l -n -p $pid > "$logfilename"
howmany=$(wc -l < "$logfilename")
cat <<EOF
$nameofprocess has $howmany files open, and is using $percent% of memory.
-----------------------------------
A log has been created in your home directory ($logfilename)
-----------------------------------
EOF
read -p "$USER, do you want to terminate? (y/n) "
case $REPLY in
[yY] | [yY][Ee][Ss] )
kill -15 $pid
;;
[nN] | [n|N][O|o] )
echo "Not killing. Powering down."
echo "......."
sleep 2
;;
*) echo "Does not compute"
;;
esac
You could achieve this for example by (update)
#!/bin/bash
# place distribution independent code here
# dist=$(lsb_release -is)
if [[ -f /etc/redheat-release ]];
then # this is a RedHead based distribution like centos, fedora, ...
dist="redhead"
elif [[ -f /etc/issue.net ]];
then
# dist=$(cat /etc/issue.net | cut -d' ' -f1) # debian, ubuntu, ...
dist="ubuntu"
else
dist="unknown"
fi
if [[ $dist == "ubuntu" ]];
then
# use your ubuntu command set
elif [[ $dist == "redhead" ]];
then
# use your centos command set
else
# do some magic here
fi
# place distribution independent code here

Resources