./pingscript.sh - Georgia Weidmann's Book - linux

I'm trying to create a bash script from Georgia Wiedmann's book. I have my XP target machine and my Kali machine up and running.
#!/bin/bash
if [ "$1" == "" ]
then
echo "Usage: ./pingscript.sh [network]"
echo "example: ./pingscript.sh 192.168.x.x"
else
for x in 'seq 1 254'; do
ping -c 1 $1.$x | grep "64 bytes" | cut -d" " -f4 | sed 's/.$//'
done
fi
I don't know whether the 8.8.8.8 should be my XP's IP or Gateway address but when I do ./pingscript.sh 192.168.x.x I should be getting a response but it instead says Ping unknown host.
Anyone help?

Here's shellcheck:
Line 7:
for x in 'seq 1 254'; do
^-- SC2041: This is a literal string. To run as a command, use $(..) instead of '..' .
Quoting its wiki:
The intent was to run the code in the single quotes. This would have worked with slanted backticks, `..`, but here the very similar looking single quotes '..' were used, resulting in a string literal instead of command output.
This is one of the many problems with backticks, so it's better to use $(..) to expand commands.
In other words, use:
for x in $(seq 1 254); do

Related

Bash: New line in echo string fails when output is piped to crontab [duplicate]

How do I print a newline? This merely prints \n:
$ echo -e "Hello,\nWorld!"
Hello,\nWorld!
Use printf instead:
printf "hello\nworld\n"
printf behaves more consistently across different environments than echo.
Make sure you are in Bash.
$ echo $0
bash
All these four ways work for me:
echo -e "Hello\nworld"
echo -e 'Hello\nworld'
echo Hello$'\n'world
echo Hello ; echo world
echo $'hello\nworld'
prints
hello
world
$'' strings use ANSI C Quoting:
Words of the form $'string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard.
You could always do echo "".
For example,
echo "Hello,"
echo ""
echo "World!"
On the off chance that someone finds themselves beating their head against the wall trying to figure out why a coworker's script won't print newlines, look out for this:
#!/bin/bash
function GET_RECORDS()
{
echo -e "starting\n the process";
}
echo $(GET_RECORDS);
As in the above, the actual running of the method may itself be wrapped in an echo which supersedes any echos that may be in the method itself. Obviously, I watered this down for brevity. It was not so easy to spot!
You can then inform your comrades that a better way to execute functions would be like so:
#!/bin/bash
function GET_RECORDS()
{
echo -e "starting\n the process";
}
GET_RECORDS;
Simply type
echo
to get a new line
POSIX 7 on echo
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html
-e is not defined and backslashes are implementation defined:
If the first operand is -n, or if any of the operands contain a <backslash> character, the results are implementation-defined.
unless you have an optional XSI extension.
So I recommend that you should use printf instead, which is well specified:
format operand shall be used as the format string described in XBD File Format Notation [...]
the File Format Notation:
\n <newline> Move the printing position to the start of the next line.
Also keep in mind that Ubuntu 15.10 and most distros implement echo both as:
a Bash built-in: help echo
a standalone executable: which echo
which can lead to some confusion.
str='hello\nworld'
$ echo | sed "i$str"
hello
world
You can also do:
echo "hello
world"
This works both inside a script and from the command line.
On the command line, press Shift+Enter to do the line break inside the string.
This works for me on my macOS and my Ubuntu 18.04 (Bionic Beaver) system.
For only the question asked (not special characters etc) changing only double quotes to single quotes.
echo -e 'Hello,\nWorld!'
Results in:
Hello,
World!
There is a new parameter expansion added in Bash 4.4 that interprets escape sequences:
${parameter#operator} - E operator
The expansion is a string that is the value of parameter with
backslash escape sequences expanded as with the $'…' quoting
mechanism.
$ foo='hello\nworld'
$ echo "${foo#E}"
hello
world
I just use echo without any arguments:
echo "Hello"
echo
echo "World"
To print a new line with echo, use:
echo
or
echo -e '\n'
This could better be done as
x="\n"
echo -ne $x
-e option will interpret backslahes for the escape sequence
-n option will remove the trailing newline in the output
PS: the command echo has an effect of always including a trailing newline in the output so -n is required to turn that thing off (and make it less confusing)
My script:
echo "WARNINGS: $warningsFound WARNINGS FOUND:\n$warningStrings
Output:
WARNING : 2 WARNINGS FOUND:\nWarning, found the following local orphaned signature file:
On my Bash script I was getting mad as you until I've just tried:
echo "WARNING : $warningsFound WARNINGS FOUND:
$warningStrings"
Just hit Enter where you want to insert that jump. The output now is:
WARNING : 2 WARNINGS FOUND:
Warning, found the following local orphaned signature file:
If you're writing scripts and will be echoing newlines as part of other messages several times, a nice cross-platform solution is to put a literal newline in a variable like so:
newline='
'
echo "first line${newline}second line"
echo "Error: example error message n${newline}${usage}" >&2 #requires usage to be defined
If the previous answers don't work, and there is a need to get a return value from their function:
function foo()
{
local v="Dimi";
local s="";
.....
s+="Some message here $v $1\n"
.....
echo $s
}
r=$(foo "my message");
echo -e $r;
Only this trick worked on a Linux system I was working on with this Bash version:
GNU bash, version 2.2.25(1)-release (x86_64-redhat-linux-gnu)
You could also use echo with braces,
$ (echo hello; echo world)
hello
world
This got me there....
outstuff=RESOURCE_GROUP=[$RESOURCE_GROUP]\\nAKS_CLUSTER_NAME=[$AKS_CLUSTER_NAME]\\nREGION_NAME=[$REGION_NAME]\\nVERSION=[$VERSION]\\nSUBNET-ID=[$SUBNET_ID]
printf $outstuff
Yields:
RESOURCE_GROUP=[akswork-rg]
AKS_CLUSTER_NAME=[aksworkshop-804]
REGION_NAME=[eastus]
VERSION=[1.16.7]
SUBNET-ID=[/subscriptions/{subidhere}/resourceGroups/makeakswork-rg/providers/Microsoft.Network/virtualNetworks/aks-vnet/subnets/aks-subnet]
Sometimes you can pass multiple strings separated by a space and it will be interpreted as \n.
For example when using a shell script for multi-line notifcations:
#!/bin/bash
notify-send 'notification success' 'another line' 'time now '`date +"%s"`
With jq:
$ jq -nr '"Hello,\nWorld"'
Hello,
World
Additional solution:
In cases, you have to echo a multiline of the long contents (such as code/ configurations)
For example:
A Bash script to generate codes/ configurations
echo -e,
printf might have some limitation
You can use some special char as a placeholder as a line break (such as ~) and replace it after the file was created using tr:
echo ${content} | tr '~' '\n' > $targetFile
It needs to invoke another program (tr) which should be fine, IMO.

linux bash script -> update remote mysql table -> error [0m

i'm using some bash command to extract the current teamviewer id.
Therefore i use this:
#!/bin/bash
OUTPUT="$(teamviewer --info | grep "TeamViewer ID:" | tr -s " " | cut -d ":" -f$
TEAMVIEWERID="${OUTPUT}"
echo $TEAMVIEWERID
mysql --host=xxx --user=xxx --password=xxx xxx$
update table SET teamviewerID="$TEAMVIEWERID" WHERE client="$1";
EOF
echo "DONE"
if i run it:
pi#xxx:~/Documents/xxx/tv $ sudo ./tv.sh client_xxx
4975XXXXX
DONE
pi#xxx:~/Documents/xxx/tv $
ok everything seems to be fine BUT in mysql i receive the following thing:
[0m 4975XXXXX
I'm confused what is happening here...
thx for helping
The characters at the beginning ([0m) are a so called escape sequence. This specific one is used to clear all terminal formattings.
You can easily strip it by using sed.
Just replace your TEAMVIEWERID= line with the following:
TEAMVIEWERID=$(echo "$OUTPUT" | sed 's/\[0m\s//g')
Edit: If the TeamViewer ID always consists of numbers only, we can strip the unknown character by only allowing numbers:
TEAMVIEWERID=$(echo "$id" | sed 's/\[0m\s//g' | sed -re 's/[^0-9]+//g')
This will only allow numbers.

Mail output with Bash Script

SSH from Host A to a few hosts (only one listed below right now) using the SSH Key I generated and then go to a specific file, grep for a specific word with a date of yesterday .. then I want to email this output to myself.
It is sending an email but it is giving me the command as opposed to the output from the command.
#!/bin/bash
HOST="XXXXXXXXXXXXXXXXXX, XXXXXXXXXXXXX"
DATE=$(date -d "yesterday")
INVALID=' cat /xxx/xxx/xxxxx | grep 'WORD' | sed 's/$/.\n/g' | grep "$DATE"'
COUNT=$(echo "$INVALID" | wc -c)
for x in $HOSTS
do
ssh BLA#"$x" $COUNT
if [ "$COUNT" -gt 1 ];
then
EMAILTEXT=""
if [ "$COUNT" -gt 1 ];
then
EMAILTEXT="$INVALID"
fi
fi
done | echo -e "$EMAILTEXT" | mail XXXXXXXXXXX.com
This isn't properly an attempt to answer your question, but I think you should be aware of some fundamental problems with your code.
INVALID=' cat /xxx/xxx/xxxxx | grep 'WORD' | sed 's/$/.\n/g' | grep "$DATE"'
This assigns a simple string to the variable INVALID. Because of quoting issues, s/$/.\n/g is not quoted at all, and will probably be mangled by the shell. (You cannot nest single quotes -- the first single-quoted string extends from the first quote to the next one, and then WORD is outside of any quotes, followed by the next single-quoted string, etc.)
If your intent is to execute this as a command at this point, you are looking for a command substitution; with the multiple layers of uselessness peeled off, perhaps something like
INVALID=$(sed -n -e '/WORD/!d' -e "/$DATE/s/$/./p" /xxx/xxx/xxxx)
which looks for a line matching WORD and $DATE and prints the match with a dot appended at the end -- I believe that's what your code boils down to, but without further insights into what this code is supposed to do, it's impossible to tell if this is what you actually need.
COUNT=$(echo "$INVALID" | wc -c)
This assigns a number to $COUNT. With your static definition of INVALID, the number will always be 62; but I guess that's not actually what you want here.
for x in $HOSTS
do
ssh BLA#"$x" $COUNT
This attempts to execute that number as a command on a number of remote hosts (except the loop is over HOSTS and the variable containing the hosts is named just HOST). This cannot possibly be useful, unless you have a battery of commands named as natural numbers which do something useful on these remote hosts; but I think it's safe to assume that that is not what is supposed to be going on here (and if it was, it would absolutely be necessary to explain this in your question).
if [ "$COUNT" -gt 1 ];
then
EMAILTEXT=""
if [ "$COUNT" -gt 1 ];
then
EMAILTEXT="$INVALID"
fi
fi
So EMAILTEXT is either an empty string or the value of INVALID. You assigned it to be a static string above, which is probably the source of your immediate question. But even if it was somehow assigned to a command on the local host, why do you need to visit remote hosts and execute something there? Or is your intent actually to execute the command on each remote host and obtain the output?
done | echo -e "$EMAILTEXT" | mail XXXXXXXXXXX.com
Piping into echo makes no sense at all, because it does not read its standard input. You should probably just have a newline after done; though a possibly more useful arrangement would be to have your loop produce output which we then pipe to mail.
Purely speculatively, perhaps something like the following is what you actually want.
for host in $HOSTS; do
ssh BLA#"$host" sed -n -e '/WORD/!d' -e "/$DATE/s/$/./p" /xxx/xxx/xxxx |
grep . || echo INVALID
done | mail XXXXXXXXXXX.com
If you want to check that there is strictly more than one line of output (which is what the -gt 1 suggests) then this may need to be a little bit more complicated.
Your command substitution is not working. You should read up on how it works but here are the problem lines:
COUNT=$(echo "$INVALID" | wc -c)
[...]
ssh BLA#"$x" $COUNT
should be:
COUNT_CMD="'${INVALID} | wc -c'"
[...]
COUNT=$(ssh BLA#"$x" $COUNT_CMD)
This inserts the value of $INVALID into the string, and puts the whole thing in single quotes. The single quotes are necessary for the ssh call so the pipes aren't evaluated in the script but on the remote host. (COUNT is changed to COUNT_CMD for readability/clarity.)
EDIT:
I misread the question and have corrected my answer.

Bash loop is not working — cannot find command "[0%"

I just wrote a ping sweep script in Bash this morning, and guess what: it's not working. Can you please check what it is that I'm missing.
Here's the script:
for i in `seq 1 255`
do
if ["$(ping -c1 -W1 -n 192.168.1.$i | grep '%' | cut -d',' -f3 | cut -d' ' -f2)" -eq "0%"]
then echo "Host live"
else echo "Host down"
fi
done
And here's the error:
bash: [0%: command not found
Host down
bash: [100%: command not found
Host down
My purpose is to make a ping sweep program which scans the range 192.168.1.1-255 and it notifies the host's status. I know about nmap but just wanted to learn skills in Bash so I made this one. Please try to tell what the error meant. I mean to what command it's referring "command not found"?
The ping command returns error code if there was any problem, so you do not need to parse the output:
for i in {1..255}
do
if ping -c1 -W1 -n "192.168.1.$i"
then
echo 'Host live'
else
echo 'Host down'
fi
done
Primary diagnosis
The [ command needs a space after its name, just like the rm command needs a space after its name and the ls command does, and … The [ command also requires its last argument to be ], spelled thus, so there needs to be a space before that, too.
You have:
if ["$(ping -c1 -W1 -n 192.168.1.$i | grep '%' | cut -d',' -f3 | cut -d' ' -f2)" -eq "0%"]
At minimum, you need:
if [ "$(ping -c1 -W1 -n 192.168.1.$i | grep '%' | cut -d',' -f3 | cut -d' ' -f2)" -eq "0%" ]
Secondary issues
Note that 'at minimum' means, amongst other things, that I've not spent time analyzing why you are executing the complex sequence of 4 commands in the test condition, or looked for ways to cut that down to two (using grep and cut twice suggests that sed or a more powerful tool would be better). I griped about the formatting in the original version of the question, where the loop (it isn't a nested loop, incidentally — or it isn't in the code shown) was all on one line thanks to Bash flattening it in history. My version of the code would have far fewer semicolons in it, for example. The -eq operator in [ is for testing the equality of numbers (the converse convention applies in Perl, where eq is for testing strings and == tests numbers). Note that POSIX standard [ (aka test) does not support == as a synonym for =, though Bash does. It isn't entirely clear that "0%" is OK as an argument for numeric comparison. Many programs would not object — the zero can be converted and the residue doesn't matter; others might decide legitimately to complain that the whole string could not be converted, so it is erroneous. Careful code wouldn't risk the disconnect.
See Steven Penny's answer for a more thorough rewrite of the code. My answer remains a valid diagnosis of the immediate problem of not being able to find commands named [0% and [100%.

Bash, Escaping quotes in command construction,

I have an array of numbers - sipPeers.
I want to iterate trough that array and pass each value into the command
asterisk -rx "sip show peer 1234" - where 1234 is obviously an extension.
The output of sip show peer is piped and manipulated twice to output one value which I want to store in a second array sipUserAgent.
temp="asterisk -rx \"sip show peer "${sipPeers[q]}"\" | grep Useragent | awk \'{print$3}\'" #(1)
echo $temp #(2)
sipUserAgent[q]=$($temp) #(3)
The output of one iteration yields the constructed command as a string (2) and the tries to execute it (3):
asterisk -rx "sip show peer 564" | grep Useragent | awk '{print }'
No such command '"sip' (type 'core show help sip' for other possible commands)
If I copy and paste the echo'd command it runs, but when the script executes I get that error. Somewhere the " character changes meaning when the scipt executes it?
Don't try to store the command in a variable before executing it. There are all sorts of problems with doing this, and you're running into several of them (see BashFAQ #50: I'm trying to put a command in a variable, but the complex cases always fail!). Just put the pipeline directly inside $( ... ) where it belongs:
sipUserAgent[q]=$(asterisk -rx "sip show peer ${sipPeers[q]}" | grep Useragent | awk '{print $3}')
1st problem
In the step (1) you write: awk \'{print$3}\'
In the output of (2) you receive: awk '{print }'
The problem is an missed escape of $3
Try is again with awk \'{print\$3}\' in (1)
2nd problem
sipUserAgent[q]=$($temp) tries to invoke a command asterisk_-rx...
This is surely not what you want.
The solution is to use eval - see help eval for more informations.
example
cmd="echo TEST"
$(${cmd})
gives:
TEST: command not found
cmd="echo TEST"
eval ${cmd}
gives:
TEST
Note about code style
If possible it is better to avoid complex command creation and execution via eval.
Better is to write more simple code, if applicable.
So you should use (as also mentioned by Gordon Davisson in another answer):
sipUserAgent[q]=$(asterisk -rx "sip show peer ${sipPeers[q]}" \
| grep Useragent \
| awk '{print $3}' \
)
I am not aware of a way to escape quotes during command substitution, but a workaround is to use eval. See the following example:
$ cat test.sh
echo "$1"
$ temp="./test.sh \"2 34 434\" "
$ echo "$temp"
./test.sh "2 34 434"
$ result=$(eval $temp)
$ echo "$result"
2 34 434
$ result=$($temp)
$ echo "$result"
"2
Note that using eval is generally considered bad practice in most situations, if you can avoid it.

Resources