I think that Ruby/Python are the spiritual descendants to this argument in 2013, by the way. e.g. A class project was to scrape eBay and predict the winnin bids on a variety of item classes. (Spoiler: "the current bid" outperforms most algorithms.). With AWK, you scrape the HTML and do some ugly parsing. Of course, with Ruby you'd hpricot a single CSS selector and have the lab 80% complete in ~3 lines.
The single greatest disadvantage to using AWK for serious work is that nobody but you and Dr. Loui does that so you get to invent everything for yourself every time. (You probably do not appreciate how much your language ecosystem bakes in code re-use until you've used a language that assumes essentially all use is one-off, by the way.)
Take Python, which is supposedly a "scripting" language, but requires relatively painful amounts of boilerplate to actually read from or write to pipes, etc. It doesn't force you to keep everything in Python, but it certainly nudges you that way. Without naming names, certain statically typed languages that are obsessed with safety are even worse in this regard.
I guess it depends what you mean by 'complex'. Doing basic statistical summaries on a few columns of a TB or so of some logfile in a weird format would be a big project using a lot of the tools I've had to use, but it's more or less 'hello world' for awk. I really like it as part of an "ETL before you ETL" process where you massage your data into a format that makes your official ETL tool with fancy capabilities not choke.
The simplicity of awk also gives it a cultural advantage over things like perl in that you can argue that you're not really introducing another language into your environment, just including some glue logic with a Standardized Tool Everyone Should Know.
The only thing I'm familiar with that does such a good job of keeping simple things simple even when distributed or at largish data volumes is Splunk.
=> 1.upto(100) { |n| puts `curl -s fizzy.heroku.com/#{n}` }
1
2
Fizz
4
Buzz
... from subprocess import call
call(["ls", "-l"])
or import os
os.system("ls -l")Compare
from subprocess import call
call(["ls", "-l"])
to `ls -l` for listing in ls('-l'):
'''etc'''
Here's the library in question - https://pypi.python.org/pypi/pbs - it makes me super happy to do shell work in python.Also, when using Popen, calling .wait() can cause problems if you are expecting large amounts of info back from stdin or stderr. Using .communicate() is generally better.
It's a shame Scheme Shell didn't catch on more. That had a syntax for pipes that simply used the pipe symbol in a way that feels a lot like just using pipes in bash. Unforunately, the project page is down, and the Wikipedia entry doesn't have the best examples.
Sam and vi are both descendants of ed, but go in very different directions. Is there a path forward from awk that focuses on different strengths to perl's choices? (perhaps avoiding perl's move to being multi-paradigm)
Is there something that sits close to unix in the way awk does, but which is stronger?
The editors have had major impact on our languages mainly because regular expressions, called regular sets at the time, where implemented in Thompson's ed which was based on an earlier line editor implementation called qed used on ctss and multics.
One can follow the evolution almost in dialect fairly well.
ed -> grep -> sed -> awk
ed -> em -> ex -> vi
awk -> perl -> ruby
so
ed -> grep -> sed -> awk -> perl -> ruby
The right tool for the right job when all you know is a chainsaw everything looks like a hammer unix process are cheap and all that yada yada philosophical paradigmy finite state automata pipelines vs pointers vs classes vs recursive enumeration iterative parenthesis backtick mind expansions expression logic =P
Never underestimate the power of intellectual inertia, and the lengths people will go to in order to continue using their favorite tool - no matter how poorly suited to a task it might be. ):
from sys import stdin, stdout, stderr
for n,line in enumerate(stdin):
stdout.write(str(n) + '\n')
stderr.write(line)
Is that the kind of reading and writing you are talking about?Compare this to a shell where the combination of &&, ||, and | can fit on a single line what would have taken dozens of lines of python.
For _that_ type of python, I usually use the sh module[0]. It handles piping pretty well, especially if you use StringIO to deal with the pipes. Unfortunately, return code-based conditionals are nowhere as simple as || and && are in shell.
Universal answer: because it's workable, and I'm emotionally invested by now.
It is better to be an expert in a few languages rather than a dilettante in many.
Of course, the best of all possible worlds is to be an expert in many languages, but that often requires time that gets in the way of 'getting stuff done'.
That's the experienced version. I was describing the inexperienced one.
When I encountered AWK I was amazed by associative arrays. It was the first language I've seen where associative array were so accessible. Then there was PHP (I think arrays are one of the things that strongly contribute to its popularity).
Today pretty much every commonly used language has this feature. Often it seems more mimicry that actual appreciation of this data structure. For example when other languages creators bring this structure in they tend to forget about important feature. Ordering. For example python didn't have standard ordered dictionary type for a long time. Also ruby keeps order of the items in hash only since 1.9
Minimise resistance of expressing a translation of a hypothesis from thought into a computing language at all costs: get onto the highway as fast as possible.
First, AI has discovered that brute-force combinatorics, as an approach to generating intelligent behavior, does not often provide the solution ... A language that maximizes what the programmer can attempt rather than one that provides tremendous control over how to attempt it, will be the AI choice in the end.
Okay. And... awk has this quality? What can I do in awk but not in C or a lisp? In what way does programming in awk lead you toward less brute-force solutions than any other language? He doesn't support this in any way at all.
The thing to understand about awk is that it's basically a DSL. It's optimized specifically for crunching data contained in line-oriented text files, and within this niche, it is awesome. Since line-oriented text files are used for just about everything in Unix, awk is an especially useful tool there. But once you stray from awk's niche, things start to get awkward, and the further you go, the tougher it gets.
Perl was written to be an awk-killer, and it didn't accomplish that by being better within awk's niche. It did it by not being a DSL: it's still "good enough" for the sorts of work that awk really excels at, but it works much better for just about everything else.
The fact that Awk is Turing-complete has nothing to do with the fact (if it is a fact) that there's not much you can do in it that you can't do elsewhere.
The SKI combinator calculus is Turing-complete, but you can't read a CSV file with it.
Of course you can, it's just a matter of input handling. For that matter you can read a CSV file with any Turing machine. It's just easier elsewhere.
That said, keep in mind that most likely, you'd want to start implementing levels of abstraction pretty early on. The fact that SKI is Turing-equivalent means that you can implement a Turing machine (or anything else that is Turing-equivalent) in it. Build your favorite abstraction, and then implement your machine and OS the way you would using that abstraction. It's still SKI underneath, so you're golden.
Unlambda, for instance, is Turing complete, and moreover, it can do I/O. An Unlambda program is nevertheless incapable of opening files or doing different things depending on its command-line arguments. You can write cat (the version of cat that just echoes stdin to stdout) in Unlambda, but not ls.
You might be able to write an Unlambda-based operating system in which all the various sorts of input events that an OS needs to respond to are represented as elements in its input stream (or, even better, an OS in Lazy-K).
But when you've got that OS up and running, Unlambda programs running on it still won't be able to open files. (Frankly I'd be surprised if the "abstractions" necessary to get something like that up and running weren't essentially an interpreter written in another language dealing with the encoding and decoding of input and output to your Unlambda/Lazy-K program, rather than abstractions written in Unlambda/Lazy-K. (Consider that the numbers that Lazy-K outputs are church encoded and must be converted by the Lazy-K interpreter into C-like integers before characters can be output to stdout.) This isn't really important, though.)
Consider also this final note from the Lazy-K page:
"Remove output entirely. You still have a Turing-complete language this way, and it is if anything more elegant. But, as with the equally elegant SMETANA, you can't do anything with it except stare at it in admiration, and the novelty of that wears off after a few minutes."
That's not really true, of course: there are other things you can do, like increase the temperature of your processor. Not many other things, though.