How to write a Bash Script for Pushgateway and prometheus - bash

I am trying to use a bash script for Pushgateway and Prometheus, but can't get it working.
I have Pushgateway on my raspberry and Prometheus on another raspberry. All work correctly and I test a simple bash script, this script works correctly.
My simple script (test.sh):
echo "metric 99" | curl --data-binary #- http://localhost:9091/metrics/job/some_job
Now I want to write a more complex script. This script must push to prometheus the metric from CPU usage (the same as the "$ps aux" command or same as "$ top" command). But this script doesn't work and I don't know what to do to modify it..
My more complex script :
#!/bin/bash
z="ps aux"
while read -r $z
do
var=$var$(awk '{print "cpu_usage{process=\""$11"\", pid=\""$2"\"}", $3$z}');
done <<< "$z"
curl -X POST -H "Content-type: text/plain" --data "$var" http://localhost:9091/metrics/job/top/instance/machine
If anyone could help me. Thanks a lot.
I also try this code :
#!/bin/bash
z="ps aux"
while read -r "ps aux"
do
var=$var$(awk '{print "cpu_usage{process=\""$11"\", pid=\""$2"\"}", $3$z}');
done <<< "$z"
curl -X POST -H "Content-type: text/plain" --data "$var" http://localhost:9091/metrics/job/top/instance/machine
But I am not sure about the syntax. What's wrong ?
I try the code :
load=$(ps aux | awk '{ print "cpu_usage{ process=\"" $11 "\",pid=\"" $2 "\"}," $3 }')
curl -X POST -H --data "$load" http://localhost:9091/metrics/job/top/instance/machine
But it doesn't work. The first line is ok, but when i run this code, I find an error message to the curl command:
curl: (3) URL using bad/illegal format or missing URL
========== The solution to my problem is : ==========
ps aux | awk '$3>0 {print "cpu_usage"$2" "$3""}' | curl --data-binary #- http://localhost:9091/metrics/job/top/instance/machine
This command could transfer to the pushgateway all process data with % CPU > 0. In this line, $3 = %CPU, $2 = PID. Be carefull with special caracters. If the result command is an error message, maybe it is because there is special caracter...

If your problem is too complex, divide it into smaller, more manageable pieces and see what they do. Start by analysing output from the awk part.
AWK can be a bit of a handful.
Try a simpler approach:
ps aux | tr -s ' ' ',' | cut -d, -f2,11 |
while read pid process; do
req="cpu_usage{process=$process,pid=$pid}"
echo "Sending CURL Request with this data: $req"
curl -X POST -H "Content-type: text/plain" --data "$req" http://localhost:9091/metrics/job/top/instance/machine
done
You may want to review the brackets. I have no means of testing this.

You seem to be confused about several details of basic Bash syntax.
command <<<"string"
simply passes the literal string as standard input to command. The syntax you seem to be looking for is the process substitution
command < <(other)
which runs other and passes its output as input to command. But this too is an overcomplication; you probably want a simpler straight pipeline.
load=$(ps aux | awk '{ print "cpu_usage{ process=\"" $11 "\",pid=\"" $2 "\"}," $3 }')
curl -X POST -H "Content-type: text/plain" --data "$load" http://localhost:9091/metrics/job/top/instance/machine
where I had to resort to some wild speculation around what you hoped the Awk script should do.
Also, the argument to read is the name of a variable, so read z, not read $z (and usually use read -r unless you specifically require the odd legacy behavior of read with backslashes in its input).
Finally, you basically never want to store a command in a variable; see https://mywiki.wooledge.org/BashFAQ/050
Going forward, probably try http://shellcheck.net/ before asking for human assistance.

/!\ The solution to my problem is : /!\
ps aux | awk '$3>0 {print "cpu_usage"$2" "$3""}' | curl --data-binary #- http://localhost:9091/metrics/job/top/instance/machine
This command could transfer to the pushgateway all process data with % CPU > 0. In this line, $3 = %CPU, $2 = PID. Be carefull with special caracters. If the result command is an error message, maybe it is because there is special caracter...
Thanks...

