LinuxQuestions.org
Visit Jeremy's Blog.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie
User Name
Password
Linux - Newbie This Linux forum is for members that are new to Linux.
Just starting out and have a question? If it is not in the man pages or the how-to's this is the place!

Notices


Reply
  Search this Thread
Old 03-14-2010, 06:46 AM   #1
lupusarcanus
Senior Member
 
Registered: Mar 2009
Location: USA
Distribution: Arch
Posts: 1,022
Blog Entries: 19

Rep: Reputation: 146Reputation: 146
BASH Script, 'awk' and 'if' question.


Hey guys!

Well, I am making my second BASH script ever, and have learned quite a bit. The concept that I am having a lot of trouble with is the 'if' and 'fi' syntax, and what not.

Anyways, I have gotten my script to already have a license agreement, root user check, and a dependency check already! (Way too much caffeine, lol)

OK, so here is the goal. I want to run the "ifconfig" command and grab the interface label of the first usable wireless device, then make it a variable for use later in the script. The problem here is this script is made to be modular. So, there is a high possibility that there will be many interface labels out there. I want the script to be able to automatically realise each individual systems' interface label, and print just the label so I can read it and use it later.

Here is what I have so far:-

Code:
if [ "ifconfig | awk /wlan/ | awk '{print $1}' | grep -q wlan" -eq 0 ]; then
	ifconfig | awk /wlan/ | awk '{print $1}'
	read wlan
fi
if [ "ifconfig | awk /rausb/ | awk '{print $1}' | grep -q rausb" -eq 0 ]; then
	ifconfig | awk /rausb/ | awk '{print $1}'
	read rausb
fi
and so on... (same syntax, different labels to try)

A couple of questions here.

1: Will this work?
2: Is there a better way I can code this?

Also, I only want one variable for use later. Is it possible to have only one, or have the script 'know' which one to use? This is getting confusing, lol. And I'm only at the start!

Thanks in advance.

Last edited by lupusarcanus; 03-14-2010 at 07:35 AM. Reason: grammar, spelling, clarification
 
Old 03-14-2010, 07:42 AM   #2
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852

Rep: Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037
First of all, you'd need to embed the command inside $() to use it in the test. Also, there's no need to call two instances of awk when one can do. Or even just use grep alone with the right flags.

I think it would be better to simply put the device into a variable first, then test that variable as you need it.

Code:
wdev=$( ifconfig | grep -o -m 1 '^wlan[0-9]*' )
#the "-m 1" option in grep stops it after the first match.

