diff --git a/awk.html.markdown b/awk.html.markdown
index de26c0a1..3d2c4ccb 100644
--- a/awk.html.markdown
+++ b/awk.html.markdown
@@ -6,14 +6,15 @@ contributors:
---
-AWK is a standard tool on every POSIX-compliant UNIX system. It's like a
-stripped-down Perl, perfect for text-processing tasks and other scripting
-needs. It has a C-like syntax, but without semicolons, manual memory
-management, or static typing. It excels at text processing. You can call to it
-from a shell script, or you can use it as a stand-alone scripting language.
+AWK is a standard tool on every POSIX-compliant UNIX system. It's like
+flex/lex, from the command-line, perfect for text-processing tasks and
+other scripting needs. It has a C-like syntax, but without mandatory
+semicolons (although, you should use them anyway, because they are required
+when you're writing one-liners, something AWK excells at), manual memory
+management, or static typing. It excels at text processing. You can call to
+it from a shell script, or you can use it as a stand-alone scripting language.
-Why use AWK instead of Perl? Mostly because AWK is part of UNIX. You can always
-count on it, whereas Perl's future is in question. AWK is also easier to read
+Why use AWK instead of Perl? Readability. AWK is easier to read
than Perl. For simple text-processing scripts, particularly ones that read
files line by line and split on delimiters, AWK is probably the right tool for
the job.
@@ -23,8 +24,23 @@ the job.
# Comments are like this
-# AWK programs consist of a collection of patterns and actions. The most
-# important pattern is called BEGIN. Actions go into brace blocks.
+
+# AWK programs consist of a collection of patterns and actions.
+pattern1 { action; } # just like lex
+pattern2 { action; }
+
+# There is an implied loop and AWK automatically reads and parses each
+# record of each file supplied. Each record is split by the FS delimiter,
+# which defaults to white-space (multiple spaces,tabs count as one)
+# You cann assign FS either on the command line (-F C) or in your BEGIN
+# pattern
+
+# One of the special patterns is BEGIN. The BEGIN pattern is true
+# BEFORE any of the files are read. The END pattern is true after
+# an End-of-file from the last file (or standard-in if no files specified)
+# There is also an output field separator (OFS) that you can assign, which
+# defaults to a single space
+
BEGIN {
# BEGIN will run at the beginning of the program. It's where you put all
@@ -32,114 +48,116 @@ BEGIN {
# have no text files, then think of BEGIN as the main entry point.
# Variables are global. Just set them or use them, no need to declare..
- count = 0
+ count = 0;
# Operators just like in C and friends
- a = count + 1
- b = count - 1
- c = count * 1
- d = count / 1 # integer division
- e = count % 1 # modulus
- f = count ^ 1 # exponentiation
+ a = count + 1;
+ b = count - 1;
+ c = count * 1;
+ d = count / 1; # integer division
+ e = count % 1; # modulus
+ f = count ^ 1; # exponentiation
- a += 1
- b -= 1
- c *= 1
- d /= 1
- e %= 1
- f ^= 1
+ a += 1;
+ b -= 1;
+ c *= 1;
+ d /= 1;
+ e %= 1;
+ f ^= 1;
# Incrementing and decrementing by one
- a++
- b--
+ a++;
+ b--;
# As a prefix operator, it returns the incremented value
- ++a
- --b
+ ++a;
+ --b;
# Notice, also, no punctuation such as semicolons to terminate statements
# Control statements
if (count == 0)
- print "Starting with count of 0"
+ print "Starting with count of 0";
else
- print "Huh?"
+ print "Huh?";
# Or you could use the ternary operator
- print (count == 0) ? "Starting with count of 0" : "Huh?"
+ print (count == 0) ? "Starting with count of 0" : "Huh?";
# Blocks consisting of multiple lines use braces
while (a < 10) {
print "String concatenation is done" " with a series" " of"
- " space-separated strings"
- print a
+ " space-separated strings";
+ print a;
- a++
+ a++;
}
for (i = 0; i < 10; i++)
- print "Good ol' for loop"
+ print "Good ol' for loop";
# As for comparisons, they're the standards:
- a < b # Less than
- a <= b # Less than or equal
- a != b # Not equal
- a == b # Equal
- a > b # Greater than
- a >= b # Greater than or equal
+ # a < b # Less than
+ # a <= b # Less than or equal
+ # a != b # Not equal
+ # a == b # Equal
+ # a > b # Greater than
+ # a >= b # Greater than or equal
# Logical operators as well
- a && b # AND
- a || b # OR
+ # a && b # AND
+ # a || b # OR
# In addition, there's the super useful regular expression match
if ("foo" ~ "^fo+$")
- print "Fooey!"
+ print "Fooey!";
if ("boo" !~ "^fo+$")
- print "Boo!"
+ print "Boo!";
# Arrays
- arr[0] = "foo"
- arr[1] = "bar"
- # Unfortunately, there is no other way to initialize an array. Ya just
- # gotta chug through every value line by line like that.
-
- # You also have associative arrays
- assoc["foo"] = "bar"
- assoc["bar"] = "baz"
+ arr[0] = "foo";
+ arr[1] = "bar";
+
+ # You can also initialize an array with the built-in function split()
+
+ n = split("foo:bar:baz", arr, ":");
+
+ # You also have associative arrays (actually, they're all associative arrays)
+ assoc["foo"] = "bar";
+ assoc["bar"] = "baz";
# And multi-dimensional arrays, with some limitations I won't mention here
- multidim[0,0] = "foo"
- multidim[0,1] = "bar"
- multidim[1,0] = "baz"
- multidim[1,1] = "boo"
+ multidim[0,0] = "foo";
+ multidim[0,1] = "bar";
+ multidim[1,0] = "baz";
+ multidim[1,1] = "boo";
# You can test for array membership
if ("foo" in assoc)
- print "Fooey!"
+ print "Fooey!";
# You can also use the 'in' operator to traverse the keys of an array
for (key in assoc)
- print assoc[key]
+ print assoc[key];
# The command line is in a special array called ARGV
for (argnum in ARGV)
- print ARGV[argnum]
+ print ARGV[argnum];
# You can remove elements of an array
# This is particularly useful to prevent AWK from assuming the arguments
# are files for it to process
- delete ARGV[1]
+ delete ARGV[1];
# The number of command line arguments is in a variable called ARGC
- print ARGC
+ print ARGC;
# AWK has several built-in functions. They fall into three categories. I'll
# demonstrate each of them in their own functions, defined later.
- return_value = arithmetic_functions(a, b, c)
- string_functions()
- io_functions()
+ return_value = arithmetic_functions(a, b, c);
+ string_functions();
+ io_functions();
}
# Here's how you define a function
@@ -159,26 +177,26 @@ function arithmetic_functions(a, b, c, d) {
# Now, to demonstrate the arithmetic functions
# Most AWK implementations have some standard trig functions
- localvar = sin(a)
- localvar = cos(a)
- localvar = atan2(b, a) # arc tangent of b / a
+ localvar = sin(a);
+ localvar = cos(a);
+ localvar = atan2(b, a); # arc tangent of b / a
# And logarithmic stuff
- localvar = exp(a)
- localvar = log(a)
+ localvar = exp(a);
+ localvar = log(a);
# Square root
- localvar = sqrt(a)
+ localvar = sqrt(a);
# Truncate floating point to integer
- localvar = int(5.34) # localvar => 5
+ localvar = int(5.34); # localvar => 5
# Random numbers
- srand() # Supply a seed as an argument. By default, it uses the time of day
- localvar = rand() # Random number between 0 and 1.
+ srand(); # Supply a seed as an argument. By default, it uses the time of day
+ localvar = rand(); # Random number between 0 and 1.
# Here's how to return a value
- return localvar
+ return localvar;
}
function string_functions( localvar, arr) {
@@ -188,61 +206,66 @@ function string_functions( localvar, arr) {
# Search and replace, first instance (sub) or all instances (gsub)
# Both return number of matches replaced
- localvar = "fooooobar"
- sub("fo+", "Meet me at the ", localvar) # localvar => "Meet me at the bar"
- gsub("e+", ".", localvar) # localvar => "m..t m. at th. bar"
+ localvar = "fooooobar";
+ sub("fo+", "Meet me at the ", localvar); # localvar => "Meet me at the bar"
+ gsub("e+", ".", localvar); # localvar => "m..t m. at th. bar"
# Search for a string that matches a regular expression
# index() does the same thing, but doesn't allow a regular expression
- match(localvar, "t") # => 4, since the 't' is the fourth character
+ match(localvar, "t"); # => 4, since the 't' is the fourth character
# Split on a delimiter
- split("foo-bar-baz", arr, "-") # a => ["foo", "bar", "baz"]
+ n = split("foo-bar-baz", arr, "-"); # a[1] = "foo"; a[2] = "bar"; a[3] = "baz"; n = 3
# Other useful stuff
- sprintf("%s %d %d %d", "Testing", 1, 2, 3) # => "Testing 1 2 3"
- substr("foobar", 2, 3) # => "oob"
- substr("foobar", 4) # => "bar"
- length("foo") # => 3
- tolower("FOO") # => "foo"
- toupper("foo") # => "FOO"
+ sprintf("%s %d %d %d", "Testing", 1, 2, 3); # => "Testing 1 2 3"
+ substr("foobar", 2, 3); # => "oob"
+ substr("foobar", 4); # => "bar"
+ length("foo"); # => 3
+ tolower("FOO"); # => "foo"
+ toupper("foo"); # => "FOO"
}
function io_functions( localvar) {
# You've already seen print
- print "Hello world"
+ print "Hello world";
# There's also printf
- printf("%s %d %d %d\n", "Testing", 1, 2, 3)
+ printf("%s %d %d %d\n", "Testing", 1, 2, 3);
# AWK doesn't have file handles, per se. It will automatically open a file
# handle for you when you use something that needs one. The string you used
# for this can be treated as a file handle, for purposes of I/O. This makes
- # it feel sort of like shell scripting:
+ # it feel sort of like shell scripting, but to get the same output, the string
+ # must match exactly, so use a vaiable:
+
+ outfile = "/tmp/foobar.txt";
- print "foobar" >"/tmp/foobar.txt"
+ print "foobar" > outfile;
- # Now the string "/tmp/foobar.txt" is a file handle. You can close it:
- close("/tmp/foobar.txt")
+ # Now the string outfile is a file handle. You can close it:
+ close(outfile);
# Here's how you run something in the shell
- system("echo foobar") # => prints foobar
+ system("echo foobar"); # => prints foobar
# Reads a line from standard input and stores in localvar
- getline localvar
+ getline localvar;
- # Reads a line from a pipe
- "echo foobar" | getline localvar # localvar => "foobar"
- close("echo foobar")
+ # Reads a line from a pipe (again, use a string so you close it properly)
+ cmd = "echo foobar";
+ cmd | getline localvar; # localvar => "foobar"
+ close(cmd);
# Reads a line from a file and stores in localvar
- getline localvar <"/tmp/foobar.txt"
- close("/tmp/foobar.txt")
+ infile = "/tmp/foobar.txt";
+ getline localvar < infile;
+ close(infile);
}
# As I said at the beginning, AWK programs consist of a collection of patterns
-# and actions. You've already seen the all-important BEGIN pattern. Other
+# and actions. You've already seen the BEGIN pattern. Other
# patterns are used only if you're processing lines from files or standard
# input.
#
@@ -257,7 +280,7 @@ function io_functions( localvar) {
# expression, /^fo+bar$/, and will be skipped for any line that fails to
# match it. Let's just print the line:
- print
+ print;
# Whoa, no argument! That's because print has a default argument: $0.
# $0 is the name of the current line being processed. It is created
@@ -268,16 +291,16 @@ function io_functions( localvar) {
# does. And, like the shell, each field can be access with a dollar sign
# This will print the second and fourth fields in the line
- print $2, $4
+ print $2, $4;
# AWK automatically defines many other variables to help you inspect and
# process each line. The most important one is NF
# Prints the number of fields on this line
- print NF
+ print NF;
# Print the last field on this line
- print $NF
+ print $NF;
}
# Every pattern is actually a true/false test. The regular expression in the
@@ -286,7 +309,7 @@ function io_functions( localvar) {
# currently processing. Thus, the complete version of it is this:
$0 ~ /^fo+bar$/ {
- print "Equivalent to the last pattern"
+ print "Equivalent to the last pattern";
}
a > 0 {
@@ -315,10 +338,10 @@ a > 0 {
BEGIN {
# First, ask the user for the name
- print "What name would you like the average age for?"
+ print "What name would you like the average age for?";
# Get a line from standard input, not from files on the command line
- getline name <"/dev/stdin"
+ getline name < "/dev/stdin";
}
# Now, match every line whose first field is the given name
@@ -335,8 +358,8 @@ $1 == name {
# ...etc. There are plenty more, documented in the man page.
# Keep track of a running total and how many lines matched
- sum += $3
- nlines++
+ sum += $3;
+ nlines++;
}
# Another special pattern is called END. It will run after processing all the
@@ -348,7 +371,7 @@ $1 == name {
END {
if (nlines)
- print "The average age for " name " is " sum / nlines
+ print "The average age for " name " is " sum / nlines;
}
```
@@ -357,3 +380,4 @@ Further Reading:
* [Awk tutorial](http://www.grymoire.com/Unix/Awk.html)
* [Awk man page](https://linux.die.net/man/1/awk)
* [The GNU Awk User's Guide](https://www.gnu.org/software/gawk/manual/gawk.html) GNU Awk is found on most Linux systems.
+* [AWK one-liner collection](http://tuxgraphics.org/~guido/scripts/awk-one-liner.html)