runawk(1)

πŸ¦‹ runawk(1) β€” wrapper for AWK interpreter πŸ¦‹

okay bestie, grab your iced coffee because this is the runawk(1) manual and it is lowkey a love letter to AWK. πŸ’–

✨ NAME ✨

runawk β€” wrapper for AWK interpreter.

✨ SYNOPSIS ✨

runawk [options] program_file

runawk -e program

Two moods, same icon. 🫢

πŸ’… MOTIVATION πŸ’…

After years of using AWK for programming, I’ve found that despite its simplicity and limitations, AWK is good enough for scripting a wide range of different tasks. AWK is not as powerful as its bigger counterparts like Perl, Ruby, TCL and others, but it has its own advantages like compactness, simplicity, and availability on almost all UNIX-like systems. I personally also like its data-driven nature and token orientation β€” very useful techniques for text processing utilities.

Unfortunately, awk interpreters lack some important features and sometimes do not work as good as they could do.

Problems I see (some of them, of course):

  1. AWK lacks support for modules. Even if I create small programs, I often want to use functions created earlier and already used in other scripts. That is, it would be great to organise functions into so-called libraries (modules). 🧩
  2. The -- thing is giving me a headache. In order to pass arguments to a #!/usr/bin/awk -f script (not to the awk interpreter), it is necessary to prepend a list of arguments with -- (two minus signs). In my view, this looks badly. Also such behaviour violates POSIX/SUS Utility Syntax Guidelines.

Example:

awk_program:

    #!/usr/bin/awk -f

    BEGIN {
       for (i=1; i < ARGC; ++i){
          printf "ARGV [%d]=%s\n", i, ARGV [i]
       }
    }

Shell session:

% awk_program --opt1 --opt2
/usr/bin/awk: unknown option --opt1 ignored

/usr/bin/awk: unknown option --opt2 ignored

% awk_program -- --opt1 --opt2
ARGV [1]=--opt1
ARGV [2]=--opt2
%

In my opinion awk_program script should work like this:

% awk_program --opt1 --opt2
ARGV [1]=--opt1
ARGV [2]=--opt2
%
  1. stdin needs a whole extra argument. When a #!/usr/bin/awk -f script handles arguments (options) and wants to read from stdin, it is necessary to add /dev/stdin (or -) as a last argument explicitly.

Example:

awk_program:

    #!/usr/bin/awk -f

    BEGIN {
       if (ARGV [1] == "--flag"){
          flag = 1
          ARGV [1] = "" # to not read file named "--flag"
       }
    }

    {
       print "flag=" flag " $0=" $0
    }

Shell session:

% echo test | awk_program -- --flag
% echo test | awk_program -- --flag /dev/stdin
flag=1 $0=test
%

Ideally awk_program should work like this:

% echo test | awk_program --flag
flag=1 $0=test
%
  1. igawk(1) cannot be used in a shebang. igawk(1) which is shipped with GNU awk can not be used in shebang. On most (all?) UNIXes scripts beginning with #!/usr/local/bin/igawk -f will not work.

runawk was created to solve all these problems. periodt. πŸ‘‘

πŸŽ€ OPTIONS πŸŽ€

okay so the options, bestie β€” one bullet each, no skipping:

  • -d β€” Turn on a debugging mode. πŸ›
  • -e program β€” Specify program. If -e is not specified, the AWK code is read from program_file. πŸ“„
  • -f awk_module β€” Activate awk_module. This works the same way as the #use "awk_module.awk" directive in the code. Multiple -f options are allowed. 🧩
  • -F fs β€” Set the input field separator FS to the regular expression fs. βœ‚οΈ
  • -h β€” Display help information. πŸ†˜
  • -t β€” If this option is applied, a temporary directory is created by runawk and path to it is passed to awk child process. Temporary directory is created under ${RUNAWK_TMPDIR} (if it is set), or ${TMPDIR} (if it is set) or /tmp directory otherwise. If #use "tmpfile.awk" is detected in a program this option is activated automatically. πŸ—‚οΈ
  • -T β€” Set FS to TAB character. This is equivalent to -F'\t'. 🫧
  • -V β€” Display version information. 🏷️
  • -v var=val β€” Assign the value val to the variable var before execution of the program begins. πŸͺ„