if [[ {wdev/#wlan} -eq 0 ]] ; then

    do commands

fi

or

case ${wdev/#wlan} in

     0) do commands for wan0 ;;
     1) do commands for wan1 ;;

esac
#See how I used parameter substitution to strip the "wlan" off the interface.

#I also used the [[ ]] extended double-bracket test. It's not necessary, but it's more stable and flexible than the single-bracket version. I've been getting into the habit of using it instead.

#A case statement often works better when you have to test a single variable for multiple possible values.

I'm afraid I don't quite understand your last question about "one variable". Could you please explain in more detail?
 
Old 03-14-2010, 08:51 AM   #3
lupusarcanus
Senior Member
 
Registered: Mar 2009
Location: USA
Distribution: Arch
Posts: 1,022

Original Poster
Blog Entries: 19

Rep: Reputation: 146Reputation: 146
I would like to thank you for your intelligent response, David. I did not know grep was that powerful.

Secondly, when I ran your modified version of the script, it resulted in this error:-

Code:
line 5: [: {wlan0}: integer expression expected
Third:-
Code:
if [[ {wdev/#wlan} -eq 0 ]] ; then

    ...
    ...
    ...
    ...

fi
would make it so that I would have to copy & paste the entire rest of the script in each 'if' block. Is there a way around that?

Lastly, to clarify what I meant by that last question. Lets say I have three variables:-
Code:
wlan
rausb
wifi
And when the script runs, only one of these is the 'right' one, and is the one I would like to use. How would I tell BASH to use the only variable with text in it, up to that point?

I was thinking something like this would work?
Code:
if [ "$wlan|$rausb|$wifi | sed 's/  *//g' | grep -q ' *'" -eq 0 ]; then

   echo "$wlan|$rausb|$wifi | sed 's/  *//g'" >/dev/null
   read $interface
   echo "The interface label of your wireless device is $interface"

else

   echo "No usable wireless device was detected"
   exit 1

fi

# Rest of script...
The idea is that I check for all possible interface labels and grab them as variables, then have a block like the one above decipher the 'right' one, and continue on with the rest of the script without fitting it in a function.

Does that clarify my intention a bit? Sorry if I am unclear.

Last edited by lupusarcanus; 03-14-2010 at 09:07 AM. Reason: code clean-up, formatted better
 
Old 03-14-2010, 09:58 AM   #4
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
Quote:
Originally Posted by leopard View Post
Secondly, when I ran your modified version of the script, it resulted in this error:-

Code:
line 5: [: {wlan0}: integer expression expected
It was intended to be ${wdev/#wlan} which is shell parameter expansion and means "the value of $wdev with "wlan" at the beginning of the value changed to nothing". The same thing could have been achieved by ${wdev#wlan} which means "the value of $wdev with "wlan" removed from the left".

Last edited by catkin; 03-14-2010 at 09:59 AM. Reason: Missing space
 
Old 03-14-2010, 10:04 AM   #5
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852

Rep: Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037
...when I ran your modified version of the script, it resulted in this error:-

Code:
line 5: [: {wlan0}: integer expression expected
First of all, you need to use $var or ${var} to expand the value of a variable.

Second, "-eq" is an integer operator. It works in my example because I used ${var/#wlan} to strip the "wlan" part off of the front of the variable, leaving only the number. You have two options here. First, you can re-set the variable so that it permanently contains only the integer:
Code:
wdev=${wdev/#wlan}
if [[ ${wdev} -eq 0 ]] ; then
or else use a string operator instead:
Code:
if [[ $wdev = wlan0 ]] ; then


Quote:
...would make it so that I would have to copy & paste the entire rest of the script in each 'if' block. Is there a way around that?
Yes, you can use a function. Functions act like scripts within a script.
Code:
function repeatedcode {

    local x=foo
    Your commands $1 $x

}

if [[ ${wdev} = wlan0 ]] ; then

    repeatedcode $wdev 

fi
# $1, etc act the same as the input parameters of the script itself, but only exist within the function. "local" sets variables that are only available within the function, so that they don't conflict with the rest of the script.


Quote:
Lastly, to clarify what I meant by that last question. Lets say I have three variables: .... And when the script runs, only one of these is the 'right' one, and is the one I would like to use. How would I tell BASH to use the only variable with text in it, up to that point?

I was thinking something like this would work?
Code:
if [ "$wlan|$rausb|$wifi | sed 's/  *//g' | grep -q ' *'" -eq 0 ]; then
Again, you need to enclose your commands in $() in order to use their output inside another command, such as test.
Code:
if [[ $(series | of | commands) = string ]]; then
But all you really need to do is run an if-elif-else on all three. The first one that matches will be run, and the rest ignored.
Code:
if [[ $wlan ]]; then

    repeatedcode $wlan

elif [[ $rausb ]]; then

    repeatedcode $rausb

elif [[ $wifi ]]; then

    repeatedcode $wifi

else

   echo "No usable wireless device was detected"
   exit 1

fi
 
Old 03-14-2010, 10:25 AM   #6
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
Quote:
Originally Posted by leopard View Post
Lastly, to clarify what I meant by that last question. Lets say I have three variables:-
Code:
wlan
rausb
wifi
And when the script runs, only one of these is the 'right' one, and is the one I would like to use. How would I tell BASH to use the only variable with text in it, up to that point?

I was thinking something like this would work?
Code:
if [ "$wlan|$rausb|$wifi | sed 's/  *//g' | grep -q ' *'" -eq 0 ]; then

   echo "$wlan|$rausb|$wifi | sed 's/  *//g'" >/dev/null
   read $interface
   echo "The interface label of your wireless device is $interface"

else

   echo "No usable wireless device was detected"
   exit 1

fi

# Rest of script...
The idea is that I check for all possible interface labels and grab them as variables, then have a block like the one above decipher the 'right' one, and continue on with the rest of the script without fitting it in a function.

Does that clarify my intention a bit? Sorry if I am unclear.
I'm not clear whether only one of the three will ever be present or there may be more than one and the user will choose which one to do things with. Assuming the latter (because it can more easily be converted to address the first than the other way round, a bit adding more sugar when the tea is not sweet enough) ...

First off, your code. Presumably
Code:
if [ "$wlan|$rausb|$wifi | sed 's/  *//g' | grep -q ' *'" -eq 0 ]; then
is intended to determine whether any of the wireless interfaces are present, as indicated by at least one of $wlan, $rausb and $wifi having the "right" content as ultimately determined from grep's exit status.

There's so much wrong with it (sorry!), I don't know where to start and if I did the explanation would be too complex. The essential problems are
  • "$wlan|$rausb|$wifi | sed 's/ *//g' | grep -q ' *'" is simply a string (because of the double quotes) so it cannot be tested with the numeric comparison operator "-eq".
  • bash will put the output of a command (which may be a pipeline) into an expression when $( <command(s)> ) is used.
Moving on to
Code:
   echo "$wlan|$rausb|$wifi | sed 's/  *//g'" >/dev/null
   read $interface
The effect of this is to generate the string "$wlan|$rausb|$wifi | sed 's/ *//g'" and discard it then wait for the user to enter a line (terminated by Enter).

Enough for one post.
 
Old 03-14-2010, 11:05 AM   #7
lupusarcanus
Senior Member
 
Registered: Mar 2009
Location: USA
Distribution: Arch
Posts: 1,022

Original Poster
Blog Entries: 19

Rep: Reputation: 146Reputation: 146
Quote:
Originally Posted by catkin View Post
I'm not clear whether only one of the three will ever be present or there may be more than one and the user will choose which one to do things with. Assuming the latter (because it can more easily be converted to address the first than the other way round, a bit adding more sugar when the tea is not sweet enough) ...

First off, your code. Presumably
Code:
if [ "$wlan|$rausb|$wifi | sed 's/  *//g' | grep -q ' *'" -eq 0 ]; then
is intended to determine whether any of the wireless interfaces are present, as indicated by at least one of $wlan, $rausb and $wifi having the "right" content as ultimately determined from grep's exit status.

There's so much wrong with it (sorry!), I don't know where to start and if I did the explanation would be too complex. The essential problems are
  • "$wlan|$rausb|$wifi | sed 's/ *//g' | grep -q ' *'" is simply a string (because of the double quotes) so it cannot be tested with the numeric comparison operator "-eq".
  • bash will put the output of a command (which may be a pipeline) into an expression when $( <command(s)> ) is used.
Moving on to
Code:
   echo "$wlan|$rausb|$wifi | sed 's/  *//g'" >/dev/null
   read $interface
The effect of this is to generate the string "$wlan|$rausb|$wifi | sed 's/ *//g'" and discard it then wait for the user to enter a line (terminated by Enter).

Enough for one post.
Its OK. I'm a newbie at BASH scripting. I'd expect errors. That's why I came here, to ask the pros.

Thanks for being nice about it, though.
 
Old 03-14-2010, 11:07 AM   #8
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
Here's a bit of script to get an interface to <whatever> with.
Code:
#!/bin/bash

# Load array with names of existing interfaces
i=0
for interface in $( /sbin/ifconfig | grep '^[a-z]' | sed 's/ .*//' )
do
    interfaces[$i]="$interface"
    let i=i+1
done
[[ $i -eq 0 ]] && echo 'No interfaces available to <whatever> with' && exit

# If more than one interface, ask user to choose
if [[ $i -gt 0 ]]; then
    interface=
    PS3='Choose the interface to <whatever> with (Ctrl+C to terminate): '
    select interface in "${interfaces[@]}"
    do
        [[ $interface != '' ]] && break
    done
fi

echo "DEBUG: \$interface is '$interface'"
When developing a script to do unfamiliar tasks, it is very useful to test each command at the command line. For example, when developing the above, I tested these commands in sequence to ensure the output was what I thought it would be.
Code:
/sbin/ifconfig
/sbin/ifconfig | grep '^[a-z]'
/sbin/ifconfig | grep '^[a-z]' | sed 's/ .*//'
Another useful technique is to trace the script to see what it is doing by adding the command set -xv just before the place of interest (set +xv to turn it off).

When that is too verbose and you just need to know what a specific variable holds you can use something like this (the single quotes ensure any leading or trailing whitespace is visible):
Code:
echo "DEBUG: \$var is '$var'"
Another useful technique is to test each new bit of script as it is added; that way, if there is a syntax error, you don't have far to look for it.

Last edited by catkin; 03-14-2010 at 11:10 AM. Reason: Expunged errant dot
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Can I use 'if' statements in Expect script? d3funct Programming 3 02-24-2011 03:59 PM
Bash script is enterpreting $1, $2 values in awk script ... praveen_218 Programming 4 09-14-2009 03:38 PM
Help with BASH script and AWK and SED NickJH Linux - Newbie 11 03-07-2009 04:08 PM
can awk see bash script arguments ? sharapchi Programming 7 12-14-2006 08:03 PM
Bash script question (grep and awk) hamish Linux - Software 6 04-06-2005 03:14 PM

LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie

All times are GMT -5. The time now is 10:33 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration