LinuxQuestions.org
Review your favorite Linux distribution.
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 04-25-2013, 07:15 AM   #1
ThePedge
LQ Newbie
 
Registered: Apr 2013
Posts: 7

Rep: Reputation: Disabled
Using a FOR loop to count


So I have variables which store a binary number and I need to count the number of 1's which are in that number. I figured the best way is to use a for loop.

In all I am trying to convert my netmask to binary, and count the number of 1's and zero's that exist in the binary.

For example
Code:
binary1=$(echo "obase=2; 255" | bc)
count=(0)

for 1 in $binary1;
do
    $count=$(($count+1))
done

echo $count
My error is that is says
Quote:
'1': not a valid identifier
I am very new to bash/linux and I guarantee my syntax is wrong. I've tried looking at various websites for help but I can't figure out how to relate it to my code to fix.
Thank you for reading
 
Old 04-25-2013, 07:39 AM   #2
pan64
LQ Guru
 
Registered: Mar 2012
Location: Hungary
Distribution: debian i686 (solaris)
Posts: 8,104

Rep: Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267
you looks really creative on misinterpreting the syntax. I'm really impressed by the idea, unfortunately it does not work that way:
Quote:
for name [ [ in [ word ... ] ] ; ] do list ; done
The list of words following in is expanded, generating a list of items.
you have no list of words, you have no variable called name....

So you would need to split the result and count single chars one by one
Code:
binary1=$(echo "obase=2; 255" | bc | sed 's/./& /g')
count1=0
count0=0

for a in $binary1;
do
    [[ $a -eq 1 ]] && count1=$(($count1+1))
    [[ $a -eq 0 ]] && count0=$(($count0+1))
done

echo "0:$count0"
echo "1:$count1"
something like this will work

Last edited by pan64; 04-25-2013 at 07:48 AM.
 
1 members found this post helpful.
Old 04-25-2013, 07:57 AM   #3
druuna
LQ Veteran
 
Registered: Sep 2003
Posts: 10,532
Blog Entries: 7

Rep: Reputation: 2387Reputation: 2387Reputation: 2387Reputation: 2387Reputation: 2387Reputation: 2387Reputation: 2387Reputation: 2387Reputation: 2387Reputation: 2387Reputation: 2387
Quote:
Originally Posted by ThePedge View Post
So I have variables which store a binary number and I need to count the number of 1's which are in that number.

In all I am trying to convert my netmask to binary, and count the number of 1's and zero's that exist in the binary.
Have a look at this alternative using bash's parameter expansion:
Code:
#!/bin/bash

binary="$(echo "obase=2; 255" | bc)"

binary0="${binary//[^0]/}" ; count0="${#binary0}"
binary1="${binary//[^1]/}" ; count1="${#binary1}"

echo "Zeros : $count0"
echo "Ones  : $count1"
 
1 members found this post helpful.
Old 04-25-2013, 09:49 AM   #4
ThePedge
LQ Newbie
 
Registered: Apr 2013
Posts: 7

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by druuna View Post
Have a look at this alternative using bash's parameter expansion:
Code:
#!/bin/bash

binary="$(echo "obase=2; 255" | bc)"

binary0="${binary//[^0]/}" ; count0="${#binary0}"
binary1="${binary//[^1]/}" ; count1="${#binary1}"

echo "Zeros : $count0"
echo "Ones  : $count1"
This worked a charm! Can't thank you enough.

@pan64 thank you for your time & input, I tried that way first but couldn't get it working. Anyway, both of your help is very much appreciated

Last edited by ThePedge; 04-25-2013 at 10:19 AM.
 
Old 04-25-2013, 01:42 PM   #5
jpollard
Senior Member
 
Registered: Dec 2012
Location: Washington DC area
Distribution: Fedora, CentOS, Slackware
Posts: 4,600

Rep: Reputation: 1241Reputation: 1241Reputation: 1241Reputation: 1241Reputation: 1241Reputation: 1241Reputation: 1241Reputation: 1241Reputation: 1241
I suggest a simpler solution:

http://stackoverflow.com/questions/1...32-bit-integer

And you can implement that in bash if you want - it has all the operators needed.
 
Old 04-26-2013, 02:14 AM   #6
pan64
LQ Guru
 
Registered: Mar 2012
Location: Hungary
Distribution: debian i686 (solaris)
Posts: 8,104

Rep: Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267Reputation: 2267
Quote:
Originally Posted by jpollard View Post
what a nice discussion! unfortunately it won't help to understand how a simple for cycle work in bash.
 
Old 04-26-2013, 08:45 AM   #7
jpollard
Senior Member
 
Registered: Dec 2012
Location: Washington DC area
Distribution: Fedora, CentOS, Slackware
Posts: 4,600

Rep: Reputation: 1241Reputation: 1241Reputation: 1241Reputation: 1241Reputation: 1241Reputation: 1241Reputation: 1241Reputation: 1241Reputation: 1241
Ah ... a bit of homework (though it may be informal rather formal homework).

There are two forms of the "for". From the bash manpage:
Code:
       for name [ [ in [ word ... ] ] ; ] do list ; done
              The list of words following in is expanded, generating a list of
              items.  The variable name is set to each element of this list in
              turn, and list is executed each time.  If the in word  is  omit‐
              ted,  the  for  command  executes  list once for each positional
              parameter that is set (see PARAMETERS below).  The return status
              is  the  exit  status of the last command that executes.  If the
              expansion of the items following in results in an empty list, no
              commands are executed, and the return status is 0.
The only thing is the list of words. Words are sequence broken up by whitespace (blanks). A simple example of this is a basic command line like "cp a b". The list of words is "cp" "a" and "b". In this simple example, the first word is then used to be the name of an executable image (located using the PATH environment variable) and the rest is used as parameters to that executable.

