Does awk have parameter substitution?

Can I specify a default value to a variable in AWK like BASH in one statement using parameter substitution?

BASH example:

argument=${$1-"default if empty"} (BASH)

I know I can do:

argument=$1; sub ( "^$", "default if empty", argument) (AWK)

Mike

Well you could us the ? operator like this:

argument=length($1) ? $1 : "default if empty"

or like this:

argument=$1~"^$" ? "default if empty" : $1
1 Like

First, you have an extra dollar sign (shown in red above) that shouldn't be there.

Second: your default if empty string is misleading. With the form argument=${parm-word} , word is assigned only if parm is unset. If parm is set, but empty; argument will be set to the empty string.

To get what you probably intended (assign word if parm is set to an empty string or if parm is unset); you want:

argument=${1:-"default if unset or empty"}

If I understand what you're trying to do (assign an awk variable the value of a shell variable if it is set and not empty, the easy way is to still use shell variable assignment:

awk -v argument=${1:-"default if unset or empty"} 'BEGIN{print argument}'

But, you can also do it entirely inside an awk script. Compare what happens with the following script:

#!/bin/ksh
date > now
awk -v v1="v1 -v" '
function setvars() {
	v1 = v1 ? v1 : "v1 unset, empty string, or 0"
	sub("^$", "v2 is unset or empty string", v2)
	printf("fn=%d, FILENAME=\"%s\"\nv1=\"%s\"\nv2=\"%s\"\n\n",
		fn, FILENAME, v1, v2)
}
BEGIN {	setvars()
}
FNR == 1 {
	fn++
	setvars()
	print fn, $0
	print ""
}
END {	print "in END clause"
	setvars()
}' v2='v2 before 1st file operand' now v1= v2= now v1=0 v2=0 now v1=abc \
   v2=def now v1='v1 after last file operand' v2=0

which, with any POSIX conforming shell such as bash or ksh (it has been tested with both) produces the output:

fn=0, FILENAME=""
v1="v1 -v"
v2="v2 is unset or empty string"

fn=1, FILENAME="now"
v1="v1 -v"
v2="v2 before 1st file operand"

1 Thu Apr 30 20:54:59 PDT 2015

fn=2, FILENAME="now"
v1="v1 unset, empty string, or 0"
v2="v2 is unset or empty string"

2 Thu Apr 30 20:54:59 PDT 2015

fn=3, FILENAME="now"
v1="v1 unset, empty string, or 0"
v2="0"

3 Thu Apr 30 20:54:59 PDT 2015

fn=4, FILENAME="now"
v1="abc"
v2="def"

4 Thu Apr 30 20:54:59 PDT 2015

in END clause
fn=4, FILENAME="now"
v1="v1 after last file operand"
v2="0"

If you want to try this on a Solaris/SunOS system, change awk to /usr/xpg4/bin/awk .

1 Like

Thanks for pointing out the extra dollar sign. I made a typo while posting.

I am asking if there is an equivilent in awk to the BASH example (only awk variables involved--no shell variables involved).

Edit: I missed that one of your examples was applicable to my code based on my lack of undertanding of how ? worked. I'm glad to learn.

Mike

I'm sorry my crystal ball didn't work.

Giving an awk expression with no context indicating whether the $1 you showed us in your awk expression is a shell command line variable or a field from an input line makes it hard to guess at what you're trying to do.

If you would show us an example of what you're really trying to do instead of giving us part of one line of an awk script and (making us guess at the context) is likely to get you suggestions that will come closer to doing what you want.

I thought the meaning of $1 was pretty unambiguous in AWK.

