-   Solaris / OpenSolaris (
-   -   print a variable out side of while read line in solaris (

nagendrar 03-12-2013 01:09 AM

print a variable out side of while read line in solaris
I write following code to update all packages in "total" variable and i will use this variable later. But i am not getting "total" value out side of loop. Please help me to solve this problem

name=`pkginfo -i`
echo "$name" | while read LINE
n=`echo $LINE|awk '{print $2}'`
echo $n
total="$total $n"

echo $total


jlliagre 03-12-2013 11:18 AM

Use the korn shell.

The bourne shell (and bash for that matter) do not preserve the "total" variable set in your pipeline.



David the H. 03-12-2013 01:06 PM

Please use ***[code][/code]*** tags around your code and data, to preserve the original formatting and to improve readability. Do not use quote tags, bolding, colors, "start/end" lines, or other creative techniques.

As stated, bash and most other sh-style shells run piped commands in subshells. This means any environment settings made in them are lost when the loop terminates. ksh, however, runs the final entry in the pipe chain in the main shell, and so this does work properly in it.

I set variables in a loop that's in a pipeline. Why do they disappear after the loop terminates? Or, why can't I pipe data to read?

Note that bash has recently introduced a lastpipe shell option that does the same thing, but it conflicts with job management and so isn't enabled by default. It's also not recommended for interactive use because of this.

For all sh-based shells (including ksh), you can usually use some form of redirection for input instead.


while read line; do
    echo "$line"
done < <( command )

The above uses bash's process substitution feature ( ksh also has P.S. but I believe it functions slightly differently, so be sure to check the documentation). You could instead use a here document/here string with a command substitution, or a named pipe, or an external tempfile, or any other similar kind of stdin feed.

Now to give you some other script pointers (links are for bash, but the general advice is applicable to all shells).

1) When you use "!#/bin/sh" as the shebang, your script will be interpreted according to POSIX compatibility, and the use of shell-specific features becomes questionable at best. This is usually not what you want, so always be sure to explicitly state the interpreter you want to use (e.g. /bin/bash or /bin/ksh ).

2) $(..) is highly recommended over `..`. Please try to avoid using backticks. This is posix-defined and fully portable for all but the oldest shells.

3) Lists of things should never be stored in a single scalar variable, except in the case of limited, POSIX-based shells. Always use arrays if the shell supports them, or process the input directly. I assume that "pkginfo -i" produces such a list.

If the shell doesn't support arrays then you have to be much more careful in how you handle such things. See the next point for why:

4) QUOTE ALL OF YOUR VARIABLE EXPANSIONS. You should never leave the quotes off a parameter expansion unless you explicitly want the resulting string to be word-split by the shell and possible globbing patterns expanded. This is a vitally important concept in scripting, so train yourself to do it correctly now. You can learn about the exceptions later.

(You're actually mostly ok here. You just missed one set, in front of the awk command.)

5) A small point: since environment variables are generally all upper-case, it's recommended practice to keep your own user variables in lower-case or mixed-case to help differentiate them.

6) You don't need awk to split a line if you know how to use read properly. Speaking of which, always use the "-r" option, to keep backslashes in the input from being interpreted.



while read -r _ line _; do

    echo "$line"
    total="$total $line"

done  < <( pkginfo -i )

echo "$total"

Note that the above just creates a simple list of field 2's. Is that what you want? Again, I'd suggest an array instead:



while read -r _ line _; do

    echo "$line"
    total+=( "$line" )

done  < <( pkginfo -i )

echo "${total[*]}"

How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?

How can I use array variables?

string manipulations in bash. Most of them are portable to ksh.

jlliagre 03-12-2013 03:05 PM

Thanks David the H. for your detailed and insightful reply.

Just a small comment about this statement:

Originally Posted by David the H. (Post 4910156)
1) When you use "!#/bin/sh" as the shebang, your script will be interpreted according to POSIX compatibility, and the use of shell-specific features becomes questionable at best.

While correct with Gnu/Linux and most Unix implementations, this feature is not mandated by POSIX and doesn't apply to Solaris up to version 10 with which /bin/sh is not a POSIX compliant shell but the original Bourne shell.

POSIX actually recommend not to use any shebang at all for a compatible script to run in a POSIX environment. Under Solaris, this environment is met by having /usr/xpg4/bin first in the PATH variable.

All times are GMT -5. The time now is 04:26 AM.