Variables checking & usage 

*Tags*: empty check, test empty

check set 

${VAR+T} True if VAR is set, even if it is empty

check empty 

${VAR:+T} True if VAR is set and non-null

use if set (use as default) 

${VAR:-default_value} use the value of VAR if VAR is set and non-null,
   otherwise use "default_value"

use different if set (change default) 

${VAR:+value} return "value" if VAR is set and non-null,
   otherwise return VAR

Change a bit if a variable defined 

In short, use the "check set" feature.

I have always want this feature. If a varible is defined, I want to use it, with a little bit of twisting. Else, if it is not defined, leave empty.

${ parameter :+ word }

If parameter is set and is non-null, substitute word; otherwise substitute nothing.

$ echo =$test_var=
==
$ echo =${test_var:-asdf}=
=asdf=
$ echo =${test_var:+asdf}=
==
$ test_var=T
$ echo =${test_var:+asdf}=
=asdf=
$ echo =${test_var:+pre-$test_var.post}=
=pre-T.post=

do if variable not set 

no_log_url=F
[ "x$1" = "x-n" ] && {
  no_log_url=T                # don't log the url
  shift 1
  }
[ $no_log_url = F ] && urllogging='-l'
gnuwget $urllogging -t "$file_title"...

Variable initialisation and sh syntax 

Procmail borrows some sh syntax for variable initialisation. Note that available in a procmail rcfile.

VAR1 = ${VAR2:-value} sets VAR1 to VAR2 if VAR2 is set and
non-null, and sets VAR1 to default "value" otherwise
VAR1 = ${VAR2-value} sets VAR1 to VAR2 if VAR2 is set, and sets
VAR1 to default otherwise
VAR1 = ${VAR2:+value} sets VAR1 to "value" if VAR2 is set and
non-null, and sets VAR1 to VAR2 otherwise.
VAR1 =${VAR2+value} Sets VAR1 to "value" if VAR2 is set and
sets VAR1 to VAR2 otherwise.

And here are the classic usage examples

VAR = ${VAR:-"yes"}     # set VAR to default value "yes"
VAR = ${VAR+"yes"}      # If VAR contains value, set "yes"

Ever wondered if this calls `date` in all cases?

VAR = ${VAR:-`date`}

No, procmail is smart enough to skip calling date if VAR already had value. It doesn't evaluate the whole line. Below you see what each initialising operator does. Study it carefully

VAR = ""                # Define variable
VAR = ${VAR:-"value1"}  # VAR = "value1"
VAR = ""
VAR = ${VAR-"value2"}   # VAR = ""
VAR = ""
VAR = ${VAR:+"value3"}  # VAR = ""
VAR = ""
VAR = ${VAR+"value4"}   # VAR = "value4"
# Note these:
VAR = "val"
VAR = ${VAR:+"value3"}  # VAR = "value3"
VAR = "val"
VAR = ${VAR+"value4"}   # VAR = "value4"
VAR                     # kill the variable
VAR = ${VAR:-"value1"}  # VAR = "value1"
VAR
VAR = ${VAR-"value2"}   # VAR = "value2"
VAR
VAR = ${VAR:+"value3"}  # nothing is assigned
VAR
VAR = ${VAR+"value4"}   # nothing is assigned

default values 

$ DISP=``

— imitate an empty return

$ svs DISP
DISP=
$ echo ${DISP:-'eee'}
eee

— check null

$ echo ${DISP-'eee'}

— check set only

$ unset DISP
$ echo ${DISP-'eee'}
eee

Help 

${parameter:-word}
     If parameter is set and  is  non-null,  substitute  its
     value; otherwise substitute word.
${parameter:=word}
     If parameter is not set or is null set it to word;  the
     value  of  the  parameter  is  substituted.  Positional
     parameters may not be assigned in this way.
${parameter:?word}
     If parameter is set and  is  non-null,  substitute  its
     value;  otherwise,  print word and exit from the shell.
     If word is omitted, the message "parameter null or  not
     set" is printed.
${parameter:+word}
     If parameter is set and is non-null,  substitute  word;
     otherwise substitute nothing.
If the colon (:)  is omitted from the above expressions, the
shell only checks whether parameter is set or not.

internal variables and sub-shell 

$ vtest=1234abcd
$ ( echo $vtest )
1234abcd

— sub-shells invoked from () take internal variables automatically, no need to export them first.

Variable pattern testing 

*Tags*: cmd:case

Sample 0, one line 

 $ case $v in [cC]) echo found;; *) echo not found ;; esac

Sample 1 