Here is a little more context:

 
while (getline < "'"$failListFile"'") { split( $0, a, ","); failMessage[a[1]a[2]a[3]a[4]a[5]]=a[8]
fs=a[6]; sub ( "^$", "0000 01 01 00 00 00", fs ); failStart[a[1]a[2]a[3]a[4]a[5]]=fs
fe=a[7]; sub ( "^$", "9999 12 31 23 59 59", fe ); failEnd[a[1]a[2]a[3]a[4]a[5]]=fe

Trying to do the assignment and default in a single command if possible (possibly analagous to parameter substitution in BASH, if such a thing exists in AWK).

Mike

There is no invocation of awk in this script. There is a reference to a shell variable failListFile (or maybe it is a reference to the current line in awk since the undefined awk variable failListFile with a $ in front of it references the contents of the current input line). There are mismatched braces. There is no description of what you are trying to do with this awk and/or bash script. I have no idea what "assignment and default in a single command" you are trying to perform.

I have shown you (in a working shell script that invokes awk ) how you can assign an awk variable in an awk script based on a variable left uninitialized by the shell, a variable initialized to an empty string by the shell, and a variable initialized to a non-empty string by the shell and use that inside an awk script to set an awk variable to a default value based on a couple of different conditions.

You haven't explained what you want to do that isn't being done by the sample script I showed you. Since I can't figure out what you're trying to do, I am afraid that I am unable to help you any further.

That is because these are but three lines of a 70 line AWK script. Isn't it enough to say code is AWK code or BASH code or Korn code?

indeed there is. I am passing filenames from the Bash wrapper to the AWK script this way.

I disagree.

Those 3 lines are what I am doing (they work just fine). I am just looking for a more elegant solution

You showed me several things including the solution I used in my very first post in this thread.

argument=$1; sub ( "^$", "default if empty", argument) (AWK) 

That's becaue the sample script you showed me works just fine as do the scripts and portions of scripts I posted.

My question was "Does awk have parameter substitution (or something with the same functionality)?" I'm trying my best to explain what I mean by that. I gave a BASH example of parameter substitution and I gave example of AWK code that performs the same function but using more commands.

Let me try again: In BASH you can equate one BASH variable to another BASH variable with a default in a single command.

a=${b:-"default"}

Is there a similar command in AWK (equate one AWK variable to another AWK variable with a default value in a single command)?

Mike

But - hasn't that been shown to you in posts #2 and #3?

awk -vb=XXX 'BEGIN {a=b?b:"Default"; print a}' 
XXX
awk  'BEGIN {a=b?b:"Default"; print a}' 
Default
2 Likes

Post 2, I recognised that it worked (the reason I thanked the post).
Post 3, the second example you reposted is applicable to my script but I failed to understand it.

It appears that I did not appreciate the difference between how AWK ? operates from the BASH conditionals based on std error I am used to.

 
$ echo | awk 'BEGIN { a=b?b:"Default"; print a }' # I expected this result
Default
 
$ echo | awk 'BEGIN { b=""; a=b?b:"Default"; print a }' # I did not expect this result
Default
 
$ echo | awk 'BEGIN { c="something"; a=b?b:"Default"; print a }' # expected
something
 
$ echo | awk 'BEGIN { b="0"; a=b?b:"Default"; print a }' # expected
0
mestora@MESTORA-MOBL1 ~
$ echo | awk 'BEGIN { b=0; a=b?b:"Default"; print a }' # was not sure what to think but this is useful.
Default

Mike

PS. Going back to thank post 3.
PPS. That example works for one part of my sciprt that does include truely null values. In another file I am working with, the column is never truely empty but it consists of double quoted emptyness, in which case I need to stick with the gsub.
PPS. I also was not aware that you could simultaneously do the assignement and test the assignment. I would have thought the required syntax was:

a= a=b? c : "Default"; #which also works

If you would just tell us the constraints under which you want to assign one of a group of values to a variable, we can probably help you construct a single command using one or more ternary expressions to get what you want. But just saying "I want to set a default value like bash does." doesn't really make sense. The awk utility doesn't have the concept of an "unset" scalar variable. (It does for elements of an array variable; but not for scalar variables.) And, in awk the empty string and the string "0" and the number 0 will be treated as identical in many awk expressions. But, if we know explicitly what you are trying to do we can differentiate between unset array elements, an empty string, and a 0 (although you can't differentiate a string 0 from a number 0 in awk ). And, if you reference an array element (other than by using the in operator), you create that array element. For instance, if you split a line using:

n = split($0, a, ",")

and you want to set the variable v to "unset" if array[5] has not been set (i.e., split() returned a value less than 5), "empty" if a[5] is an empty string, "zero" if a[5] compares equal to 0 (such as with "0", "000", or "0.0"), and to the contents of a[5] for anything else you could use:

v = (5 in a) ? a[5] == "" ? "empty" : a[5] == 0 ? "zero" : a[5] : "unset"

The command:

b=""; a=b?b:"Default"

sets a to Default because the ternary operator evaluates the 1st expression ( b in this case) and if that expression evaluates to TRUE returns the 2nd expression; otherwise it returns the 3rd expression. In awk a variable evaluates to TRUE if it is a non-zero string, a non-zero number, or a non-empty string; otherwise, it evaluates to FALSE.

Scalar variables in awk are never "unset" as in a shell variable that has never been set. In awk , every unset scalar variable has the value 0 if it is referenced as a numeric value and has the value empty string if referenced as a string.

That surprises me. c is "something", but neither assigned to a nor printed anywhere. So, the result should be "Default".

As to what pertains to the "double quoted emptyness", awk can't differentiate between empty and unset. But, neither can bash (man bash):

Also from man bash:

So to test for an unset variable, use:

${parameter-word}

Which is also standard for any POSIX-conformant shell..

--
Regarding awk, it can detect set or unset array elements through the relational expression "element in array", where the element does not get created...

With regular variables, it is not so much that awk cannot detect unset variables, but rather - as a design choice - awk works such, that referencing a variable creates it first with an empty value.

1 Like

Should have read on...