A former network engineer was using Xymon for node monitoring. This shell script was previously used to compare actively monitored nodes against the routing table of our core switch and output data accordingly to show any new networks.
Attempts to execute the shell script now return error:
awk: cmd. line:1: warning: escape sequence `\.' treated as plain `.'
Here's the commands applied to shell script:
cat /var/lib/rancid/dnow/configs/ushouston-dnw1-cr01 | awk '/display ip routing-table/{flag=1;next}/display vlan all/{flag=0}f$
ROUTELIST=$(grep -E "\.0/2[2,3,4]" /tmp/routes.txt | grep -v 192.168.13.1 | awk '{print $2}' | awk -F. '{print $1"\."$2"\."$3}$
for ROUTE in $ROUTELIST ; do
CHECK=$(grep -w $ROUTE /etc/hosts)
if [ "$CHECK" = "" ] ; then
echo "$ROUTE is not monitored"
fi
done
Any assistance or guidance understanding why the error is received and what needs to be adjusted is greatly appreciated.
Edit: I failed to mention this is a Linux system, kernel version 3.16.0-44-generic.
The warning is coming from this:
{print $1"\."$2"\."$3}
There's no need to escape . in ordinary strings, only in regular expressions. That should be:
{print $1"."$2"."$3}
Related
I'm developping a script to search for patterns within scripts executed from CRON on a bunch of remote servers through SSH.
Script on client machine -- SSH --> Remote Servers CRON/Scripts
For now I can't get the correct output.
Script on client machine
#!/bin/bash
server_list=( '172.x.x.x' '172.x.x.y' '172.x.x.z' )
for s in ${server_list[#]}; do
ssh -i /home/user/.ssh/my_key.rsa user#${s} crontab -l | grep -v '^#\|^[[:space:]]*$' | cut -d ' ' -f 6- | awk '{print $1}' | grep -v '^$\|^echo\|^find\|^PATH\|^/usr/bin\|^/bin/' | xargs -0 grep -in 'server.tld\|10.x.x.x'
done
This only gives me the paths of scripts from crontab, not the matched lines and line number plus the first line is prefixed with "grep:" keyword (example below):
grep: /opt/directory/script1.sh
/opt/directory/script2.sh
/opt/directory/script3.sh
/opt/directory/script4.sh
How to get proper output, meaning the script path plus line number plus line of matching pattern?
Remote CRON examples
OO 6 * * * /opt/directory/script1.sh foo
30 6 * * * /opt/directory/script2.sh bar
Remote script content examples
1 ) This will match grep pattern
#!/bin/bash
ping -c 4 server.tld && echo "server.tld ($1)"
2 ) This won't match grep pattern
#!/bin/bash
ping -c 4 8.x.x.x && echo "8.x.x.x ($1)"
Without example input, it's really hard to see what your script is attempting to do. But the cron parsing could almost certainly be simplified tremendously by refactoring all of it into a single Awk script. Here is a quick stab, with obviously no way to test.
#!/bin/sh
# No longer using an array for no good reason, so /bin/sh will work
for s in 172.x.x.x 172.x.x.y 172.x.x.z; do
ssh -i /home/user/.ssh/my_key.rsa "user#${s}" crontab -l |
awk '! /^#|^[[:space:]]*$/ && $6 !~ /^$|^(echo|find|PATH|\/usr\/bin|\/bin\/)/ { print $6 }' |
# no -0; use grep -E and properly quote literal dot
xargs grep -Ein 'server\.tld|10.x.x.x'
done
Your command would not output null-delimited data to xargs so probably the immediate problem was that xargs -0 would receive all the file names as a single file name which obviously does not exist, and you forgot to include the ": file not found" from the end of the error message.
The use of grep -E is a minor hack to enable a more modern regex syntax which is more similar to that in Awk, where you don't have to backslash the "or" pipe etc.
This script, like your original, runs grep on the local system where you run the SSH script. If you want to run the commands on the remote server, you will need to refactor to put the entire pipeline in single quotes or a here document:
for s in 172.x.x.x 172.x.x.y 172.x.x.z; do
ssh -i /home/user/.ssh/my_key.rsa "user#${s}" <<\________HERE
crontab -l |
awk '! /^#|^[[:space:]]*$/ && $6 !~ /^$|^(echo|find|PATH|\/usr\/bin|\/bin\/)/ { print $6 }' |
xargs grep -Ein 'server\.tld|10.x.x.x'
________HERE
done
The refactored script contains enough complexities in the quoting that you probably don't want to pass it as an argument to ssh, which requires you to figure out how to quote strings both locally and remotely. It's easier then to pass it as standard input, which obviously just gets transmitted verbatim.
If you get "Pseudo-terminal will not be allocated because stdin is not a terminal.", try using ssh -t. Sometimes you need to add multiple -t options to completely get rid of this message.
I was unsure on how to correctly script a particular awk command which uses a shell variable, when I read the answers to How do I use shell variables in an awk script?.
The accepted answer demonstrates how interpolating a shell variable in an awkcommand would be prone to malicious code injection, and while I was able to reproduce the demo, I could not find the same problem with either of the following two commands:
#HWLINK=enp10s0
ip -o route | awk '/'$HWLINK'/ && ! /default/ {print $1}'
ip -o route | awk "/$HWLINK/"' && ! /default/ {print $1}'
So, the main question is if any of these (or both) is vulnerable.
A secondary question would be which form is preferred. I tried ip -o route | awk -v hwlink="$HWLINK" '/hwlink/ && ! /default/ {print $1}' but that doesn't work.
p.s. this is a refactoring; the original command was ip -o route | grep $HWLINK | grep -v default | awk '{print $1}'.
Sure, both are vulnerable, the first a bit less so.
This breaks your second line:
HWLINK="/{}BEGIN{print \"Your mother was a hamster and your father smelt of elderberries\"}/"
The only reason it doesn't break your first line is, in order to be able to be injected into the first line it must not contain spaces.
HWLINK="/{}BEGIN{print\"Your_mother_was_a_hamster_and_your_father_smelt_of_elderberries\"}/"
I see you already got the correct syntax to use :)
Your idea was right about letting the shell variables getting interpolated inside awk could let malicious code injection. As rightly pointed use the -v syntax, but your attempt fails because the pattern match with variable doesn't work in the form /../, use the direct ~ match
ip -o route | awk -v hwlink="$HWLINK" '$0 ~ hwlink && ! /default/ {print $1}'
Recommended way to sanitize your variables passed to awk would be to use the ARGV array or ENVIRON variable. Variables passed this way don't undergo expansion done by the shell
value='foo\n\n'
awk 'BEGIN {var=ARGV[1]; delete ARGV[1]}' "$value"
If you printed the value of var inside the awk it would be a literal foo\n\n and not the multi-line string which usually happens when the shell expands it.
This question already has answers here:
Command not found error in Bash variable assignment
(5 answers)
Closed 4 years ago.
Im having trouble calling a variable that should bring out the output of a command.
#!/bin/bash
ipAddresses = 'ifconfig | awk -v OFS=": " -v RS= '$1!="lo" && split($0, a, /inet addr:/) > 1{sub(/ .*/, "", a[2]); print $1, a[2]}''
echo -e "Sus direcciones IP son: \n " $(ipAddresses)
Appreciating any advice
Variable assignments cannot have space around the = in the shell. Also, you don't want single quotes there, you want either backticks or $(). The single quotes should only be for your awk command. Your awk is needlessly complicated as well, and you are using command substitution ($()) when printing, but ipAdresses is a variable, not a command.
Try something like this:
#!/bin/bash
ipAddresses=$(ifconfig | sed 's/^ *//' | awk -F'[: ]' '/^ *inet addr:/{print $3}')
printf 'Sus direcciones IP son:\n%s\n' "$ipAddresses"
But that is really not portable. You didn't mention your OS, but I am assuming it's a Linux and the output suggests Ubuntu (I don't have addr after inet in the output of ifconfig on my Arch, for example).
If you are running Linux, you could use grep instead:
ipAddresses=$(ifconfig | grep -oP 'inet addr:\K\S+')
ip is generally replacing ifconfig, so try this instead:
ipAddresses=$(ip addr | awk '/inet /{print $2}')
or
ipAddresses=$(ip addr | grep -oP 'inet \K\S+')
Or, to remove the trailing /N:
ipAddresses=$(ip addr | grep -oP 'inet \K[\d.]+')
And you don't need the variable anyway, you can just:
printf 'Sus direcciones IP son:\n%s\n' "$(ip addr | awk '/inet /{print $2}')"
I am not sure about your intention, since they are not stated, so I am trying to guess them from the script.
Option 1: you are trying to get IP address to into the variable ipAddresses and that is not happenning.
Start by changing single quotes around the long command and debug the command.
Option 2: you are storing a command in variable ipAddresses that you want to execute on the second line.
For both of the options you need to use the the value of the variable through $ipAdresses on the second line.
Also fix the assignment to following formart:
varName="value" # Note no spaces around = sign
Replace the final $(ipAddresses) with ${ipAddresses} or just "$ipAddresses", but also save the output of your command using $().
Check Difference between ${} and $() in Bash.
A basic example:
#!/bin/sh
OUTPUT=$(uname -a)
echo "The output: $OUTPUT"
I am writing a small script to map all the current memory being used by services running in a server. However, I am facing a problem doing that. My script is quite simple. I'm using pmap to find out memory being used and trying add up all the pid of a service running.
#!/bin/bash
result=`$pgrep java`
wc=`$pmap -x $result | wc -l`
gawk=`$pmap -x $result | gawk 'NR==$wc{print $3}'`
echo "$gawk"
Now, my problem is that gawk uses single quote when searching for a specific pattern (gawk 'NR==$wc{print $3}') but shell script gives me error because then meaning of single quote is different in shell from gawk.
Based on your comment, it looks like you're trying to do this:
pmap -x "$(pgrep java)" | awk '{s=$3}END{print s}'
This prints the third column of the last line of the output of pmap -x, with the PID of the running java process. In some versions of awk, you can simply do 'END{print $3}' but this isn't guaranteed to work.
pmap -x $result | gawk 'NR==$wc{print $3}' is not doing what you think it is. (I have replaced your $pmap with pmap, but my analysis is only of the gawk command so if that is incorrect it should be irrelevant.) The shell is going to pass the literal string NR==$wc{print $3} to awk, but it appears that you want awk to see the value of the shell variable $wc rather than the literal string $wc. When awk sees $wc, it treats wc an an uninitialized value, so $wc become equivalent to $0, and awk will print any line whose content matches the line number. The standard way to pass the shell variable into awk is:
pmap -x $result | gawk 'NR==w{print $3}' w=$wc
This assignes the shell variable wc to the awk variable w, and will print the third column of that line.
Note that there are a number of issues with this shell script, but this seems to be the core confusion.
I wonder why this command:
FILE=`file /usr/bin/java | tr -d \`\' | awk '{print $5}'`
Results in this error message:
bash: command substitution: line 1: unexpected EOF while looking for matching ``'
bash: command substitution: line 2: syntax error: unexpected end of file
If I run the previous command without assigning it to a variable, it works as expected:
$ file /usr/bin/java | tr -d \`\' | awk '{print $5}'
/etc/alternatives/java
Does anyone know why this happens and how can I successfully assign the output value to a variable?
Note: for the curious, I'm trying to find the pointed path to a binary file from a symbolic link, so I can find out if it is a 32 or 64 bits file (in a generic way, not using something like java -version)
Note 2: I've tried removing quotes with sed instead of tr, but it returns the same error
Thank you very much in advance, regards...
Nacho
I think it's because you enclosed the commands inside back-ticks. Use $() instead of backticks.
FILE=$(file /usr/bin/java | tr -d \`\' | awk '{print $5}')