[HN Gopher] The Awk book's 60-line version of Make
       ___________________________________________________________________
        
       The Awk book's 60-line version of Make
        
       Author : nalgeon
       Score  : 240 points
       Date   : 2023-09-10 22:36 UTC (23 hours ago)
        
 (HTM) web link (benhoyt.com)
 (TXT) w3m dump (benhoyt.com)
        
       | teo_zero wrote:
       | I know that these programs are only for didactic purposes and my
       | comment may seem nitpicking, but I can't help noticing that the
       | age comparison of the two versions differ semantically: AWK
       | version uses greater-or-equal, but Python uses strictly-less-
       | than. The behavior is different when target and prerequisite have
       | exactly the same age/mtime: AWK will execute the commands, Python
       | won't.
       | 
       | Python's behavior seems wrong to me. It shows up in rules with a
       | phony target and phony prerequisites, which by definition share
       | the same age (9999) and mtime (0). For example, it wouldn't
       | delete prog in the following rule:                 clean: clean-
       | objs         rm prog            clean-objs:         rm *.o
       | 
       | On the other hand, the AWK version has a subtle bug in that it
       | sets to zero the age of a newly updated target: this is not
       | required in the most common cases (because the target will likely
       | be the first file listed by "ls -t" anyway) and makes it
       | incompatible with GNU make in those rare cases when the commands
       | don't actually touch the target. I know they're rare, but just
       | imagine a rule that uses rsync to replace a file with a copy
       | fetched from a remote site only if a newer version exists on that
       | site. If rsync does not download a new version, there's no need
       | to artificially assume that the file was changed, and propagate
       | "upwards" the need to recompile everything that depends on it.
       | 
       | Both bugs are easy to correct, though. That could be left as an
       | exercise for the reader!
        
       | kazinator wrote:
       | Ben notes that Kernighan regrets the way local variables are
       | handled in Awk.
       | 
       | I patched GNU Awk to have a @let extension that gives you scoped
       | locals (usable in functions as well as in BEGIN/END blocks):
       | $ egawk  'BEGIN { x = 3; print x; @let (x = 4, y) { print x }
       | print x }'       3       4       3
       | 
       | @ is used because there is at least one other existing extension
       | which is like that: @include.
       | 
       | https://www.kylheku.com/cgit/egawk/about/
       | 
       | This was rejected by the GNU Awk project, though. I was
       | encouraged to make a fork and give it some kind of different
       | name, so I did that.
        
         | Brian_K_White wrote:
         | Did they cite a reason that sounded reasonable? Like the
         | particular implementation breaks some design principle they
         | want to stick to or something? Did they suggest it might be
         | acceptable some other way or in some other form?
         | 
         | It's curious because gawk cannot for one second claim something
         | like needing to stick to some legacy standard, not with a
         | straight face.
        
           | Joeri wrote:
           | If I were the gawk maintainer I would be unwilling to take on
           | features by default. It is widely used infrastructure and
           | keeping out bugs is far more important than taking on
           | features. If the userbase keeps asking for the same feature
           | over and over, at that point it would be up for
           | consideration, but no sooner.
        
             | vidarh wrote:
             | I think the point is that the list of features added in
             | gawk vs. POSIX awk is miles long... They've not exactly
             | shown restraint.
             | 
             | That said, proving it's value in a fork first seems
             | reasonable.
        
           | im3w1l wrote:
           | Programming languages try very hard to be backward compatible
           | so every feature you add is an eternal commitment.
        
       | jph00 wrote:
       | awk and sed are cool, but whenever someone tells me they're
       | interested in learning them, I always redirect them to learn
       | perl's `-n` and `-p` flags instead, particularly with `-la`
       | added. This gives you, basically, a superset of sed and awk,
       | which makes many things easier to express, often resulting in
       | clearer and more concise code.
       | 
       | For those who have taken this advice, they've always told me
       | later they're really glad they did so, and generally express
       | surprise that this isn't more widely known.
       | 
       | (If you already know awk and sed well, then you mightn't view
       | learning perl in addition worth the effort -- I'm not sure either
       | way. This advice is for people that currently are not strong
       | users of either.)
        
         | mattrighetti wrote:
         | Is perl as widespread as sed and awk in linux distros and other
         | OSes? If I want to make a script that works across the board I
         | feel like the latter are much more adopted, is that correct?
        
           | mbivert wrote:
           | Perl is essentially a standard on UNIX. In particular,
           | there's a Perl interpreter backed with OpenBSD's base
           | installation[0]. If you avoid recent features (you wouldn't
           | be missing too much), then your Perl code should run with
           | little trouble.
           | 
           | However, in my experience, when I start to feel the need to
           | use something more sophisticated than sh/sed/awk, I tend to
           | shy away from Perl in favor of more "robust" languages. Go
           | often is a good-enough substitute (static typing, single-file
           | deployment, trivial cross-compilation); YMMV.
           | 
           | [0]: https://marc.info/?l=openbsd-misc&m=159041121804486&w=2
        
           | asicsp wrote:
           | Perl would be better suited as a portable solution, since you
           | only have to take care of feature differences between Perl
           | versions.
           | 
           | For grep/sed/awk, you also have to worry about implementation
           | differences (GNU/BSD, gawk/mawk/nawk and so on).
        
         | lapsed_lisper wrote:
         | ISTM one could use the same premises to reach the opposite
         | conclusion, namely, that because awk is basically a subset of
         | Perl (excluding CPAN, of course), many things are easier to
         | read, often resulting in more regular, if sometimes longer
         | code. :-)
         | 
         | (FWIW, I learned Perl before sed and awk, and when I was using
         | Perl every day, it was easy enough to whip up one-liners and
         | throwaway scripts. However, I find that as I stopped using Perl
         | on a day-to-day basis about 17 years ago, I can't produce Perl
         | without re-learning the language; but I can produce sed and awk
         | a few times per year without any refresher. I suspect that --
         | for me -- the smallness of each of sed and awk has something to
         | do with it. YMMV, of course.)
        
         | biomcgary wrote:
         | I only write Perl for one-liners anymore, and use -F"\t" all
         | the time (mostly tab-delimited files), but I wasn't familiar
         | with -l to avoid writing chomp. Thank you!
        
         | vidarh wrote:
         | Ruby similarly has -n and -p that does much the same, and Ruby
         | has many of the same `$` vars as Perl/Awk. But even so I often
         | find myself reaching for awk for the simplest stuff because I
         | know it'll be there on pretty much every machine.
         | 
         | Only for 1-2 liners, typically though, the moment something
         | grows beyond that, I don't use Awk any more, so really I only
         | use a tiny sliver of what it can do.
        
       | greyman wrote:
       | I must admit that I use awk only via GPT-4, which will write me
       | the one-liner I need and I just run it. I somewhat cannot
       | remember the syntax, provided I use the tool only occasionally.
        
       | version_five wrote:
       | I'm a big awk fan but I'm not sold on this. The awk program is
       | not very readable- I think that's fine for a dense one-liner, I'm
       | not really sure it carries over to a 60 line script. I think for
       | something like this I'd prefer a bash script, maybe with awk
       | invoked somewhere, that would be much easier to understand at a
       | glance.
       | 
       | Is there something in the awk script that makes it advantageous
       | over a shell script?
       | 
       | Edit: I hadn't read the author's conclusion yet when I posted, he
       | agrees                 I consider AWK amazing, but I think it
       | should remain where it excels: for exploratory data analysis and
       | for one-liner data extraction scripts
        
         | Brian_K_White wrote:
         | A while ago I wrote a program to renumber TRS-80 Model 100
         | BASIC code in awk. Then re-wrote it in bash (pure bash, no
         | sed/grep/cut/tr/awk/bc/etc), and the two are practically
         | identical. That suprised me just how practically identical they
         | wwere in the end.
         | 
         | awk is like a hidden miracle of utility just sitting there
         | unused on every machine since the dawn of time.
         | 
         | Normally if you want something to be ultra portable, you write
         | it in sh or ksh, (though by now, bash would be ok, I mean there
         | is bash for xenix), but to get the most out of ksh or bash, you
         | have to use all the available features and tricks that are
         | powerful and useful but NOT readable. 50% of the logic of a
         | given line of code is not spelled out in the keywords but in
         | arcane brace expansion and word splitting rules.
         | 
         | But every system that might have some version of bash or ksh or
         | plain sh, _always_ has awk too, and even the oldest plain not-
         | gnu awk is a real,  "normal", more or less straighforward
         | explicit programming language compared to bash. Not all that
         | much more pawerful, but more readable and more straightforward
         | to write. Things are done with functions that take parameters
         | and do things to the parameters, not with special syntax that
         | does magic transformations of variables which you then parlay
         | into various uses.
         | 
         | Everyone uses perl/python/ruby/php/whatever when the project
         | goes beyond bash scope, but they all need to be installed and
         | need to be a particular version, and almost always need some
         | library of modules as well, and every python script breaks
         | every other year or on every other new platform. But awk is
         | already there, even on ancient obscure systems that absolutely
         | can not have the current version of python or ruby and all the
         | gems.
         | 
         | I don't use it for current day to day stuff either, there's too
         | many common things today that it has no knowledge of. I don't
         | want to try to do https transactions or parse xml in awk. I'm
         | just saying it's interesting or somehow notable how generically
         | useful awk is pretty much just like bash or python, installed
         | everywhere already, and almost utterly unused.
        
         | camgunz wrote:
         | It seems pretty readable to me, in particular the "update"
         | function parses as JavaScript if you fix the implicit string
         | concatenation (template literals or +) and replace the #
         | comments with //. I'm actually surprised JavaScript is so
         | similar to awk; it feels like a descendant language tbh.
        
         | agateau wrote:
         | I would find it easier to read with more sensible, non-
         | abreviated variable names.
        
         | Spivak wrote:
         | > for exploratory data analysis and for one-liner data
         | extraction scripts
         | 
         | I think both you and the author just don't like AWK if that's
         | the takeaway. What you're describing is literally 1% of the AWK
         | language -- like you don't have to like it, it's weird in many
         | respects but you're treating AWK like it's jq when it's
         | actually closer to like a Perl-Lite/Bash mix. An AWK focused on
         | just those use-cases would look _very_ different.
         | 
         | One of my favorite resources on AWK:
         | https://www.grymoire.com/Unix/Awk.html
        
         | Avshalom wrote:
         | Well I think generally a 60 line program fits in that spot of
         | "write once, read never, start from scratch if it ever turns
         | out to be inadequate"
         | 
         | ... also known as the APL Zone
        
           | yjftsjthsd-h wrote:
           | I'm not dead set against it, but if there was any mistake or
           | bugs I don't know how you'd find them and fix them in that
           | approach
        
             | fuzztester wrote:
             | By checking the correctness of the outputs, which you need
             | to do anyway?
        
               | yjftsjthsd-h wrote:
               | Okay, so the first dev writes 60 lines of indecipherable
               | code, runs some sample invocations, looks at the output,
               | says it looks good. A few months later, someone - maybe
               | the original dev, maybe some other sucker - notices that
               | in some edge case the code misbehaves. Now what?
               | (Obviously, any answer that involves "don't write code
               | with bugs" or "write perfect tests" is a nonstarter)
        
               | HALtheWise wrote:
               | If we're going with the "start from scratch if it ever
               | proves inadequate" philosophy, then the person who
               | notices the misbehavior looks at the original code, sees
               | that it's written in some obscure language, is
               | undecipherable, but also is only 60 lines long, and
               | decides that it will probably be simpler to make a new
               | (short) implementation in their _own_ favorite language
               | that correctly handles both the original use case and
               | their new requirement. The key insight is that given how
               | much easier it is to write fresh code than understand old
               | stuff, they could very well be correct in that guess, and
               | the end result is a single piece of small clean code,
               | rather than a simple core with layers of patches glued on
               | top.
               | 
               | In this _particular_ case, we 're talking about a "make"
               | replacement, so testing the new implementation can be
               | done by simply running "make all" for the project. If it
               | passes, then the new implementation must be identical to
               | the old one in all the ways that _actually matter_ for
               | the project at hand. In all likelihood, for a simple
               | program like this, fixing one bug will also silently fix
               | others because the new architecture is probably better
               | than the old one.
        
               | falcor84 wrote:
               | I actually really like this approach, and have been
               | thinking about this in regards to coding with an LLM -
               | for a sufficiently simple program (and assuming no
               | security concerns), once you trust your test suite, you
               | should trust AI generated code that passes it. And then
               | if requirements change, you should be able to amend the
               | test cases, rerun the AI until it passes all tests and
               | linters, maybe give the code a quick glance, and be on
               | with your life.
        
               | edgyquant wrote:
               | The point is the "and fix them"
        
               | [deleted]
        
               | macintux wrote:
               | Not only is fixing more difficult, but also looking for
               | likely weaknesses (and thus the inputs and outputs to
               | focus on for testing).
        
         | mbivert wrote:
         | I think it should be appreciated in context: it's a good way to
         | teach both awk(1) and make(1) to someone new to UNIX. It also
         | demonstrates how to use awk(1) for prototyping, which IMO is a
         | good programming habit to "develop": it forces to focus on the
         | essential, and not to unnecessarily overthink.
        
         | rottc0dd wrote:
         | Bash would really be bad idea if it is going to use bash
         | stitching so many gnu utils for this kind of job.
         | 
         | I once had to rewrite a bash script into awk[1] that is big
         | enough and it made the program more readable and the total time
         | execution came down from 12 mins to less than 1 second.
         | 
         | I think maybe the original bash script would have written
         | badly, (each util command will invoke it's own process and it
         | has to piped to others instead of using awk which will be
         | running in a single process).
         | 
         | [1] - https://github.com/berry-
         | thawson/diff2html/blob/master/diff2...
        
         | tyingq wrote:
         | > Is there something in the awk script that makes it
         | advantageous over a shell script?
         | 
         | Pseudo multi-dimensional associative arrays for representing
         | the dependency graph of make. This part:                 for (i
         | = 2; i <= NF; i++)           slist[nm, ++scnt[nm]] = $i
         | 
         | The way awk supports them is hacky and not really a
         | multidimensional array, but still is better than what you would
         | have to do with bash, because of split() and some other
         | language features.
         | 
         | It would be much easier with any scripting language though,
         | Perl for example.
        
         | pushedx wrote:
         | Writing this make program in bash would invovle even more
         | difficult to read hacks, as bash also does not support
         | multidimensional arrays.
        
         | Someone wrote:
         | > The awk program is not very readable
         | 
         | What do you find hard to read about it? If you know what _make_
         | does, I think it is fairly easy to read, even for those who
         | don't know awk at all, but do know the Unix shell (to recognize
         | 'ls -t') and C (both of which, probably the audience for this
         | book knew, given that the book is from 1988)
         | 
         | > I think for something like this I'd prefer a bash script
         | 
         | But would it be easier to read? I doubt see why it would.
        
           | mauvehaus wrote:
           | Bash _also_ would have been an unlikely choice for a book
           | published in 1988, considering it wasn 't released until 1989
           | (Per Wikipedia).
        
             | Brian_K_White wrote:
             | It would have been ksh, which was the bash of the day, as
             | in, the more featureful sh-compatible sh-superset.
             | 
             | But a bash or ksh script would have been less readable than
             | awk.
             | 
             | bash (or ksh88 or ksh93) is powrful and useful but not
             | readable if you're actually using the powerful useful
             | features.
             | 
             | In bash, a lot of functionality comes in the form of brace
             | expansions and word splitting, basically abusing the
             | command parser to get results there is no actual function
             | for. In awk and any other more normal programming language,
             | those same features come in the form of an explicit
             | function to do that thing.
        
               | Someone wrote:
               | > It would have been ksh
               | 
               | No, it wouldn't have been _ksh_ or any other shell, nor C
               | or Perl, nor anything else but _awk_ , in a book titled
               | "The AWK Programming Language".
        
               | Brian_K_White wrote:
               | Someone didn't read the thread (or lost the plot), but
               | that didn't stop them from making a non-sensical remark
               | about it.
        
               | fuzztester wrote:
               | >In bash, a lot of functionality comes in the form of
               | brace expansions and word splitting, basically abusing
               | the command parser to get results there is no actual
               | function for. In awk and any other more normal
               | programming language, those same features come in the
               | form of an explicit function to do that thing.
               | 
               | Right. That's one of the reasons why the man page for
               | bash is so long. IIRC, going way back, even the page for
               | plain sh was long, for the same reason.
        
               | falcor84 wrote:
               | Indeed. But at least it acknowledges it, with the iconic
               | "It's too big and too slow."
        
               | fuzztester wrote:
               | Interesting, didn't know. Been a while since I read the
               | page.
        
       | teo_zero wrote:
       | > return 1 to the caller to indicate we did make an update.
       | 
       | This (both in awk and python code) seems useless, as the return
       | value from update() is not used anyhere. Am I missing something
       | obvious?
        
         | benhoyt wrote:
         | Yeah, it's used in the top-level update() call in the BEGIN
         | block:                 if (update(ARGV[1]) == 0)
         | print ARGV[1] " is up to date"
        
           | teo_zero wrote:
           | Right, I missed that one, probably considered it a cosmetic
           | feature!
        
       | csours wrote:
       | Is there a way to get Awk to emit a non-terse version of the
       | script passed in?
       | 
       | ie awk '/test/' -> '{ if($0~/test/){print $0} }'
        
       | whartung wrote:
       | Funny, I would have approached this by removing the while loop
       | and the if else parts of the BEGIN clause, leveraging the stock
       | file reading and line iteration along with AWK pattern matching
       | (terminated with a next statement to skip to the next row), and
       | then shoved the rest in the END clause.
       | 
       | It's always been a "thing" with me of not liking to put
       | everything into BEGIN. Kind of a "if I'm doing that, why am I
       | using awk" thing.
       | 
       | Just how I approach problems with awk.
        
         | eequah9L wrote:
         | I'm with you on this, but is there a way to force input file to
         | read in AWK? That would be a reason to choose the while loop
         | over AWK's implicit iteration. In particular, overriding
         | FILENAME in BEGIN does not do anything.
        
       | 1vuio0pswjnm7 wrote:
       | These blog posts and discussion usually pit one language against
       | others and often attempt to restrict a language to some specific
       | context, ignoring that each user's experience, needs and
       | _preferences_ may be different. A more interesting debate would
       | be language-agnostic, such as writing one-liners versus writing
       | lengthy programs.
       | 
       | In short, the debate might be something like: What does the
       | computer user prefer more: (a) writing one-liners or (b) writing
       | lengthy programs. Not everyone will have the same answer. Knuth
       | might prefer (b). McIllroy might prefer (a).
       | 
       | Assuming one reading this blog post knew nothing about
       | programming languages, it seems to imply Python is not well-
       | suited for one-liners, or at least not comparable to AWK in that
       | context. Perhaps the interpreter startup time might have
       | something to do with the failure to consider Python for one-
       | liners.
        
         | [deleted]
        
         | benhoyt wrote:
         | I don't think Python _is_ very well suited to one-liners, but
         | it 's not due to interpreter startup time (20ms on my machine).
         | Rather, it's due to all the scaffolding needed, which AWK
         | provides implicitly: AWK automatically reads input lines and
         | splits them into fields, automatically initializes variables to
         | the type's default value, and has terser syntax for things like
         | regex matching.
         | 
         | Consider the following AWK one-liner which, for every input
         | line that starts with a letter, prints the line number and the
         | line's second field:                 awk '/^[A-Za-z]/ { print
         | NR, $2 }'
         | 
         | The equivalent Python program has a _ton_ more boilerplate:
         | import statements, explicit input reading and field splitting,
         | and more verbose regex matching:                 import re
         | import fileinput            inp =
         | fileinput.input(encoding='utf-8')       for line in inp:
         | if re.match(r'[A-Za-z]', line):               fields =
         | line.split()               print(inp.lineno(), fields[1])
        
           | fuzztester wrote:
           | >The equivalent Python program has a ton more boilerplate:
           | import statements, explicit input reading and field
           | splitting, and more verbose regex matching:
           | 
           | "awks and pythons"
        
           | 1vuio0pswjnm7 wrote:
           | Everyone has their own personal preferences.
           | 
           | Here is how I would do that task, assuming (a) I had to do it
           | more than once and (b) I could choose any software. On the
           | computer I'm using, the statically-linked, stripped binary is
           | 50k versus a dynamically-linked gawk which is 623k. This
           | solution is faster than AWK, Python, Go, etc. and uses much
           | less CPU and memory. This is quick and dirty, written in a
           | few minutes. I am not a paid programmer. I'm the so-called
           | average user. I'm not compensated for writing programs.
           | 
           | usage: a.out <-- minimal typing
           | 
           | NB. There is a two space indent added to each line. One must
           | remove exactly two spaces from each line or there will be
           | error messages and this will not compile.
           | #!/bin/sh       flex -8Crf <<eof        int fileno (FILE*);
           | int x,y,n=1;       %option noyywrap noinput nounput        %%
           | ^[A-Za-z][^\n]+ {        printf("%d ",n);
           | for(x=0;x<yyleng;x++){if(yytext[x]==32)y++;
           | if(y==1)putc(yytext[x],yyout);        }
           | putchar(10);y=0;        }       \n n++;       .       %%
           | int main(){ yylex();exit(0);}       eof       cc -O3 -std=c89
           | -W -Wall -pedantic -pipe lex.yy.c -static
        
             | Brian_K_White wrote:
             | If you suggested this as a joke, it is hilarious. Well
             | done.
        
           | vidarh wrote:
           | Ruby and Perl has the -n switches to provide that
           | boilerplate. E.g Ruby:                   ruby -nae 'print
           | $.," ",$F[1],"\n" if $_ =~ /^[A-Za-z]/'
           | 
           | -n wraps an implicit "while gets; ... ;end" around the code;
           | "-a" adds an implicit "$F = $_.split" at the start of the
           | loop; "-" takes an expression from the command line; $_
           | contains the result of the `gets`; $. contains the line
           | number of the last line read.
           | 
           | Alternatively:                   ruby -ne
           | '$_.match(/^[A-Za-z]+(.*)/) { puts "#{$.}#{$1}" }'
           | 
           | `match` sets $1, $2 etc to the corresponding capture group,
           | and calls the block if successful.
           | 
           | The scaffolding would be easy to provide w/Python too, but
           | the extra Awk/Perl-isms to make it _convenient_ is another
           | matter (and while I use them occasionally for one-liners, I
           | will get shouty if I find $1 etc. in production code...).
           | 
           | Even the Ruby differences are sufficient extra noise that I
           | still reach for awk for simple stuff like that.
        
           | [deleted]
        
           | make3 wrote:
           | I always thought we should make a short of Python for one
           | liners inspired from awk, where the loop over the lines would
           | be implied.
           | 
           | the line, lineno and fields would be predifined, and I guess
           | re, os, shutil, pathlib and sys are pre imported. maybe the
           | whole stdlib acts as if it's preimported, while only being
           | imported lazyly
           | 
           | here it would be something like
           | 
           | ```
           | 
           | if re.match(r'[A-Za-z]', line): fields = line.split()
           | print(inp.lineno(), fields[1])
           | 
           | ```
           | 
           | so
           | 
           | ```
           | 
           | cat makefile | pyawk 'if re.match(r"[A-Za-z]", line):
           | print(lineno, fields[1])'
           | 
           | ```
           | 
           | I don't see a way out of multiple if statements requiring
           | multiple lines though, otherwise you would have to introduce
           | brackets to Python lol
        
             | cb321 wrote:
             | Often whole program generation in a prog.lang (&
             | ecosystem!) that _you already know_ can substitute for a
             | new prog.lang. Python even has _eval_. You may be
             | interested in:
             | https://github.com/c-blake/bu/blob/main/doc/rp.md
             | 
             | You can actually get pretty far depending upon boundaries
             | with the always implicit command-option language (when
             | launched from the shell language, anyway). For example,
             | Ben's example can be adapted to:                   rp
             | -m^\[A-Za-z\] 'echo nr," ",s[1]'
             | 
             | which is only 5 more characters and only 3 more key downs
             | (less SHIFT-ing) than the space-optimized version of his
             | `awk`. { key downs are, of course, just a start to a deep
             | rabbit hole on HCI ergonometrics ending in heatmaps, finger
             | reach/strain/keyboard layouts, left-right hand switching
             | dynamics, etc., but they seem the most portable idea. }
             | 
             | Nim is not Python - it is actually a bit more concise while
             | also being statically typed and can be compiled to code
             | which runs as fast as the best C/C++ (at more expense than
             | one usually wants for 1-liner interactive iteration, though
             | unless you need to test on very large data). That said, I
             | find it roughly "as easy" to enter `rp` commands as `awk`.
             | 
             | If doing this in Python tickles your fancy, Ben actually
             | has an interesting on these ideas:
             | https://benhoyt.com/writings/prig/ you might also find
             | interesting.
             | 
             | EDIT: and while I was typing in a sibling @networked
             | mentions a bunch more examples, but I think my comment here
             | remains non-redundant. I'm not sure even one of those
             | examples has some simple `-m` for auto-match mode (although
             | many would say a grep pre-filter is enough for this).
        
               | networked wrote:
               | Sorry, I have removed the list of awk replacements for
               | other languages from that comment because I thought it
               | wasn't the right place for it in the thread. I'll just
               | post it here.
               | 
               | - Common Lisp: https://github.com/sharplispers/clawk
               | 
               | - Haskell: https://github.com/gelisam/hawk
               | 
               | - Racket: https://gitlab.com/xgqt/racket-rawk
               | 
               | - Tcl: https://wiki.tcl-
               | lang.org/page/owh+%2D+a+fileless+tclsh (disclosure: the
               | page links to my fork)
               | 
               | One use for an awk replacement is emitting more
               | structured data. I have used my fork of owh a few times
               | to emit JSON after awk-style parsing. I know GNU Awk can
               | generate JSON with https://www.gnu.org/software/gawk/manu
               | al/html_node/gawkextli..., but I haven't tried it.
        
               | cb321 wrote:
               | No problem. It might also bear mentioning that if one is
               | willing to learn more specialized tools, even less key-
               | downing is possible, such as (using
               | https://github.com/c-blake/bu/blob/main/doc/cols.md):
               | grep ^[A-Za-z]|cols 2
               | 
               | You just lose that row number in the original input
               | coordinates feature of Ben's example which could probably
               | be recovered with `grep -n` & `cols -d' :'`, etc., etc.
               | In exchange, you can say `cols 2:5` to get a block of
               | columns trivially. And then, of course, once you have any
               | oft-repeated atom you can save it in a tiny script/etc.
               | 
               | A lot of these choices come down to atom discovery & how
               | willing/facile someone is juggling/remembering
               | syntax/sub-languages. In my experience, willingness
               | tracks facility and both are highly variable
               | distributions over the human population.
        
             | benhoyt wrote:
             | Alec Thomas wrote a script like this called pawk.py
             | (https://github.com/alecthomas/pawk). It reads input
             | automatically, and for each line, defines "n" and "f" to
             | the line number and fields list (among other things). It
             | even supports /regex/ patterns. Even the print is implicit.
             | So the example above would be:                 pawk
             | '/^[A-Za-z]/ (n, f[1])'
             | 
             | By the way, triple backticks don't work on HN. You have to
             | indent by 2 spaces to get a code block.
        
               | make3 wrote:
               | thanks a lot for mentioning pawk, it really looks like
               | what I had in mind
        
             | networked wrote:
             | A sibling comment already mentions PAWK. You can do
             | cat makefile | pyawk 'if re.match(r"[A-Za-z]", line):
             | print(lineno, fields[1])'
             | 
             | in a Python one-liner without PAWK by abusing list
             | comprehensions:                 python -c 'import
             | fileinput, re; [print(re.split(r"\s+", line)[0],
             | fileinput.lineno()) for line in fileinput.input() if
             | re.match(r"[A-Za-z]", line)]' makefile
             | 
             | Edit: Removed a list of other awk replacements to post in a
             | separate comment
             | (https://news.ycombinator.com/item?id=37465164).
        
             | [deleted]
        
       | mtillman wrote:
       | I wan't aware that an updated book was on the way. Pre-ordered it
       | immediately. It's wonderful to see that, even after 40+ years,
       | the people who created a scripting language are still providing
       | new, well-documented features.
        
       ___________________________________________________________________
       (page generated 2023-09-11 22:02 UTC)