[2.0] Awk Syntax

v1.1.5 / chapter 2 of 3 / 01 mar 16 / greg goebel

* This chapter gives a description of the precise syntax of Awk.

[2.6] ARRAYS


* Awk is invoked as follows:

   awk [ -F<ch> ] {pgm} | { -f <pgm_file> } [ <vars> ] [ - | <data_file> ]

-- where:

   ch:          Field-separator character.
   pgm:         Awk command-line program.
   pgm file:    File containing an Awk program.
   vars:        Awk variable initializations.
   data file:   Input data file.

An Awk program has the general form:

   BEGIN              {<initializations>} 
   <search pattern 1> {<program actions>} 
   <search pattern 2> {<program actions>} 
   END                {<final actions>}

If the Awk program is written on the command line, it should be enclosed in single quotes ('{pgm}') instead of double quotes ("{pgm}") to prevent the shell from interpreting characters within the program as special shell characters. Please remember that the Windows COMMAND shell does not allow use of single quotes in this way. Naturally, if such interpretation is desired, double quotes can be used. Those special shell characters in the Awk program that the shell should not interpret should be preceded with a "\".

* This syntax diagram should be easily understood by anyone who has read the first chapter, with a few comments.

First, the data file is optional. If it isn't specified, Awk takes data from standard input, with input terminated by a CTRL-D. However, if a user is initializing variables on the command line, a matter explained below, standard input must be specified by using "-" as a parameter. Multiple data files can also be specified. Awk will scan each in turn and generate a continuous output from the contents of the multiple files.

Second, notice the "-F" option. This allows changing Awk's "field separator" character. As noted in the previous chapter, Awk regards each line of input data as composed of multiple "fields", which are essentially words separated by blank spaces. A blank space (or a tab character) is the default "field separator". In some cases, the input data may be divided by another character, for example, a ":", and it would be nice to be able to tell Awk to use a different field separator. This is what the "-F" option does. To invoke Awk and specify a ":" as the field separator, we write:

   awk -F:  ...

This can also be done by changing one of Awk's built-in variables; again, more on this later.

Third, it is also possible to initialize Awk variables on the command line. This is obviously only useful if the Awk program is stored in a file or is an element in a shell script, as any initial values needed in a script written on the command-line can be written as part of the program text.

Consider the program example in the previous chapter to compute the value of a coin collection. The current prices for silver and gold were embedded in the program, which means that the program would have to be modified every time the price of either metal changed. It would be much simpler to specify the prices when the program is invoked.

The main part of the original program was written as:

   /gold/    { num_gold++; wt_gold += $2 }
   /silver/  { num_silver++; wt_silver += $2 }
   END { val_gold   = 485 * wt_gold
         val_silver = 16 * wt_silver

The prices of gold and silver could be specified by variables, say, "pg" and "ps":

   END { val_gold   = pg * wt_gold
         val_silver = ps * wt_silver

-- and then the program would be invoked with variable initializations in the command line as follows:

   awk -f summary.awk pg=485 ps=16 coins.txt

-- with the same results as before. Notice that the variable initializations are listed as "pg=485" and "ps=16", and not "pg = 485" and "ps = 16"; including spaces is not recommended, since it might confuse command-line parsing.



* The simplest kind of search pattern that can be specified is a simple string, enclosed in forward-slashes ("/"). For example:


-- searches for any line that contains the string "The". This will not match "the" as Awk is "case-sensitive", but it will match words like "There" or "Them".

This is the crudest sort of search pattern. Awk defines special characters or "metacharacters" that can be used to make the search more specific. For example, preceding the string with a "^" tells Awk to search for the string at the beginning of the input line. For example:


-- matches any line that begins with the string "The". Similarly, following the string with a "$" matches any line that ends with "The", for example:


But what if we actually want to search the text for a character like "^" or "$"? Simple, we just precede the character with a backslash ("\"). For example:


-- matches any line with a "$" in it.

* Such a pattern-matching string is known as a "regular expression". There are many different characters that can be used to specify regular expressions. For example, it is possible to specify a set of alternative characters using square brackets ("[]"):


This example matches the strings "The" and "the". A range of characters can also be specified. For example:


-- matches any character from "a" to "z", and:


-- matches any letter or number.

A range of characters can also be excluded, by preceding the range with a "^". For example:


-- matches any letter except "a", "b", and "c", while:


-- matches any line that doesn't start with a letter or digit. This second example is a bit confusing, since the "^" is being used in two different ways.

A "|" allows regular expressions to be logically ORed. For example:


-- matches lines that start with the word "Germany" or the word "Netherlands". Notice how parentheses are used to group the two expressions.

* The "." special characters allows "wildcard" matching, meaning it can be used to specify any arbitrary character. For example:


-- matches "who", "why", and any other string that has the characters "wh" and any following character.

This use of the "." wildcard should be familiar to UNIX shell users, but Awk interprets the "*" wildcard in a subtly different way. In the UNIX shell, the "*" substitutes for a string of arbitrary characters of any length, including zero, while in Awk the "*" simply matches zero or more repetitions of the previous character or expression. For example, "a*" would match "a", "aa", "aaa", and so on. That means that ".*" will match any string of characters.

There are other characters that allow matches against repeated characters expressions. A "?" matches zero or one occurrences of the previous regular expression, while a "+" matches one or more occurrences of the previous regular expression. For example:


-- matches any line that consists only of a (possibly signed) integer number. This is a somewhat confusing example and it is helpful to break it down by parts:

   /^                  Find string at beginning of line.
   /^[-+]?             Specify possible "-" or "+" sign for number.
   /^[-+]?[0-9]+       Specify one or more digits "0" through "9".
   /^[-+]?[0-9]+$/     Specify that the line ends with the number.


* There is more to Awk's string-searching capabilities. The search can be constrained to a single field within the input line. For example:

   $1 ~ /^France$/

-- searches for lines whose first field ("$1" -- more on "field variables" later) is the word "France", while:

   $1 !~ /^Norway$/

-- searches for lines whose first field is not the word "Norway".

It is possible to search for an entire series or "block" of consecutive lines in the text, using one search pattern to match the first line in the block and another search pattern to match the last line in the block. For example:


-- matches a block of text whose first line begins with "Ireland" and whose last line begins with "Summary".

* There is no need for the search pattern to be a regular expression. It can be a wide variety of other expressions as well. For example:

   NR == 10

-- matches line 10. NR is, as explained in the overview, a count of the lines searched by Awk; and "==" is the "equality" operator. Similarly:

   NR == 10,NR == 20

-- matches lines 10 through 20 in the input file. Awk supports search patterns using a full range of comparison operations:

   <          Less than.
   <=         Less than or equal.
   ==         Equal.
   !=         Not equal.
   >=         Greater than or equal to.
   >          Greater than.

For example:

   NF == 0

-- matches all blank lines, or those whose number of fields is zero.

   $1 == "France"

-- is a string comparison that matches any line whose first field is the string "France". The astute reader may notice that this example seems to do the same thing as the previous example:

   $1 ~ /^France$/

In fact, both examples do the same thing, but in the example immediately above the "^" and "$" metacharacters had to be used in the regular expression to specify a match with the entire first field; without them, it would match such strings as "FranceFour", "NewFrance", and so on. The string expression matches only to "France".

* It is also possible to combine several search patterns with the "&&" (AND) and "||" (OR) operators. For example:

   ((NR >= 30) && ($1 == "France")) || ($1 == "Norway")

-- matches any line past the 30th that begins with "France", or any line that begins with "Norway".

* One class of pattern-matching that wasn't listed above is performing a numeric comparison on a field variable. It can be done, of course; for example:

   $1 == 100

-- matches any line whose first field has a numeric value equal to 100. This is a simple thing to do and it will work fine. However, suppose we want to perform:

   $1 < 100

This will generally work fine, but there's a nasty catch to it, which requires some explanation. The catch is that if the first field of the input can be either a number or a text string, this sort of numeric comparison can give crazy results, matching on some text strings that aren't equivalent to a numeric value.

This is because Awk, as mentioned earlier, is a "weakly-typed" language. Its variables can store a number or a string, with Awk performing operations on each appropriately. In the case of the numeric comparison above, if $1 contains a numeric value, Awk will perform a numeric comparison on it, as expected; but if $1 contains a text string, Awk will perform a text comparison between the text string in $1 and the three-letter text string "100". This will work fine for a simple test of equality or inequality, since the numeric and string comparisons will give the same results, but it will give crazy results for a "less than" or "greater than" comparison.

Awk is not broken; it is doing what it is told to do in this case. If this problem comes up, it is possible to add a second test to the comparison to determine if the field contains a numeric value or a text string. This second test has the form:

   (( $1 + 0 ) == $1 )

If $1 contains a numeric value, the left-hand side of this expression will add 0 to it, and Awk will perform a numeric comparison that will always be true.

If $1 contains a text string that doesn't look like a number, for want of anything better to do Awk will interpret its value as 0. That means the left-hand side of the expression will evaluate to zero; since there is a non-numeric text string in $1, Awk will perform a string comparison that will always be false. This leads to a more workable comparison:

   ((( $1 + 0 ) == $1 ) && ( $1 > 100 ))

The same test could be modified to check for a text string instead of a numeric value:

   (( $1 + 0 ) != $1 )

It is worthwhile to remember this trickery for the rare occasions it is needed. Weakly-typed languages are convenient, but they can sometimes bite.

* Incidentally, if there's some uncertainty as to how Awk is handling a particular sort of data, it is simple to run tests to find out for sure. For example, suppose we want to see if a version of Awk could handle a hexadecimal value as would be specified in C -- for example, "0xA8". We could simply type in the following at the command prompt:

   awk 'BEGIN {tv="0xA8"; print tv,tv+0}'

If it prints "0xA8 0", that means Awk thinks that the data is strictly a string. This little example consists only of a BEGIN clause, allowing an Awk program to be run without specifying an input file, which is convenient when playing with examples. If uncertain about what Awk may be doing, just ask it; nothing's going to break.



* Numbers can be expressed in Awk as either decimal integers or floating-point quantities. For example:

   789   3.141592654   +67   +4.6E3   -34   -2.1e-2

There is no provision for specifying values in other bases, such as hex or octal, though, as will be shown later, it is possible to output them from Awk in hex or octal format.

Strings are expressed in double-quotes. For example:

   "All work and no play makes Jack a homicidal maniac!"
   "do re mi fa so la ti do"

Awk also supports null strings, which are represented by empty quotes: "".

There are various "special" characters that can be embedded into strings:

   \n     Newline (line feed).
   \t     Horizontal tab.
   \b     Backspace.
   \r     Carriage return.
   \f     Form feed.

A double-quote (") can be embedded in a string by preceding it with a "\", and a "\" can be embedded in a string by typing it in twice: "\\". If a backslash is used with other characters (say, "\m"), it is simply treated as a normal character.

It is possible in the C programming language to specify a character by its three-digit octal code, preceded by a "\", but this is not possible in Awk.



* As already mentioned, Awk supports both user-defined variables and its own predefined variables. Any string beginning with a letter, defined as consisting of alphanumeric characters or underscores ("_"), and which does not conflict with Awk's reserved words can be used as a variable name. Beware that using a reserved word is a common bug when building Awk programs, so if a program blows up on a seemingly inoffensive word, try changing it to something more unusual and see if the problem goes away.

There is no need to declare variables, and in fact it can't be done, though it is a good idea in an elaborate Awk program to initialize variables in the BEGIN clause to make them obvious and to make sure they have proper initial values. Relying on default values is a bad habit in any programming language. The fact that variables aren't declared in Awk can also lead to some odd bugs, for example by mis-spelling the name of a variable and not realizing that this has created a second, different variable that is out of the loop in the rest of the program.

Once again, Awk is weakly typed. Variables have no data type, and can be used to store either string or numeric values; string operations on variables will give a string result and numeric operations will give a numeric result, with a text string that doesn't look like a number simply being regarded as 0 in a numeric operation. Awk will follow its own rules in this issue and so it is important for the programmer to remember it and avoid possible traps. For example:

   var = 1776

-- is the same as:

   var = "1776"

-- both loading the value 1776 into the variable "var". This can be treated as a numeric value in calculations in either case, and string operations can be performed on it as well. If "var" is loaded up with a text string of the form:

   var = "somestring"

-- string operations can be performed on it, but it will evaluate to a 0 in numeric operations. If this example is changed as follows:

   var = somestring

-- this will always return 0 for both string and numeric operations -- because Awk thinks "somestring" without quotes is the name of an uninitialized variable. Incidentally, an uninitialized variable can be tested for a value of 0:

   var == 0

This tests "true" if "var" hasn't been initialized; but, oddly, an attempt to "print" an uninitialized variable gives nothing. For example:

   print var

-- simply prints a blank line, while:

   var = 0; print var

-- prints a "0".

* Unlike many other languages, an Awk string variable is not represented as one-dimensional array of characters. However, it is possible to use the "substr()" function, more later, to access characters or substrings of a string.

* Awk's built-in variables include the field variables -- $1, $2, $3, and so on ($0 is the entire line) -- that give the text or values in the individual text fields in a line, and a number of variables with specific functions:

By the way, values can also be loaded into field variables; they aren't read-only. For example:

   $2 = "NewText"

-- changes the second text field in the input line to "NewText".


[2.6] ARRAYS

* Awk also permits the use of arrays. The naming convention is the same as it is for variables, and, as with variables, the array does not have to be declared. Awk arrays can only have one dimension; the first index is 1. Array elements are identified by an index, contained in square brackets. For example:

   some_array[1], some_array[2], some_array[3] ...

One interesting feature of Awk arrays is that the indexes can also be strings, which allows them to be used as a sort of "associative memory". For example, an array could be used to tally the money owed by a set of debtors, as follows:

   debts["Kim"], debts["Roberto"], debts["Vic"] ...


* Awk's relational operations ("<" "<=" "==" "!=" ">=" ">") have already been discussed. Note that, unlike some languages, relational expressions in Awk do not return a value. They only evaluate to a true condition or a false condition. That means that an Awk program like:

   BEGIN {a=1; print (a==1)}

-- doesn't print anything at all, and trying to use relational expressions as part of an arithmetic expression causes an error.

Awk uses the standard four arithmetic functions:

   +   addition
   -   subtraction
   *   multiplication
   /   division 

All computations are performed in floating-point. There is also a modulo-division ("remainder") operator:

   %   mod 

For example, "13 % 8" yields 5, "20 % 6" yields 2, "3 % 5" yields 3, and so on.

There are increment and decrement operators:

   ++  Increment.
   --  Decrement.

The position of these operators with respect to the variable they operate on is important. If "++" precedes a variable, that variable is incremented before it is used in some other operation. For example:

   BEGIN {x=3; print ++x} 

-- prints: 4. If "++" follows a variable, that variable is incremented after it is used in some other operation. For example:

   BEGIN {x=3; print x++}

-- prints: 3. Similar remarks apply to "--". Of course, if the variable being incremented or decremented is not part of some other operation at that time, it makes no difference where the operator is placed.

Awk also allows the following shorthand operations for modifying the value of a variable:

   x += 2   -- is the same as:  x = x + 2
   x -= 2   -- is the same as:  x = x - 2
   x *= 2   -- is the same as:  x = x * 2
   x /= 2   -- is the same as:  x = x / 2
   x %= 2   -- is the same as:  x = x % 2 

* There is only one unique string operation: concatenation. Two strings can be easily concatenated by placing them consecutively on the same line. For example:

   BEGIN {string = "Super" "power"; print string}

-- prints:



* Awk includes a number of predefined functions. The simplest function is "length()", which returns the length of its parameter. If no parameter is specified, it returns the length of the input line in number of characters. For example:

   {print length, $0}

-- prints each input line, preceded by its length. When provided with a string parameter, "length()", obviously, returns the length of the string. When provided with an arithmetic parameter, "length()" returns the length of the numeric string that "print" would have printed by default, as defined by default output format, if given the same arithmetic parameter.

* There are several predefined arithmetic functions:

   sqrt()     Square root.
   log()      Base-e log.
   exp()      Power of e.
   int()      Integer part of argument.

The "exp()" function can be used to derive powers of numbers besides e. Given that "^" is an exponentiation operator:


-- then if:

   2 = e^k

-- where "k" is the log to the base e of 2:

   k = log(2)

-- then:

   2^x = (e^k)^x = (e^log(2))^x = e^(x * log(2))

So Awk could compute the 20th power of 2 with:

   BEGIN {log_two = log(2); print exp(log_two * 20)}

Sine and cosine are also supported by some versions of Awk.

* Awk, not surprisingly, includes a set of string-processing operations:

   substr()   As mentioned, extracts a substring from a string.
   split()    Splits a string into its elements and stores them in an array.
   index()    Finds the starting point of a substring within a string.

The "substr()" function has the syntax:

   substr(<string>,<start of substring>,<max length of substring>)

For example, to extract and print the word "get" from "unforgettable":

   BEGIN {print substr("unforgettable",6,3)}

Please be aware that the first character of the string is numbered "1", not "0". To extract a substring of at most ten characters, starting from position 6 of the first field variable, we use:


The "split()" function has the syntax:

   split(<string>,<array>,[<field separator>])

This function takes a string with n fields and stores the fields into array[1], array[2], ... , array[n]. If the optional field separator is not specified, the value of FS (normally "white space", the space and tab characters) is used. For example, suppose we have a field of the form:


We could use "split()" to break it up and print the names as follows:

   my_string = "joe:frank:harry:bill:bob:sil";
   print names[1];
   print names[2];

The "index()" function has the syntax:

   index(<target string>,<search string>)

-- and returns the position at which the search string begins in the target string (remember, the initial position is "1"). For example:

   index("gorbachev","bach")         returns:  4
   index("superficial","super")      returns:  1
   index("sunfire","fireball")       returns:  0
   index("aardvark","z")             returns:  0


* Awk supports control structures similar to those used in C, including:

   if ... else

The syntax of "if ... else" is:

   if (<condition>) <action 1> [else <action 2>]

The "else" clause is optional. The "condition" can be any expression discussed in the section on pattern matching, including matches with regular expressions. For example, consider the following Awk program:

   {if ($1=="green") print "GO";
    else if ($1=="yellow") print "SLOW DOWN";
    else if ($1=="red") print "STOP";
    else print "SAY WHAT?";}

By the way, for test purposes this program can be invoked as:

   echo "red" | awk -f pgm.txt

-- where "pgm.txt" is a text file containing the program.

The "action" clauses can consist of multiple statements, contained by curly brackets ("{}").

The syntax for "while" is:

   while (<condition>) <action>

The "action" is performed as long the "condition" tests true, and the "condition" is tested before each iteration. The conditions are the same as for the "if ... else" construct. For example, since by default an Awk variable has a value of 0, the following Awk program could print the numbers from 1 to 20:

   BEGIN {while(++x<=20) print x}

* The "for" loop is more flexible. It has the syntax:

   for (<initial action>;<condition>;<end-of-loop action>) <action>

For example, the following "for" loop prints the numbers 10 through 20 in increments of 2:

   BEGIN {for (i=10; i<=20; i+=2) print i}

This is equivalent to:

   while (i<=20) {
      print i;

The C programming language has a similar "for" construct, with the interesting feature that multiple actions can be taken in both the initialization and end-of-loop actions, simply by separating the actions with a comma. Most implementations of Awk, unfortunately, do not support this feature.

The "for" loop has an alternate syntax, used when scanning through an array:

   for (<variable> in <array>) <action>

Given the example used earlier:

   my_string = "joe:frank:harry:bill:bob:sil";
   split(my_string, names, ":");

-- then the names could be printed with the following statement:

   for (idx in names) print idx, names[idx];

This yields:

   2 frank
   3 harry
   4 bill
   5 bob
   6 sil
   1 joe

Notice that the names are not printed in the proper order. One of the characteristics of this type of "for" loop is that the array is not scanned in a predictable sequence.

* Awk defines four unconditional control statements: "break", "continue", "next", and "exit". "Break" and "continue" are strictly associated with the "while" and "for" loops:

"Next" and "exit" control Awk's input scanning:



* The simplest output statement is the by-now familiar "print" statement. There's not too much to it:

* The "printf()" (formatted print) function is much more flexible, and trickier. It has the syntax:

   printf(<string>,<expression list>)

The "string" can be a normal string of characters:

   printf("Hi, there!")

This prints "Hi, there!" to the display, just like "print" would, with one slight difference: the cursor remains at the end of the text, instead of skipping to the next line, as it would with "print". A "newline" code ("\n") has to be added to force "printf()" to skip to the next line:

   printf("Hi, there!\n")

So far, "printf()" looks like a step backward from "print", and for do dumb things like this, it is. However, "printf()" is useful when precise control over the appearance of the output is required. The trick is that the string can contain format, or "conversion", codes to control the results of the expressions in the expression list. For example, the following program:

   BEGIN {x = 35; printf("x = %d decimal, %x hex, %o octal.\n",x,x,x)}

-- prints:

   x = 35 decimal, 23 hex, 43 octal.

The format codes in this example include: "%d" (specifying decimal output), "%x" (specifying hexadecimal output), and "%o" (specifying octal output). The "printf()" function substitutes the three variables in the expression list for these format codes on output.

* The format codes are highly flexible and their use can be a bit confusing. The "d" format code prints a number in decimal format. The output is an integer, even if the number is a real, like 3.14159. Trying to print a string with this format code results in a "0" output. For example:

   x = 35;     printf("x = %d\n",x)       yields:  x = 35
   x = 3.1415; printf("x = %d\n",x)       yields:  x = 3
   x = "TEST"; printf("x = %d\n",x)       yields:  x = 0

* The "o" format code prints a number in octal format. Other than that, this format code behaves exactly as does the "%d" format specifier. For example:

   x = 255; printf("x = %o\n",x)          yields:  x = 377

* The "x" format code prints a number in hexadecimal format. Other than that, this format code behaves exactly as does the "%d" format specifier. For example:

   x = 197; printf("x = %x\n",x)          yields:  x = c5

* The "c" format code prints a character, given its numeric code. For example, the following statement outputs all the printable characters:

   BEGIN {for (ch=32; ch<128; ch++) printf("%c   %c\n",ch,ch+128)}

* The "s" format code prints a string. For example:

   x = "jive"; printf("string = %s\n",x)  yields:  string = jive

* The "e" format code prints a number in exponential format, in the default format:


For example:

   x = 3.1415; printf("x = %e\n",x)       yields:  x = 3.141500e+000

* The "f" format code prints a number in floating-point format, in the default format:


For example:

   x = 3.1415; printf("x = %f\n",x)       yields:  f = 3.141500

* The "g" format code prints a number in exponential or floating-point format, whichever is shortest.

* A numeric string may be inserted between the "%" and the format code to specify greater control over the output format. For example:


This works as follows:

For example, consider the output of a string:

   x = "Baryshnikov"
   printf("[%3s]\n",x)          yields:       [Baryshnikov]
   printf("[%16s]\n",x)         yields:       [     Baryshnikov]
   printf("[%-16s]\n",x)        yields:       [Baryshnikov     ]
   printf("[%.3s]\n",x)         yields:       [Bar]
   printf("[%16.3s]\n",x)       yields:       [             Bar]
   printf("[%-16.3s]\n",x)      yields:       [Bar             ]
   printf("[%016s]\n",x)        yields:       [00000Baryshnikov]
   printf("[%-016s]\n",x)       yields:       [Baryshnikov     ]

-- or an integer:

   x = 312
   printf("[%2d]\n",x)          yields:       [312]
   printf("[%8d]\n",x)          yields:       [     312]
   printf("[%-8d]\n",x)         yields:       [312     ]
   printf("[%.1d]\n",x)         yields:       [312]
   printf("[%08d]\n",x)         yields:       [00000312]
   printf("[%-08d]\n",x)        yields:       [312     ]

-- or a floating-point number:

   x = 251.67309
   printf("[%2f]\n",x)          yields:       [251.67309]
   printf("[%16f]\n",x)         yields:       [      251.67309]
   printf("[%-16f]\n",x)        yields:       [251.67309      ]
   printf("[%.3f]\n",x)         yields:       [251.673]
   printf("[%16.3f]\n",x)       yields:       [        251.673]
   printf("[%016.3f]\n",x)      yields:       [00000000251.673]


* While "sprintf()" is a string function, it was not discussed with the other string functions, since its syntax is virtually identical to that of "printf()". In fact, "sprintf()" acts in exactly the same way as "printf()", except that "sprintf()" assigns its output to a variable, not standard output. For example:

   BEGIN {var = sprintf("[%8.3f]",3.141592654); print var}

-- yields:

   [   3.142]


* The output-redirection operator ">" can be used in Awk output statements. For example:

   print 3 > "tfile"

-- creates a file named "tfile" containing the number "3". If "tfile" already exists, its contents are overwritten. The "append" redirection operator (">>") can be used in exactly the same way. For example:

   print 4 >> "tfile"

-- tacks the number "4" to the end of "tfile". If "tfile" doesn't exist, it is created and the number "4" is appended to it.

Output redirection can be used with "printf" as well. For example:

   BEGIN {for (x=1; x<=50; ++x) {printf("%3d\n",x) >> "tfile"}}

-- dumps the numbers from 1 to 50 into "tfile".

* The output can also be "piped" into another utility with the "|" ("pipe") operator. As a trivial example, we could pipe output to the "tr" ("translate") utility to convert it to upper-case:

   print "This is a test!" | "tr [a-z] [A-Z]"

This yields: