While loop...

Use and complete the template provided. The entire template must be completed. If you don't, your post may be deleted!

  1. The problem statement, all variables and given/known data:
    I am trying to read a file that was made using VI that consists of a user login name, full name, phone number and a website. All the fields are seperated by a colon. When I run the script below, my output is,

testme: log_name: notfound
log_name =

The roster file has about 30 lines in it, and I know the path is correct.

  1. Relevant commands, code, scripts, algorithms:

The way I read this is the variable log_name is asigned the input line cut at field #1, first delimiter colon. Then I echo out the contents of log_name. Can anyone tell me why this is not working? Sorry all I had to * out some of the information for security reasons.

  1. The attempts at a solution (include all code and scripts):
#!/bin/sh
while read inputline
do
        log_name = "$(echo $inputline | cut -f1 -d:)"
        echo log_name = $log_name
done < /path to file named roster
exit 0
  1. Complete Name of School (University), City (State), Country, Name of Professor, and Course Number (Link to Course):

umsl, stl missouri, usa, antognoli, cs2750
Note: Without school/professor/course information, you will be banned if you post here! You must complete the entire template (not just parts of it).

When assigning a value to a variable, whitespace surrounding = is not allowed.

Regards,
Alister

Great that took care of the earlier problem, now I have another. My output is as follows:

log_name = $(echo user login name:full name: phone number:web site | cut -f1 -d: )

instead of just the first field only. It does loop through the whole roster. What could I be doing wrong here?

-f1 means you are printing only the first field ( delimited by : )

if you want to loop through the values, then use awk command

or do

 
man read # check for the delimit options

That is correct and is what I stated in my very first post. As stated in the third post it is echoing the whole roster line. I am trying to loop through the whole roster file and trying to have the output of the first field only.

Not to sure as to what you are trying to tell me here,

man read # check for the delimit options

We have not gone over the awk command so that is a mute point.

just noticed.. what is this ?

Are you trying to store the first fields in the variable ?

echo log_name = $log_name
#!/bin/sh
a=""
while read inputline
do
        log_name = "$(echo $inputline | cut -f1 -d:)"
    a=$a" "$log_name
done < /path to file named roster
echo "$a"
exit 0

You have some syntax errors, try this:

#!/bin/sh
while read inputline
do
        log_name=`echo $inputline | cut -f1 -d:`
        echo $log_name
done < roster
exit 0

Or simplify (eliminates the overhead of a pipe and the cut command):

#!/bin/sh
IFS=:      ## Set the input field separator.
while read logname the_rest_we_dont_care_about
do
  echo $logname 
done < roster
exit 0

Actually, that sets the IFS for the rest of the script. Better to set it for this loop only using this syntax:

#!/bin/sh
while IFS=: read logname the_rest
do
  echo $logname
done < roster
exit 0