# all must be in current directory:
case "$*" in
*/*) echo "nom quitting: I can't handle '/'s." >&2; exit ;;
esac

Sample 2 

case $1 in -[1-9]*)
    n=`expr 1 - "$1"`
    shift
esac

Sample: case sensitives 

Solution 
xsel -p | perl -pe 's/[a-z]/[$&\u$&]/g'
$ echo '*.tar.gz | *.tgz' | perl -pe 's/[a-z]/\[$&\u$&\]/g'
*.[tT][aA][rR].[gG][zZ] | *.[tT][gG][zZ]
Test History 
$ v=c
$ case $v in "c") echo found;; *) echo not found ;; esac
found
$ v=C
$ case $v in "c") echo found;; *) echo not found ;; esac
not found
$ case $v in "[cC]") echo found *) echo not found ;; esac not found
$ case $v in [cC]) echo found *) echo not found ;; esac found
Tip !!
Note No quoting!:
  case "$src" in '/*')          # referring to an abs point

will fail to operate while the following is ok:

  case "$src" in /*)            # referring to an abs point

documented on: 2000.08.15 Tue 16:55:16

variable buildup 

Goal 

Change from

    eval $UNTGZ
to
PROCESS=TGZ
eval $UN$PROCESS

Solution 

eval `echo \\$UN$PROCESS`

or,

eval `eval echo \\$UN$PROCESS`

Trying History 

1<<, >>
PROCESS=TGZ
UNTGZ=ls
$ eval `echo \$UN$PROCESS`
-bash: TGZ: command not found
$ eval `echo \\$UN$PROCESS`
11getlog.lst  cdt          ...
2<<, >>
UNTGZ='tar xzf $ZF'
ZF=../test.org/bar.tar.gz
set -x
$ eval `echo \\$UN$PROCESS`
++ echo '$UNTGZ'
+ eval '$UNTGZ'
++ tar xzf '$ZF'
tar (child): $ZF: Cannot open: No such file or directory
$ eval `eval echo \\$UN$PROCESS`
++ eval echo '$UNTGZ'
+++ echo tar xzf '$ZF'
+ eval tar xzf '$ZF'
++ tar xzf ../test.org/bar.tar.gz

variable interpolate 

2nd level 

0<<, >>
lvstr0='*[?&*]*'
...
lvstr2='*['\''`~<>:#$?&* -]*'
lvstr=\$lvstr2
$ lvstrn=`eval echo $lvstr`
$ svf lvstr
 lvstr='$lvstr2'
 lvstr0='*[?&*]*'
 lvstr2='*['\''`~<>:#$?&* -]*'
 lvstrn='*['\''`~<>:#$?&* -]*'
1<<, >>
a=123
b='The interpolated value for \$a is $a'
$ c=`eval echo $b`; svs c
c=The interpolated value for $a is 123
These are no good:
$ c="$b"; svs c
c=$a
$ eval c="$b"
bash: want: command not found
2<<, >>
$ v=PATH
$ echo \$"$v"
$PATH
$ eval echo \$"$v"
/export/home/tong/bin:/export/home/tong/local/bin:/usr/lib:/shared/local/bin:/usr/local/bin:/usr/shared/bin:/opt/gnu/bin:/bin:/shared/bin:/shared/sbin:/etc:/shared/ucb:/shared/ccs/bin:/shared/openwin/bin:/shared/dt/bin:/export/home/tong/bin/pf:/export/home/tong/bin/tcltk:.

3rd level 

string 

Hist 
b='The interpolated value for \$a$n is $a$n'
c=`eval echo $b`; svs c

— c=The interpolated value for $a1 is 1

command 

b=a
a='ls -l'
$ echo \$"$b"
$a
$ eval \$"$b"
total 256
-rw-r-----   1 tong     tong         8262 Aug 17 17:09 Makefile
[...]

— long list pwd

$ c=`eval echo "b de-refered to \\$b, which is "  \\$"$b" `; svs c
c=b de-refered to a, which is ls -l

These are no good:

$ c=`eval echo 'b de-refered to \\$b, which is  \\$"$b"' `; svs c
c=b de-refered to $b, which is $a
$ c=`eval echo 'b de-refered to \\$b, which is  \\\\$"$b"' `; svs c
c=b de-refered to $b, which is \a
$ c=`eval echo 'b de-refered to \\$b, which is  \\\\\\$"$b"' `; svs c
c=b de-refered to $b, which is \$a

determine a compressed file type by sed 

>I'm trying hard to write a shell script to determine a compressed file
>type.
>
>Ex.
>
>cft, stands for "compressed file type"
>
>cft xxx.tar.gz will give .gz
>cft xxx.tar.Z will give .Z

But your example suggests the first most strongly to me, so:

for f; do echo ".${f##*.}"; done

is a near miss for ksh/bash (it doesn't correctly handle the case of no "." in the name). A more accurate version, which works for any Bourneish shell, is:

for f; do expr "$f" : '.*\(\..*\)'; done
>I know I can do it easily with perl but I hope a simple solution by sed
>will do. Thanks for your help!

If you really want sed:

for f; do echo "$f" | sed 's/.*\././'; done

Ken Pizzini

Ways to test on string length 

> I'm wondering how would you test on the length of a string? Say to
> test whether the variable $fname has the length longer than 2?
>
> I can use two (nested) expr command to do that or, one awk command
> with echo:
>
>   while `echo $fname | awk '{exit !(length() >2)}' `; do
>
> any other ways? thanks!

No external programs needed using ksh:

if [ ${#fname} -le 2 ] ; then
   print "shorter or equals 2"
else
   print "longer than 2"
fi

Ways to test on string length 

> How about bash? :-)

Normally for such simple examples ksh is quite close to bash syntax. It works if you do a s/print/printf/:

$ a=test
$ if [ ${#a} -le 2 ]; then printf "shorter or equals 2"; else printf "longer than 2"; fi
longer than 2

Best regards, Roberto Nibali, ratz

Ways to test on string length 

I only need one "expr" command:

if [ 0`expr "$fname" : '.*'` -gt 2 ] ...

The following part print the string length:

expr "$fname" : '.*'

Heiner

documented on: 2000.12.21 Thu 22:33:46

get first 4 characters of variable 

Newsgroups: comp.unix.shell
> > How can I get the first 4 character of a variable?
>
> MYVAR2=${MYVAR:0:4}

ksh variant:

typeset -L4 newvar=$MYVAR

how to check if $var has an letter B in it? 

Newsgroups: comp.unix.shell
> What's the best way to find out if a string has a certain letter in it?
> So when i assign var=ABCD i would like to check if this var has a
> letter B in it. How to do it ? (i can only think of a messy loop)

bourne shell :

case $var in
*B*)    echo found ;;
*)      echo not found ;;
esac

korn shell (same as the bourne shell + ) :

if [[ $var = *B* ]]; then
        echo found
else
        echo not found
fi

Cyrille.

documented on: 2000.08.28 Mon 21:44:34

pass on arguments exactly as received 

> tst1 a b "c d"
>
> tst2 $*  <-- this would expand above to 4 args instead of 3.
> tst2 "$*" <-- this would expand above to 1 arg instead of 3.
>
> How do I get the original three args?
tst2 "$@"

is equivalent to using

tst2 "$1" "$2" "$3"

In summary, for original example:

 $@     4 args
 $*     4 args
 "$*"    1 arg
 "$@"    3 args!

Beside, when there are no positional parameters, “$@” and $@ expand to nothing (i.e., they are removed). No need to play the trick ${1+"$@"}!

shell scripts 

That "$@" string is sort of magical in the Bourne shell, it makes all the strings come out properly quoted. Not doubt there is some way to accomplish the same thing in csh/tcsh, but I don't know offhand how. Do yourself a favor and use tcsh as your interactive shell if you like, but program in sh/ksh.

documented on: 2000.06.21 Wed 16:30:54

${3+"$3"}<<, >>

>Q:  How is ${3+"$3"} different than just writting $3 ?

If $3 in undefined, you get the same result. If $3 is defined, then the ${3+"$3"} version does not do word splitting or filename expansion on the contents of $3 --- so if $3 contains spaces or shell glob characters like * you will not be surprised. It is different from "$3" in that you do not wind up passing an empty string down to the command in the case that $3 is undefined.

documented on: 1999.09.13 Mon 14:07:39

Passing VARS between Shells 

> Fair enough, but the burning question is HOW?

Here are a few rudimentary examples which should get you started:

program |&   # start program as co-process
read -p inputline   # read line of data from co-process into variable
                    # inputline
print -p "foo bar"   # send data "foo bar" to co-process
anotherprogram <&p >&p # run another program, connecting its
                        # standard input and standard output to co-process
exec 3<&p 4>&p  # connect file descriptor 3 to input from co-process and
                 # file descriptor 4 to output to co-process by default
read -u3 inputline   # read data from co-process, this time through file
                     # descriptor 3 (comparable syntax for print as well)
program2 |&   # now that the prior co-process has been connected to file
              # descriptors, we can start a second co-process without
              # the two interfering with each other

Eric Amick