πŸ¦‹ DETAILS/INTERNALS πŸ¦‹

🌈 Standalone script 🌈

Under UNIX-like OS-es you can use runawk by beginning your script with

#!/usr/local/bin/runawk

line or something like this instead of

#!/usr/bin/awk -f

or similar.

🧩 AWK modules 🧩

In order to activate modules you should add them into awk script like this:

#use "module1.awk"
#use "module2.awk"

that is the line that specifies the module name is treated as a comment line by normal AWK interpreter but is processed by runawk especially.

Unless you run runawk with option -e, #use must begin with column 0, that is no spaces or tabs symbols are allowed before it and no symbols are allowed between # and use.

Also note that AWK modules can also β€œuse” another modules and so forth. All them are collected in a depth-first order and each one is added to the list of awk interpreter arguments prepanded with -f option.

Caveat bestie: #use directive is NOT similar to #include in C programming language. runawk’s module code is not inserted into the place of #use. Runawk’s modules are closer to Perl’s use command.

In case some module is mentioned more than once, only one -f will be added for it, i.e duplications are removed automatically.

Position of #use directive in a source file does matter, i.e. the earlier module is mentioned, the earlier -f will be generated for it.

Example:

file prog:
   #!/usr/local/bin/runawk

   #use "A.awk"
   #use "B.awk"
   #use "E.awk"

   PROG code
   ...

file B.awk:
   #use "A.awk"
   #use "C.awk"
   B code
   ...

file C.awk:
   #use "A.awk"
   #use "D.awk"

   C code
   ...

A.awk and D.awk don't contain #use directive

If you run

runawk prog file1 file2

or

/path/to/prog file1 file2

the following command

awk -f A.awk -f D.awk -f C.awk -f B.awk -f E.awk -f prog -- file1 file2

will actually run.

You can check this by running

runawk -d prog file1 file2

πŸ”Ž Module search strategy πŸ”Ž