Related

How to extract Active domain

Is there any bash command/script in Linux so we can extract the active domains from a long list,
example, I have a csv file (domains.csv) there are 55 million domains are listed horizontally, we need only active domains in a csv file (active.csv)
Here active mean a domain who has a web page at least, not a domain who is expired or not expired. example whoisdatacenter.info is not expired but it has no webpage, we consider it as non-active.
I check google and stack website. I saw we can get domain by 2 ways. like
$ curl -Is google.com | grep -i location
Location: http://www.google.com/
or
nslookup google.com | grep -i name
Name: google.com
but I got no idea how can I write a program in bash for this for 55 million domains.
below commands, won't give any result so I come up that nsloop and curl is wayway to get result
$ nslookup whoisdatacenter.info | grep -i name
$ curl -Is whoisdatacenter.info | grep -i location
1st 25 lines
$ head -25 domains.csv
"
"0----0.info"
"0--0---------2lookup.com"
"0--0-------free2lookup.com"
"0--0-----2lookup.com"
"0--0----free2lookup.com"
"0--1.xyz"
"0--123456789.com"
"0--123456789.net"
"0--6.com"
"0--7.com"
"0--9.info"
"0--9.net"
"0--9.world"
"0--a.com"
"0--a.net"
"0--b.com"
"0--m.com"
"0--mm.com"
"0--reversephonelookup.com"
"0--z.com"
"0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0.com"
"0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0.com"
"0-0-0-0-0-0-0-0-0-0-0-0-0-10-0-0-0-0-0-0-0-0-0-0-0-0-0.info"
code I am running
while read line;
do nslookup "$line" | awk '/Name/';
done < domains.csv > active3.csv
the result I am getting
sh -x ravi2.sh
+ read line
+ nslookup ''
+ awk /Name/
nslookup: '' is not a legal name (unexpected end of input)
+ read line
+ nslookup '"'
+ awk /Name/
+ read line
+ nslookup '"0----0.info"'
+ awk /Name/
+ read line
+ nslookup '"0--0---------2lookup.com"'
+ awk /Name/
+ read line
+ nslookup '"0--0-------free2lookup.com"'
+ awk /Name/
+ read line
+ nslookup '"0--0-----2lookup.com"'
+ awk /Name/
+ read line
+ nslookup '"0--0----free2lookup.com"'
+ awk /Name/
still, active3.csv is empty
below . the script is working, but something stopping the bulk lookup, either it's in my host or something else.
while read line
do
nslookup $(echo "$line" | awk '{gsub(/\r/,"");gsub(/.*-|"$/,"")} 1') | awk '/Name/{print}'
done < input.csv >> output.csv
The bulk nslookup show such error in below
server can't find facebook.com\013: NXDOMAIN
[Solved]
Ravi script is working perfectly fine, I was running in my MAC which gave Nslookup Error, I work in CentOS Linux server, Nslookup work great with Ravi script
Thanks a lot!!
EDIT: Please try my EDIT solution as per OP's shown samples.
while read line
do
nslookup $(echo "$line" | awk '{gsub(/\r/,"");gsub(/.*-|"$/,"")} 1') | awk '/Name/{found=1;next} found && /Address/{print $NF}'
done < "Input_file"
Could you please try following.
OP has control M characters in her Input_file so run following command too remove them first:
tr -d '\r' < Input_file > temp && mv temp Input_file
Then run following code:
while read line
do
nslookup "$line" | awk '/Name/{found=1;next} found && /Address/{print $NF}'
done < "Input_file"
I am assuming that since you are passing domain name you need to get their address(IP address) in output. Also since you are using a huge Input_file so it may be a bit slow in providing output, but trust me this is a simpler way.
nslookup simply indicates whether or not the domain name has a record in DNS. Having one or more IP addresses does not automatically mean you have a web site; many IP addresses are allocated for different purposes altogether (but might coincidentally host a web site for another domain name entirely!)
(Also, nslookup is not particularly friendly to scripting; you will want to look at dig instead for automation.)
There is no simple way to visit 55 million possible web sites in a short time, and probably you should not be using Bash if you want to. See e.g. https://pawelmhm.github.io/asyncio/python/aiohttp/2016/04/22/asyncio-aiohttp.html for an exposition of various approaches based on Python.
The immediate error message indicates that you have DOS carriage returns in your input file; this is a common FAQ which is covered very well over at Are shell scripts sensitive to encoding and line endings?
You can run multiple curl instances in parallel but you will probably eventually saturate your network -- experiment with various degrees of parallelism -- maybe split up your file into smaller pieces and run each piece on a separate host with a separate network connection (perhaps in the cloud) but to quickly demonstrate,
tr -d '\r' <file |
xargs -P 256 -i sh -c 'curl -Is {} | grep Location'
to run 256 instances of curl in parallel. You will still need to figure out which output corresponds to which input, so maybe refactor to something like
tr -d '\r' <file |
xargs -P 256 -i sh -c 'curl -Is {} | sed -n "s/Location/{}:&/p"'
to print the input domain name in front of each output.
(Maybe also note that just a domain name is not a complete URL. curl will helpfully attempt to add a "http://" in front and then connect to that, but that still doesn't give you an accurate result if the domain only has a "https://" website and no redirect from the http:// one.)
If you are on a Mac, where xargs doesn't understand -i, try -I {} or something like
tr -d '\r' <file |
xargs -P 256 sh -c 'for url; do curl -Is "$url" | sed -n "s/Location/{}:&/p"; done' _
The examples assume you didn't already fix the DOS carriage returns once and for all; you probably really should (and consider dropping Windows from the equation entirely).

Getting a variable from a website which is then stored

I need to retrieve the "message" variable from the website and store it in a variable.
Im new to shell script so im not sure how to do it. Ive been trying various ways for a while but they dont seem to work.
This is the output of the website example.web:8080/rest/message
[{"id":33,"message":"Dash","lastUpdated":1569922857154,"userName":null}]
#!/bin/bash
message=$( curl -# -L "http://example.web:8080/rest/message}")
username=$(
<<< "${message}" \
grep -P -o -e '(?<=<li>message: <strong>)(.*?)(?=<\/strong><\/li>)' |
head -n 1
)
I need the message variable "Dash" To be stored which can be printer later on.
Though I am not sure if output you are getting is pure json or not, if you are ok with awk could you please try following(If you have proper json then please use jq or parser which is specially made for json parsing).
Your_command | awk -v s1="\"" '
match($0,s1 "message" s1 ":" s1 "[^\"]*"){
num=split(substr($0,RSTART,RLENGTH),array,"\"")
print array[num]
}'
EDIT: If you have jq tool with you, could you please try following(not tested).
curl -# -L "http://example.web:8080/rest/message" | jq '.[] | .message'

AWK print field by variable value

so I have a GET command retrieving data from a server, and only need specific parts of this info.
I have a working script but the awk part is very long and I was wondering if I could get some help shortening it.
Current script:
curl --insecure -u $HMCUSER:$HMCPASS -H "Accept: */*" -H "Content-type: application/json" -s --header "X-API-Session:$COOKIE" -X GET https://$HMCIP:6794$BCID1/blades | awk -F\" '{print $50" M1:"$42"\n"$114" M1:"$106"\n"$18" M1:"$10"\n"$98" M1:"$90"\n"$34" M1:"$26"\n"$82" M1:"$74"\n"$66" M1:"$58"\n"$130" M1:"$122}' > ~walkers/blade-info-new
echo -e "\n`cat blade-info-new`\n"
and the output is:
/api/blades/394a7ea8-02d4-11e1-b71a-5cf3fcad1a40 M1:B.1.01
/api/blades/749f35cc-02d7-11e1-946a-5cf3fcad1ef8 M1:B.1.02
/api/blades/eeae9670-02d5-11e1-a5ee-5cf3fcad21e0 M1:B.1.03
/api/blades/3949f5a0-02d4-11e1-85df-5cf3fcad1dc8 M1:B.1.04
/api/blades/d25df328-02d3-11e1-a1e9-5cf3fcad2158 M1:B.1.05
/api/blades/bbecebd8-02d0-11e1-aca7-5cf3fcacf4a0 M1:B.1.06
/api/blades/3016b5d8-02d7-11e1-a66f-5cf3fcad1dd0 M1:B.1.07
/api/blades/75796586-02ea-11e1-8ab0-5cf3fcacf040 M1:B.1.08
(there are two columns: /api/blades/... and M1:B.1.0#)
So I tried this:
for i in {10..130..8}
do
try=$(curl --insecure -u $HMCUSER:$HMCPASS -H "Accept: */*" -H "Content-type: application/json" -s --header "X-API-Session:$COOKIE" -X GET https://$HMCIP:6794$BCID1/blades | awk -v i=$i -F\" '{print $i}')
echo "$try"
done
hoping to get the same output as above and instead I just get the complete JSON object:
{"blades":[{"status":"operating","name":"B.1.03","type":"system-x","object-uri":"/api/blades/eeae9670-02d5-11e1-a5ee-5cf3fcad21e0"},{"status":"operating","name":"B.1.05","type":"system-x","object-uri":"/api/blades/d25df328-02d3-11e1-a1e9-5cf3fcad2158"},{"status":"operating","name":"B.1.01","type":"system-x","object-uri":"/api/blades/394a7ea8-02d4-11e1-b71a-5cf3fcad1a40"},{"status":"operating","name":"B.1.07","type":"system-x","object-uri":"/api/blades/3016b5d8-02d7-11e1-a66f-5cf3fcad1dd0"},{"status":"operating","name":"B.1.06","type":"system-x","object-uri":"/api/blades/bbecebd8-02d0-11e1-aca7-5cf3fcacf4a0"},{"status":"operating","name":"B.1.04","type":"system-x","object-uri":"/api/blades/3949f5a0-02d4-11e1-85df-5cf3fcad1dc8"},{"status":"operating","name":"B.1.02","type":"system-x","object-uri":"/api/blades/749f35cc-02d7-11e1-946a-5cf3fcad1ef8"},{"status":"operating","name":"B.1.08","type":"system-x","object-uri":"/api/blades/75796586-02ea-11e1-8ab0-5cf3fcacf040"}]}
So I was wondering how to get the variable to work? I've been on many websites and everyone seems to say awk -v i=$i should work...
EDIT: The sequence I want to print is the object uri (i.e. /api/blades/...) followed by the blade name (i.e. B.1.01). These infos are all in the JSON object returned by the curl command starting with the tenth field and every 8th field after that (using " as a delimiter):
{"blades":[{"status":"operating","name":"B.1.03","type":"system-x","object-uri":"/api/blades/eeae9670-02d5-11e1-a5ee-5cf3fcad21e0"},{"status":"operating","name":"B.1.05","type":"system-x","object-uri":"/api/blades/d25df328-02d3-11e1-a1e9-5cf3fcad2158"},{"status":"operating","name":"B.1.01","type":"system-x","object-uri":"/api/blades/394a7ea8-02d4-11e1-b71a-5cf3fcad1a40"},{"status":"operating","name":"B.1.07","type":"system-x","object-uri":"/api/blades/3016b5d8-02d7-11e1-a66f-5cf3fcad1dd0"},{"status":"operating","name":"B.1.06","type":"system-x","object-uri":"/api/blades/bbecebd8-02d0-11e1-aca7-5cf3fcacf4a0"},{"status":"operating","name":"B.1.04","type":"system-x","object-uri":"/api/blades/3949f5a0-02d4-11e1-85df-5cf3fcad1dc8"},{"status":"operating","name":"B.1.02","type":"system-x","object-uri":"/api/blades/749f35cc-02d7-11e1-946a-5cf3fcad1ef8"},{"status":"operating","name":"B.1.08","type":"system-x","object-uri":"/api/blades/75796586-02ea-11e1-8ab0-5cf3fcacf040"}]}
The blade names don't have to be in numerical order (B.1.01 to B.1.08), only on the same line as the corresponding ID
EDIT 2: Found a work around. Used a C type for loop instead of the normal bash: for (( i=10; i<=130; i+=8 )) instead of for i in {10..130..8}
The proper answer to this question is to ditch awk (even though I love awk) and use a real JSON parser, e.g. the very handy jq tool.
If I understand correctly you're wanting {10..130..8} to expand to give the required series of $i values.
In my version of bash (it's ooooold: 3.2.25) the string {10..130..8} doesn't expand to anything and so the loop is entered with i="{10..130..8}" and so awk uses ${10..130..8} which appears to simplify to $0 (i.e. the whole curl return string). Hence your problem. You can test if this is the case by putting echo $i inside your loop.
You need a better way of getting the series of values you want. You can use "seq" for this (man seq for more info). $( seq 10 8 130 ) should do it.
Further, you can make it so that curl is only called once with something messier like
# Construct the string of fields
for i in $( seq 10 8 130 ); do
fields="$fields,\$$i"
done
fields=$( echo "$fields" | sed 's/^,//' ) # Remove the leading comma
...curl command... | awk '{print '$fields'}'
I think you want awk to access the updated value of the i variable. Because the awk instruction is between apostrophes (''), the value of i is hidden from awk, but you can avoid it by removing the apostrophes from the piece of the instruction that is replaced by the actual value of i. It is explained on this online AWK manual, in the section Dynamic Variables.
So for your particular case, you could try
awk -F\" '{print $'$i'}'
instead of
awk -v i=$i -F\" '{print $i}'
at the end of your pipeline command.
How about changing record separator (RS), and defining field separator to quote. Then save the name, and print it together with object-url. The command below should give you a starting point
curl [...] | awk -v RS=, -F\" '{
if ($2 ~ /^name$/) {name=$4}
if ($2 ~ /^object-uri$/) {print name, $4}
}'
p.s. Remote the if's and printing $0 if you want to see how the RS=, helps you.

How to check UID exists using ldapsearch

I would like to have a shell script use 'ldapsearch' to compare UIDs listed in a text file with those on a remote LDAP directory.
I'm no shell script expert, and would appreciate any assistance. The following loops through a text file given as an argument, but what I need is to echo when a UID in my text file does not exist in the LDAP.
#!/bin/sh
for i in `cat $1`;
do ldapsearch -x -H ldaps://ldap-66.example.com -b ou=People,dc=crm,dc=example,dc=com uid=$i | grep uid: | awk '{print $2}';
echo $i
done
Try:
#!/bin/sh
url="ldaps://ldap-66.example.com"
basedn="ou=People,dc=crm,dc=example,dc=com"
for i in `cat $1`; do
if ldapsearch -x -H "$url" -b "$basedn" uid=$i uid > /dev/null
then
# Do nothing
true
else
echo $i
fi
done
Anttix, this is the final version that worked. Thanks again. Does this mark the question as accepted?
#!/bin/sh
url="ldaps://ldap-66.example.com"
basedn="ou=People,dc=crm,dc=example,dc=com"
for i in `cat $1`; do
if ldapsearch -x -H "$url" -b "$basedn" uid=$i | grep uid: | awk '{print $2}' > /dev/null
then
# Do nothing
true
else
echo $i
fi
done
#!/bin/sh
uids="${1:?missing expected file argument}"
URL='ldaps://ldap-66.example.com'
BASE='ou=People,dc=crm,dc=example,dc=com'
ldapsearch \
-x -LLL -o ldif-wrap=no -c \
-H "$URL" \
-b "$BASE" \
-f "$uids" \
'(uid=%s)' uid |
sed \
-r -n \
-e 's/^uid: //p' \
-e 's/^uid:: (.+)/echo "\1"|base64 -d/ep' |
grep \
-F -x -v \
-f - \
"$uids"
Explanation
for the ldapsearch part
You can invoke ldapsearch -f just once to perform multiple searches and read them from a file $uids.
The (uid=%s) is used as a template for each line, where the %s is filled in for each line.
The -c is needed to continue even on errors, e.g. when a UID is not found.
The -x enforces simple authentication with should be considered insecure. Better use SASL.
The -LLL will remove un-needed cruft from the ldapsearch output.
The -o ldif-wrap=no will prevent lines longer than 79 characters from being wrapped - otherwise grep may only pick up the first part of your user names.
The uid tells ldapsearch to only return that attribute and skip all the other attributes we're not interested in; saves some network bandwidth and processing time.
for the sed part
The -r enabled extended regular expressions turning +, (…) into operators; otherwise they have to be pre-fixed with a back-slash \.
The -n tells sed to do not print each line by default but only when told to do so using an explicit print command p.
The s/uid: // will strip that prefix from lines containing it.
The s/uid:: …/…/ep part is needed for handling non-ASCII-characters like Umlauts as ldapsearch encodes them using base64.
The suffix …p will print only those lines which were substituted.
for the grep part
The -F tells grep to not use regular expression pattern matching, but plain text; this becomes important if your names contain ., *, parenthesis, ….
The -x enforces whole line matching so searching for foo will not also match foobar.
The -f - tells grep to read the patterns from STDIN, that is the existing UIDs found by ldapsearch.
The -v inverts the search and will filter out those existing users.
The $uids will again read the list of all requested users. After removing the existing users the list of missing users will remain.
Issues with previous solutions
The previous solutions all had certain kinds of issues:
missing grep
missing quoting which breaks as soon as blanks are involved
use-less use of cat
use-less use of grep when awk is also used
inefficient because for each UID many processes get fork()ed
inefficient because for each UID a separate search is performed
did not work for long user names
did not work for user names containing regular expression meta characters
did not work for user names containing Umlauts

Converting the output of df -h into an array in Bash

I am trying to do a very basic thing, which I though I should be able to manage all on my own, but alas..
What I am attempting to do, is to get hold of the values returned by "df -h", and to be able to store these values in a bash script array. The purpose is to combine it with other data (like the current timestamp) and output this to a new file/overwrite an existing file.
The following two commands give me what I want on the commandline, but I cannot for the life of me load the output into an array that I can iterate over.
The followng gives me the disk utilization in percent for each disk:
df -h | grep -vE "^Filesystem|shm|boot" | awk '{ print +$5 }'
And the following gives me the disk path:
df -h | grep -vE "^Filesystem|shm|boot" | awk '{ print $6 }'
However, I am unable to assign the output of this into a variable inside a shell script that I can iterate over.
ANY ideas and help would be very much appreciated!!
You can assign the output to an array like this:
arr=(`df -h | grep -vE "^Filesystem|shm|boot" | awk '{ print +$5 }'`)
Then you can iterate over it for example like this:
for var in "${arr[#]}"
do
# things
done
You can use this.
#!/bin/bash
arr=(`df -h | grep -vE "^Filesystem|shm|boot" | awk '{ print +$5 }'`)
for v in "${arr[#]}"
do
echo $v
done
(Note only works with bash, not /bin/sh)

Resources