First off, the "cut" command does extract ONE field (or several fields, but this is advanced stuff you want to postpone until you grasped the basics) from a line consisting of several such fields. You really should read the man page of "cut". (Tip: if you want to extract several fields one at a time why don't you assign several variables one at a time?)

Secondly, you might want to think over the way you construct a while loop. Given, the way you do it works and it doesn't really make a difference in a 10-line-script, but you write this script to learn something so your concern should not only be to make it work but to learn the most possible from the exercise.

Consider this: when you feed data to a while-loop with a redirection at the end the script is becoming hard to read as the loop becomes longer. The following two loops do the same:

cat /some/file | while read line ; do
     ...some processing...
done

while read line ; do
     ...some processing...
done < /some/file

As long as the loop is short there is no problem, but if "...some processing..." consists of 100 lines: then you will start skipping backwards an forwards as you try to read the source code. Writing the input at top of the loop is somewhat easier to understand and therefore to maintain.

A third point: "read" is able to read several values at once - the values are delimited by the "IFS", the "internal field separator". This is the value the shell uses to separate input fields - even command arguments! Per default the IFS is a space character, which is why a line like "command arg1 arg2" will be interpreted as feeding two distinct arguments "arg1" and "arg2" to "command".

Lets do an exmple: Here is a input file, save this to "infile":

a b c
one two three
1 2 3

And here is some code to read this file, save it to a script "read_infile.sh" and execute it:

#! /bin/bash

value1=""
value2=""
value3=""

cat infile | while read value1 value2 value3 ; do
     echo "value1 is: $value1"
     echo "value2 is: $value2"
     echo "value3 is: $value3"
done

That leaves just the problem of changing the IFS to a colon instead of a space. Hint: you can assign it like any variable and example how to do this can be found in this forum, just use the search function.

There is even a fourth point: it pays to analyze what consumes (system) time on running a script. The most time consuming thing is forking new processes. Every time you invoke a command in a script a new process is started and this consumes time. You can read a very enlightening thread where i learned this lesson thanks to cfajohnson and perderabo here.

What does that mean for your script? Well, the line (i have corrected the syntactical error)

log_name="$(echo $inputline | cut -f1 -d:)"

does something which can be done by "parameter expansion" too. Parameter expansion happens completely inside the shell and doesn't need its own process. Have a look at the man page of bash and search for "parameter expansion" to see what i mean.

I hope this helps.

bakunin

I must respectfully disagree. This is a useless use of cat and creates unnecessary overhead of a process and a pipeline. See https://secure.wikimedia.org/wikipedia/en/wiki/Cat_%28Unix%29#Useless_use_of_cat
See also: Partmaps.org

Using redirection as in the second example is more efficient and thus preferred. As a rule, whenever you catch yourself cat'ing only one file that is a red flag that there should be a more efficient way to do what you are trying to do.

Also

log_name="$(echo $inputline | cut -f1 -d:)"

contains a syntax error. The quotes need to be removed otherwise you are just assigning a literal to your variable. This was in the original code.

I believe the intention is to echo the variable's name, the equals sign, and the variable's value. If so, that statement is fine except for the lack of double quotes around $log_name (to protect the results of the parameter expansion from field splitting and pathname expansion).

Regards,
Alister

---------- Post updated at 01:00 PM ---------- Previous update was at 12:28 PM ----------

The UUofC is of no consequence compared to the fact that in many shells the use of a pipe relegates the reading while-loop to a subshell.

bakunin's helpful and informative post neglected to mention that if you do use a pipe, and if the while-loop runs in a subshell, all changes to the execution environment (such as variable assignments and redirections) will never be accessible to the parent shell. This alone could render the approach inconvenient if not infeasible.

If one truly, madly, deeply cares to avoid a redirection at the end of the while-loop, instead of piping cat into a while-loop, it's a better idea to use exec-redirection before the while-loop. Not only is it portable, the redirection just before the while-loop makes it clear from whence the input comes.

exec 4<&0 <inputfile
while read line; do
    ....
    ....
done
exec <&4 4<&-

You are mistaken. Double quoting a command substitution has no effect.

Double quoting only prevents parsing steps which are capable of generating additional fields -- field splitting and pathname expansion/globbing, for example. However, those steps are already bypassed during parameter assignment. So, those double quotes are harmless.

The behavior you describe is what would happen if the command substitution where single-quoted.

Regards,
Alister

Interesting:
Using the original poster's shebang of /bin/sh on our solaris box using the original code and a one line roster file:

#!/bin/sh
while read inputline
do
        log_name="$(echo $inputline | cut -f1 -d:)"
        echo $log_name
done < roster
exit 0

Output shows the quotes do matter:

$ zzz3
$(echo efs:123:123:123 | cut -f1 -d:)
$

Removing them works as expected (although I need to use backquotes instead of the $( ) syntax for the subshell as in our version of /bin/sh the $( ) is not supported):

#!/bin/sh
while read inputline
do
        log_name=`echo $inputline | cut -f1 -d:`
        echo $log_name
done < roster
exit 0

Output:

$ zzz3
efs
$

Using the ksh with what you describe works though:

#!/bin/ksh
while read inputline
do
        log_name="$(echo $inputline | cut -f1 -d:)"
        echo $log_name
done < roster
exit 0

So your mileage may vary also depending on your shell. Interesting discussion!

Sweet, thanks to all who helped. I did not use the back quote. Yes I have to say the conversation was very entertaining and informative as well.

Thanks for the info, gary_w. I should have qualified my earlier post to make it clear that my statements reflected the POSIX standard. As far as POSIX sh is concerned, double-quoting a command substitution (using either $(...) or `...` syntax), whose result is assigned to a variable, has no effect.

/bin/sh on your solaris box is probably an ancient shell that doesn't support the now ubiquitous (and POSIX-standardized) $(...) command substitution syntax. To your shell, that double-quoted string does not contain a command substitution and so it simply assigns the string as is to the variable. If the OP is using solaris, then he/she may be attempting to use unsupported syntax.

If solaris /bin/sh is POSIX-compliant with regard to a double-quoted backtick command substitution, it should give identical results for var=`cmd` and var="`cmd`".

Regards,
Alister

Thanks to all who helped. The conversation has been interesting and informative.

I have to admit that - using almost exclusively AIX, where ksh is the default shell - i have developed my style with ksh and habitually tend to think in ksh. The point you raise is true in all non-ksh shells.

I stand corrected.

bakunin

This is why I keep coming back here. There is always something to learn (or re-learn heh). I can't tell you how much I have learned while participating in threads like this. Thanks to all for sharing your info!

I too usually think in ksh, so its interesting to experiment with what seems obvious, in other shells. The take-away is, always use the she-bang at the top of your scripts to remove all doubt of what shell you are using, regardless of what your login shell may be.

Gary