In the "for" bash command, the "name" variable is set to each element "in" the list, one at a time.

If you actually omit the "in" and the "list" then it will use the list of parameters given to a shell script for the list as in:

Code:
$ cat a.sh
#!/bin/bash
for i ; do
    echo $i
done
$ ./a.sh x y z
x
y
z
$
Now adding the list:
Code:
$ cat a.sh
#!/bin/bash
abc="a b c"
for i in $abc; do
    echo $i
done
$ ./a.sh
a
b
c
$
To see how bash evaluates the script:
Code:
$ cat a.sh
#!/bin/bash -vx
abc="a b c"
for i in $abc; do
    echo $i
done
$ ./a.sh
#!/bin/bash -vx
abc="a b c"
+ abc='a b c'
for i in $abc; do
    echo $i
done
+ for i in '$abc'
+ echo a
a
+ for i in '$abc'
+ echo b
b
+ for i in '$abc'
+ echo c
c
The change is to turn on verbose and tracing (though the tracing is not as useful for loops).
This displays each line of the script as it is read, then the trace of the execution (the line with the +) as bash has interpreted the line, then the result of executing the interpreted line.


When the for command is scanned, the entire loop is handled as a single entity (hence the complete loop is printed "as read".

As the loop is interpreted, it is again displayed (the + on the for line), but it is not an executed line so only the display is done. Each command of the body of the for is then rescanned (which is why the echo line has the $i replaced by the current value), and then the result of the execution.

So to have anything processed requires the data to be separated into "words".

Note also a potential issue - Getting the quoting right with such substitutions is sometimes difficult. This is frequently a problem with shell scripts as the language wasn't designed for really elaborate coding. It does simple things really well, but the complex can become very error prone. Most of the debugging is trying to get substitution done right. Bash attempts to make this simpler using a $(( .... )) syntax to indicate that the "...." is to be interpreted internally, but it is still difficult to get quoting right.

Your simple case is to get spaces between every digit of a binary number. Extra spaces are not a problem (why? because when the line is rescanned for execution the duplicate spaces are discarded).

Note that the "abc" variable gets the data with embedded blanks - note the quotes on the value changed. This is because the " quoting allows substitution within the string. This allows you to have "a b $xyz", and the the result would be 'a b value_of_xyz' as the result:
Code:
$ ./a.sh
#!/bin/bash -vx
xyz="d"
+ xyz=d
abc="a b c $xyz"
+ abc='a b c d'
But consider what happens if your list has to have an element with a space in it (for example, some file names from DOS or other applications sometimes have spaces --"my document.doc" for instance).

To keep the space it needs to be quoted...
Code:
$ ./a.sh
#!/bin/bash -vx
xyz="d e"
+ xyz='d e'
abc="a b c '$xyz'"
+ abc='a b c '\''d e'\'''
echo $abc
+ echo a b c ''\''d' 'e'\'''
a b c 'd e'
better quit... getting off topic. Putting spaces in is what sed can do with ease, but bash has problems with (string manipulation within yet another loop).
 
1 members found this post helpful.
Old 04-29-2013, 09:33 AM   #8
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957
Everything the shell does has one ultimate purpose: to generate simple commands and their corresponding lists of arguments, and then execute them. It all comes down to the shell parsing order and the way it splits the lines up into "tokens".

The word-splitting done on parameter and command expansion is based on your current IFS setting, not whitespace. space+tab+newline is just the default. This happens near the end of the parsing order. Plain text, however, is tokenized in the first stage of command parsing, and that step is always whitespace only.

Notice too how quoting parameters prevents word-splitting:

Code:
$ list1='g:h:i'
$ list2='j:k:l'
$ IFS=:
$ for entry in a b c d:e:f $list1 "$list2"; do
+   echo "[$entry]"
+ done
[a]
[b]
[c]
[d:e:f]
[g]
[h]
[i]
[j:k:l]
Note also that there's another danger involved when using unquoted variables. The globbing expansion step also takes place after variable expansion and word-splitting, so if your text includes any globbing wildcards, you might just find yourself printing out a list of filenames instead!

(You can optionally use "set -f" to temporarily turn globbing off, but quoting is usually clearer and more convenient).

The recommended way to handle spaces, however, is to use an array instead. Then you don't need to rely on shell word-splitting at all. Each element will be treated as an individual token.

Code:
$ entries=( a b c 'd e f' 'g:h:i' )
$ for entry in "${entries[@]}"; do
+    echo "[$entry]"
+ done
[a]
[b]
[c]
[d e f]
[g:h:i]
Indeed, you should never use simple variables to store lists of related things to operate on. If your shell supports arrays, use them!

Be careful to use the '@' array expansion for this, not the '*' one, and to properly quote it, or else the shell will word-split it anyway.

Finally, for simple printing, you can often skip the for loop completely and just use printf, which has implicit looping built into it.

Code:
printf '[%s]\n' "${entries[@]}"
The output should be the same as the above loop.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

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
[SOLVED] Script count files in the directory but keep loop not exit untill see 10 files. dotran Linux - Newbie 9 12-22-2014 05:34 AM
Q: count inside a loop Dr_Death_UAE Linux - General 6 03-04-2013 01:24 AM
Warning: [fnn_insert] Column count doesn't match value count at row 1 in bondoq Programming 2 09-27-2011 05:11 PM
java problem within while loop and count variable.. xskycamefalling Programming 1 09-02-2010 07:41 PM
DBD::mysql::st execute failed: Column count doesn't match value count at row 1 shifter Programming 2 02-24-2010 08:42 PM


All times are GMT -5. The time now is 03:55 PM.

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
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration