bash script - store multiple outputs of a command in separate variables - bash

I'd like to store the ouput of the following command in separate variables for each IP adress:
ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1'
Does anyone have an idea how to do this?

The requirement to store the values as separate variables seems out of place. Just produce the list, and loop over it or whatever.
Tangentially, you really want to lose the long chain of greps.
ifconfig |
awk '$1 == "inet" && ($2 != "127.0.0.1" && $2 != "addr:") { print $2 }
$1 == "inet" && $2 == "addr:" && $3 != "127.0.0.1" { print $3 }'
To assign the result to a Bash array, try
array=($(ifconfig | awk '...'))
but more often than not, you probably just want to loop over the result:
ifconfig |
awk '...' |
while IFS='' read -r ip; do
# something with "$ip"
done

Related

How to grep first match and second match(ignore first match) with awk or sed or grep?

> root# ps -ef | grep [j]ava | awk '{print $2,$9}'
> 45134 -Dapex=APEC
> 45135 -Dapex=JAAA
> 45136 -Dapex=APEC
I need to put the first APEC of first as First PID, third line of APEC and Second PID and last one as Third PID.
I've tried awk but no expected result.
> First_PID =ps -ef | grep [j]ava | awk '{print $2,$9}'|awk '{if ($0 == "[^0-9]" || $1 == "APEC:") {print $0; exit;}}'
Expected result should look like this.
> First_PID=45134
> Second_PID=45136
> Third_PID=45135
With your shown samples and attempts please try following awk code. Written and tested in GNU awk.
ps -ef | grep [j]ava |
awk '
{
val=$2 OFS $9
match(val,/([0-9]+) -Dapex=APEC ([0-9]+) -Dapex=JAAA\s([0-9]+)/,arr)
print "First_PID="arr[1],"Second_PID=",arr[3],"Third_PID=",arr[2]
}
'
How about this:
$ input=("1 APEC" "2 JAAA" "3 APEC")
$ printf '%s\n' "${input[#]}" | grep APEC | sed -n '2p'
3 APEC
Explanation:
input=(...) - input data in an array, for testing
printf '%s\n' "${input[#]}" - print input array, one element per line
grep APEC - keep lines containing APEC only
sed -n - run sed without automatic print
sed -n '2p' - print only the second line
If you just want the APECs first...
ps -ef |
awk '/java[ ].* -Dapex=APEC/{print $2" "$9; next; }
/java[ ]/{non[NR]=$2" "$9}
END{ for (rec in non) print non[rec] }'
If possible, use an array instead of those ordinally named vars.
mapfile -t pids < <( ps -ef | awk '/java[ ].* -Dapex=APEC/{print $2; next; }
/java[ ]/{non[NR]=$2} END{ for (rec in non) print non[rec] }' )
After read from everyone idea,I end up with the very simple solution.
FIRST_PID=$(ps -ef | grep APEC | grep -v grep | awk '{print $2}'| sed -n '1p')
SECOND_PID=$(ps -ef | grep APEC | grep -v grep | awk '{print $2}'| sed -n '2p')
JAWS_PID=$(ps -ef | grep JAAA | grep -v grep | awk '{print $2}')

grep match a concat of variable and string (dash) in piped input

(NOTE: this is a bash question, not k8s)
I have a working script which will fetch the name
admin-job-0
from a list of kubernetes cronjobs, of which there can be up to 32 ie. admin-job-0 -1, -2, -3 ... -31
Question: How do I grep "-$1$" ie a dash, the number, and no more, instead of just the number as I have below?
Bonus question: Is there any way to do what I'm doing below without the if/else logic regardless of whether there's an argument passed?
fetch-admin-job() {
if [[ -n $1 ]]; then
name=$(kubectl get cronjob | awk '/^admin-job.*/{print $1}' | grep $1 )
else
# get the first one (if any)
name=$(kubectl get cronjob | awk '/^admin-job.*/{print $1}')
fi
echo $name
}
#example:
fetch-admin-job 0
You can replace your function code with this:
fetch-admin-job() {
kubectl get cronjob |
awk -v n="$1" '!n || $1 == "admin-job-" n {print $1}'
}
Then invoke it as:
fetch-admin-job 0
fetch-admin-job 4
fetch-admin-job
We are using this condition in awk:
!n: will be true when you don't pass anything in first argument
||: OR
$1 == "admin-job-" n: Will be used to compare first column in output of kubectl command with first argument you pass. Note that this is equivalent of awk '/^admin-job/ ...' | grep "-$1$".
You don't need to use grep on an awk output as awk can handle that part as well.
If you pass to grep a double-hyphen (--), this signals the end of the option and a dash at the start of the pattern does not harm, i.e.
grep -- "$1"
or
grep -- "$1$"
or whatever you want to achieve.

How to reference multiple string values in array in Shell script

I am trying to store multiple string in for loop but it giving me unwanted answer.
My code is :
#!/bin/bash
declare -a arr=("ps -ef | grep icsmpgum | grep $USER | grep -v grep | awk '{print $9,$8}' | awk '{print $1}'")
for i in "${arr[#]}"
do
echo "$i"
done
The output of
ps -ef | grep icsmpgum | grep $USER | grep -v grep | awk '{print $9,$8}' | awk '{print $1}'
is :
icsmpgum
ABC
DEF
I want to refer to these 3 string values in for loop but after applying for loop as mention above it giving me output as :
Output :
ps -ef | grep icsmpgum | grep tsaprm1 | grep -v grep | awk '{print ,}' | awk '{print }'
How should I store these string values in variables ?
You need to use a command substitution, rather than quoting the command:
arr=( $(ps -ef | grep icsmpgum | grep $USER | grep -v grep | awk '{print $9,$8}' | awk '{print $1}') )
I suspect that this will work but there's a lot of further tidying up to be done; all the filtering that you want to do is possible in one call to awk:
arr=( $(ps -ef | awk -v user="$USER" '!/awk/ && /icsmpgum/ && $0 ~ user { print $9 }') )
As mentioned in the comments, there are potential risks to building an array like this (e.g. glob characters such as * would be expanded and you would end up with extra values in the array). A safer option would be to use a process substitution:
read -ra arr < <(ps -ef | awk -v user="$USER" '!/awk/ && /icsmpgum/ && $0 ~ user { print $9 }')

if-then-else based on command output

I am using netstat command to get information on networks. I want to put a condition here on protocol fetched. If it's TCP, I need to print different columns than if it's UDP.
Below is what I am trying to do, but it doesn't work. Please suggest and advise if there is something I am doing wrong:
if [$(netstat -anputw | awk '{print $1}')=="tcp"] then
netstat -anputw | awk '{print $1,",",$4"}' >> $HOME/MyLog/connections_$HOSTNAME.csv
elif [$(netstat -anputw | awk '{print $1}')=="udp"] then
netstat -anputw | awk '{print $5,",",$7}' >> $HOME/MyLog/connections_$HOSTNAME.csv
fi
I don't know what you're trying to achieve, however I think that netstat returns a list rather than a string, therefore comparing the output against a string is pointless. You have to loop it. Try the following
#!/bin/bash
OUTPUT=$(netstat -anputw | awk '{print $1}');
for LINE in $OUTPUT
do
if [[ $LINE == "tcp" ]] ; then
echo "TCP!"
elif [[ $LINE == "udp" ]] ; then
echo "UDP!"
fi
done
Why don't you separate the two cases by not asking netstat to mix them up?
netstat -anptw # Just tcp
netstat -anpuw # Just udp
Also, in the tcp case, you don't seem to care about the -p information so you might as well not ask for it. And in the udp case, there is no data in the State column, so the PID/Program name will actually be in column 6.
Putting that together, I get:
netstat -antw | awk '{print $1","$4}' >> $HOME/MyLog/connections_$HOSTNAME.csv
netstat -anpuw | awk '{print $5","$6}' >> $HOME/MyLog/connections_$HOSTNAME.csv
I suspect that isn't quite the information you're looking for either, though. Perhaps you want to distinguish between TCP listening and non-listening connections.
Always leave a space after [ and before ] in if statement and you should put command $(netstat -anputw | awk '{print $1}' under double quotes if you are doing string comparison.
Here's the final script:
if [ "$(netstat -anputw | awk '{print $1}')" == "tcp" ]; then
netstat -anputw | awk '{print $1,",",$4"}' >> $HOME/MyLog/connections_$HOSTNAME.csv
elif [ "$(netstat -anputw | awk '{print $1}') " == "udp" ]; then
netstat -anputw | awk '{print $5,",",$7}' >> $HOME/MyLog/connections_$HOSTNAME.csv
fi

Bash grep local IP address and write to file in makefile?

I'm trying to get my local ip with ifconfig and write it to file but for some reason fail to do so.
run:
LOCALIP=$(shell ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | awk '{print $1}') &
echo "var LOCAL_IP = '${LOCALIP}'" > local_ip.js &
It does get the correct IP but for some reason fails to write it to file and file contains only var LOCAL_IP = ''
Could anyone help me and tell what I'm doing wrong?
Two issues: one, each separate command in a Makefile recipe is executed in a separate shell, so you need to "merge" them. Two, you need to double the $ in the echo so that it is sent to the shell, not expanded first by make. This should work:
run:
LOCALIP=$(shell ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | awk '{print $1}'); \
echo "var LOCAL_IP = '$${LOCALIP}'" > local_ip.js
The semicolon and the line-continuation backslash ensure that the entire recipe is executed in the same shell, so that LOCALIP is still set when the echo is run. You could "simplify" this by embedding the command substitution directly in the argument to echo.
run:
echo "var LOCAL_IP = '$(shell ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | awk '{print $1}')'" > local_ip.js
This awk can work:
ifconfig | awk -F: '/inet addr/&& !($2 ~ /127\.0\.0\.1/){gsub(/ .*/, "", $2); print $2}'

Resources