Modules are first searched in a directory where main program (or module in which #use directive is specified) is placed. If it is not found there, then AWKPATH environment variable is checked. AWKPATH keeps a colon separated list of search directories. Finally, module is searched in system runawk modules directory, by default PREFIX/share/runawk but this can be changed at compile time.

An absolute path to the module can also be specified.

πŸ’» Program as an argument πŸ’»

Like some other interpreters runawk can obtain the script from a command line like this:

/path/to/runawk -e '
#use "alt_assert.awk"

{
  assert($1 >= 0 && $1 <= 10, "Bad value: " $1)

  # your code below
  ...
}'

runawk can also be used for writing oneliners:

runawk -f abs.awk -e 'BEGIN {print abs(-1)}'

πŸŽ›οΈ Selecting a preferred AWK interpreter πŸŽ›οΈ

For some reason you may prefer one AWK interpreter or another. The reason may be efficiency for a particular task, useful but not standard extensions or anything else. To tell runawk what AWK interpreter to use, one can use #interp directive:

file prog:
   #!/usr/local/bin/runawk

   #use "A.awk"
   #use "B.awk"

   #interp "/usr/pkg/bin/nbawk"

   # your code here
   ...

Note that #interp directive should also begin with column 0, no spaces are allowed before it and between # and interp.

Sometimes it also makes sense to give users ability to select their preferred AWK interpreter without changing the source code. In runawk it is possible using special directive #interp-var which sets an environment variable name assignable by user that specifies an AWK interpreter. For example, the following script

file foobar:
   #!/usr/bin/env runawk

   #interp-var "FOOBAR_AWK"

   BEGIN {
      print "This is a FooBar application"
   }

can be run as

env FOOBAR_AWK=mawk foobar

or just

foobar

In the former case mawk will be used as AWK interpreter, in the latter β€” the default AWK interpreter.

πŸ›Ÿ Using existing modules only πŸ›Ÿ

In UNIX world it is common practise to write configuration files in a programming language of the application. That is, if application is written in Bourne shell, configuration files for such application are often written in Bourne as well. Using RunAWK one can do the same for applications written in AWK. For example, the following code will use ~/.foobarrc file if it exists otherwise /etc/foobar.conf will be used if it exists.

file foobar:
  #!/usr/bin/env runawk

  #safe-use "~/.foobarrc" "/etc/foobar.conf"

  BEGIN {
    print foo, bar, baz
  }

file ~/.foobarrc:
  BEGIN {
    foo = "foo10"
    bar = "bar20"
    baz = 123
  }

Of course, #safe-use directive may be used for other purposes as well. #safe-use directive accepts as much modules as you want, but at most one can be included using awk option -f, others are silently ignored, also note that modules are analysed from left to right. Leading tilde in the module name is replaced with user’s home directory. Another example:

file foobar:
  #!/usr/bin/env runawk

  #use "/usr/share/foobar/default.conf"
  #safe-use "~/.foobarrc" "/etc/foobar.conf"

  your code is here

Here the default settings are set in /usr/share/foobar/default.conf, and configuration files (if any) are used for overriding them.

🌱 Setting environment 🌱

In some cases you may want to run AWK interpreter with a specific environment. For example, your script may be oriented to process ASCII text only. In this case you can run AWK with LC_CTYPE=C environment and use regexp ranges.

runawk provides #env directive for this. String inside double quotes is passed to putenv(3) libc function.

Example:

file prog:
   #!/usr/local/bin/runawk

   #env "LC_ALL=C"

   $1 ~ /^[A-Z]+$/ { # A-Z is valid if LC_CTYPE=C
       print $1
   }

πŸ’€ EXIT STATUS πŸ’€

If AWK interpreter exits normally, runawk exits with its exit status. If AWK interpreter was killed by signal, runawk exits with exit status 128+signal.

🌍 ENVIRONMENT 🌍

  • AWKPATH β€” Colon separated list of directories where awk modules are searched.
  • RUNAWK_AWKPROG β€” Sets the path to the AWK interpreter, used by default, i.e. this variable overrides the compile-time default. Note that #interp directive overrides this.
  • RUNAWK_KEEPTMP β€” If set, temporary files are not deleted.

πŸ‘‘ AUTHOR πŸ‘‘

Copyright (c) 2007-2014 Aleksey Cheusov <vle@gmx.net>


πŸ› BUGS/FEEDBACK β€” talk to me bestie 🫢

Okay so if you find a bug, or you're like "hmm this is weird," or you just have thoughts β€” please, please tell me. No cap, feedback is the whole vibe here.

  • Slide into my inbox by e-mail, or
  • register your comment / question / bug report at the sourceforge project home ✨

And like β€” feature requests are also welcomed πŸ‘‘ fr fr, if you're sitting there thinking "omg runawk would be iconic if it also did X," say it. Bestie energy only, nobody's getting judged for asking.

It's giving "we grow together" πŸ’… β€” every comment, question and report is genuinely wanted.

🏠 HOME β€” the main character's address 🌈

The project's home base, bookmark it, obsess over it:

<http://sourceforge.net/projects/runawk/>

πŸ‘€ SEE ALSO β€” go read these too πŸ’–

  • awk(1) β€” yeah the O.G., the blueprint, the parent. Go give it some respect ✨

πŸ’œ and that's the whole page, bestie πŸ’œ

You made it to the end πŸŽ€ β€” this was runawk(1) 2024-09-06, and it's the source/version + date right there, cute little footer, slay. Now go write some awk that actually works πŸ¦‹πŸ’…

πŸ“¦ where this page comes from