Making a graph (asteriks) with Printf on Linux - linux

I made a little program where I see the date, time and donwload speed every time in 10 sec. Before I made my program I installed speedtest-cli.
Now follows a code block:
!/bin/bash
while [ True ]
do
sleep 1
date=$(date "+%D +%T)
dlspeed=$(speedtest-cli --simple | egrep 'Download')
echo $date > bandwidth
echo $dlspeed >> bandwidth
content=$(cat bandwidth | sed ':a;N;$!ba;s/;\n/;/g')
echo $content >> output
done
When we run the program everything works nice. The first ouput is 14/12/2016 18:33:25 Download: 8.33 Mbits. Every 10 sec it shows me my download speed. I am using here a loop.
Now I need to use printf to make an graph based on the Download speed.So the output has to be 14/12/2016 18:33:25 Download: 8.33 Mbits ********.
My question. How to make this graph with asterisks and add them.

You can use a for loop in bash:
for i in {1..6} ; do
printf '*'
done
For a variable, you can use seq instead of curly brackets:
n=6
for i in $(seq $n) ; do
printf '*'
done

Related

Need a way to check content of file (time data) is more than 5 minutes or not

I am creating a script to pull out the list of highest cpu consuming jobs on my server. I am taking the dump of processes in a flat file in below format:
202009080230,4.1,218579,padaemon,02:30,00:00:01,SD_RCC_FRU:wf_rcc_ds_CRS_FactSuspendedGdc_d.s_m_rcc_ds_Staging_Dimension_FactSuspendedGdc_ipi_d
The second data point(4.1) is my cpu utilization while the sixth one is the time taken(00:00:01).
I intend to filter out only the entries from this file that are greater than 75% and run for more than 00:05:00 i.e. 5 minutes. I wrote an if logic for the same like below:
var10=`ls -lrt /projects/Informatica/INFA_ADMIN/LogFiles/myfile.txt |awk '{ print $9 }'`
sed -i 's/ /,/g' $var10
for i in `cat $var10`
do
var12=`echo $i |cut -d',' -f2`
var16=`echo $i |cut -d',' -f6`
if [ $var12 -ge 75.0 ] && ("$var16" > "00:05:00");
then
<logic>
fi
done
the script is able to identify processes taking more than 75.0 cpu but failing in the second condition.
[ 136 -ge 75.0 ]
00:20:26
1> 00:05:00 cpu_report_email_prateek_test.sh[63]: 00:20:26: not found [No such file or directory]
Can you please guide how this can be fixed?
First, I understand that on line 1 you're trying to have the latest file. I would rather have the file created always with the same name, then at the end of the script, after processing it, mv to another name (like myfile.txt.20200101). So you'll always know that myfile.txt is the latest one. More efficient than doing ls. Then again, you could improve your line with : (it's the number one, not the letter "l")
var10=$(ls -1rt /projects/Informatica/INFA_ADMIN/LogFiles/myfile.txt)
From your line 2, I assume the original file is space delimited. Actually, you might use that to your advantage instead of adding commas. Also, using a for loop with "cat" is less performant than using a while loop. For the time comparison, I would use a little trick. Transforming your threshold and reading, in second since epoch then compared them.
MyTreshold=$(date -d 00:05:00 +"%s")
while read -r line; do
MyArray=( $line )
MyReading=$(date -d ${MyArray[5]} +"%s")
#the array starts a index 0
if [[ ${MyArray[1]} -ge 75 && $MyReading -gt $MyThreshold ]]; then
<LOGIC>
fi
done <$var10
The RHS of your && expression would expand $var16 and attempt to execute the result, redirecting the output to a file 00:05:00. Replace with [ $x -gt $y ]. For a numeric comparison, you'll need straight numbers (no ':' chars). You could modify from hh:mm:ss to total seconds then compare.

Wordlist Generator in Bash

I am trying to create a wordlist consisting of the same password followed by a 4-digit numeric pin. The pin goes through every possible combination of 10,000 variations. The desired output should be like this:
UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ 1111
UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ 1112
UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ 1113
and so on.
I created a shell script that almost get this, but awk doesn't seem to like having a variable passed through it, and seems to just print out every combination when called. This is the shell script:
#!/bin/bash
# Creates 10,000 lines of the bandit24pass and every possible combination
# Of 4 digits pin
USER="UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ"
PASS=$( echo {1,2,3,4,5,6,7,8,9}{1,2,3,4,5,6,7,8,9}{1,2,3,4,5,6,7,8,9}{1,2,3,4,5,6,7,8,9} | awk '{print $I}' )
for I in {1..10000};
do
echo "$USER $PASS"
done
I though $I would translate to $1 for the first run of the loop, and increment upwards through each iteration. Any help would be greatly appreciated.
I though $I would translate to $1 for the first run of the loop, and increment upwards through each iteration.
No, command substitutions are expanded once; like, when you do foo=$(echo), foo is an empty line, not a reference to echo.
This whole task could be achieved by a single call to printf btw.
printf 'UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ %s\n' {1111..9999}
Tyr this
$echo $user
UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ
$for i in {1000..9999}; do echo $user $i; done;

How to monitor CPU usage automatically and return results when it reaches a threshold

I am new to shell script , i want to write a script to monitor CPU usage and if the CPU usage reaches a threshold it should print the CPU usage by top command ,here is my script , which is giving me error bad number and also not storing any value in the log files
while sleep 1;do if [ "$(top -n1 | grep -i ^cpu | awk '{print $2}')">>sy.log - ge "$Threshold" ]; then echo "$(top -n1)">>sys.log;fi;done
Your script HAS to be indented and stored to a file, especially if you are new to shell !
#!/bin/sh
while sleep 1
do
if [ "$(top -n1 | grep -i ^cpu | awk '{print $2}')">>sy.log - ge "$Threshold" ]
then
echo "$(top -n1)" >> sys.log
fi
done
Your condition looks a bit odd. It may work, but it looks really complex. Store intermediate results in variables, and evaluate them.
Then, you will immediately see the syntax error on the “-ge”.
You HAVE to store logfiles within an absolute path for security reasons. Use variables to simplify the reading.
#!/bin/sh
LOGFILE=/absolute_path/sy.log
WHOLEFILE=/absolute_path/sys.log
Thresold=80
while sleep 1
do
TOP="$(top -n1)"
CPU="$(echo $TOP | grep -i ^cpu | awk '{print $2}')"
echo $CPU >> $LOGFILE
if [ "$CPU" -ge "$Threshold" ] ; then
echo "$TOP" >> $WHOLEFILE
fi
done
You have a couple of errors.
If you write output to sy.log with a redirection then that output is no longer available to the shell. You can work around this with tee.
The dash before -ge must not be followed by a space.
Also, a few stylistic remarks:
grep x | awk '{y}' is a useless use of grep; this can usefully and more economically (as well as more elegantly) be rewritten as awk '/x/{y}'
echo "$(command)" is a useless use of echo -- not a deal-breaker, but you simply want command; there is no need to capture what it prints to standard output just so you can print that text to standard output.
If you are going to capture the output of top -n 1 anyway, there is no need really to run it twice.
Further notes:
If you know the capitalization of the field you want to extract, maybe you don't need to search case-insensitively. (I could not find a version of top which prints a CPU prefix with the load in the second field -- it the expression really correct?)
The shell only supports integer arithmetic. Is this a bug? Maybe you want to use Awk (which has floating-point support) to perform the comparison? This also allows for a moderately tricky refactoring. We make Awk output an exit code of 1 if the comparison fails, and use that as the condition for the if.
#!/bin/sh
while sleep 1
do
if top=$(top -n 1 |
awk -v thres="$Threshold" '1; # print every line
tolower($1) ~ /^cpu/ { print $2 >>"sy.log";
exitcode = ($2 >= thres ? 0 : 1) }
END { exit exitcode }')
then
echo "$top" >>sys.log
fi
done
Do you really mean to have two log files with nearly the same name, or is that a typo? Including a time stamp in the log might be useful both for troubleshooting and for actually using the log files.

Faster way to merge multiple files

I have multiple small files in Linux (about 70,000 files) and I want to add a word to the end of each line of the files and then merge them all into a single file.
I'm using this script:
for fn in *.sms.txt
do
sed 's/$/'$fn'/' $fn >> sms.txt
rm -f $fn
done
Is there a faster way to do this?
I tried with these files:
for ((i=1;i<70000;++i)); do printf -v fn 'file%.5d.sms.txt' $i; echo -e "HAHA\nLOL\nBye" > "$fn"; done
I tried your solution that took about 4 minutes (real) to process. The problem with your solution is that you're forking on sed 70000 times! And forking is rather slow.
#!/bin/bash
filename="sms.txt"
# Create file "$filename" or empty it if it already existed
> "$filename"
# Start editing with ed, the standard text editor
ed -s "$filename" < <(
# Go into insert mode:
echo i
# Loop through files
for fn in *.sms.txt; do
# Loop through lines of file "$fn"
while read l; do
# Insert line "$l" with "$fn" appended to
echo "$l$fn"
done < "$fn"
done
# Tell ed to quit insert mode (.), to save (w) and quit (q)
echo -e ".\nwq"
)
This solution took ca. 6 seconds.
Don't forget, ed is the standard text editor, and don't overlook it! If you enjoyed ed, you'll probably also enjoy ex!
Cheers!
Almost Same as gniourf_gniourf's solution, but without ed:
for i in *.sms.txt
do
while read line
do
echo $line $i
done < $i
done >sms.txt
What, no love for awk?
awk '{print $0" "FILENAME}' *.sms.txt >sms.txt
Using gawk, this took 1-2 seconds on gniourf_gniourf's sample on my machine (according to time).
mawk is about 0.2 seconds faster than gawk here.
This perl script adds the actual filename at the end of each line.
#!/usr/bin/perl
use strict;
while(<>){
chomp;
print $_, $ARGV, "\n";
}
Call it like this:
scriptname *.sms.txt > sms.txt
Since there is only one process and no regular expression processing involved it should be quite fast.

Bash script to copy numbered files in reverse order

I have a sequence of files:
image001.jpg
image002.jpg
image003.jpg
Can you help me with a bash script that copies the images in reverse order so that the final result is:
image001.jpg
image002.jpg
image003.jpg
image004.jpg <-- copy of image003.jpg
image005.jpg <-- copy of image002.jpg
image006.jpg <-- copy of image001.jpg
The text in parentheses is not part of the file name.
Why do I need it? I am creating video from a sequence of images and would like the video to play "forwards" and then "backwards" (looping the resulting video).
You can use printf to print a number with leading 0s.
$ printf '%03d\n' 1
001
$ printf '%03d\n' 2
002
$ printf '%03d\n' 3
003
Throwing that into a for loop yields:
MAX=6
for ((i=1; i<=MAX; i++)); do
cp $(printf 'image%03d.jpg' $i) $(printf 'image%03d.jpg' $((MAX-i+1)))
done
I think that I'd use an array for this... that way, you don't have to hard code a value for $MAX.
image=( image*.jpg )
MAX=${#image[*]}
for i in ${image[*]}
do
num=${i:5:3} # grab the digits
compliment=$(printf '%03d' $(echo $MAX-$num | bc))
ln $i copy_of_image$compliment.jpg
done
I used 'bc' for arithmetic because bash interprets leading 0s as an indicator that the number is octal, and the parameter expansion in bash isn't powerful enough to strip them without jumping through hoops. I could have done that in sed, but as long as I was calling something outside of bash, it made just as much sense to do the arithmetic directly.
I suppose that Kuegelman's script could have done something like this:
MAX=(ls image*.jpg | wc -l)
That script has bigger problems though, because it's overwriting half of the images:
cp image001.jpg image006.jpg # wait wait!!! what happened to image006.jpg???
Also, once you get above 007, you run into the octal problem.

Resources