How Unix programmers at restaurants search menus for their favorite plate

Miguel Mota on December 13, 2018

A Unix programmer heads over to the local diner to get something to eat for lunch. He, or Bob as he prefers, knows better than to manually scan the... [Read Full]
markdown guide

With real life menus with millions of lines, you usually start with head | grep, and then, when you one-liner of 15 lines is complete, you replace head with cat and cross your fingers


You can almost accomplish this entire task using awk. For example:

awk -F '$' '/shrimp/ {printf "%s- %.2f\n", $1, $NF}' < menu.txt


Grep won't be able to find anything because you are searching for ! Which is not in the menu


I've been reading the thread about this on /r/programming (how circular!) and they're mostly (deliberately?) missing the point and trying to optimise it rather than to see it as a demonstration of pipes.


grep -q 'shrimp.*$[0-9]\.' menu.txt && echo "Available" || echo ":("

EDIT: changed \d shortcode for [0-9] so it'll work with BSD and GNU grep.


That's what this bit at the end is for:


That bit of the regex will only match on a line that contains a dollar sign ($), a single digit ([0-9]), and a literal dot (\.). So "$2.99" will match, but "$12.99" won't, because there are two digits between the dollar sign and the dot.


The equivalent is much more readable with PowerShell:

$shrimp = Get-Content menu.txt | Select-String 'shrimp'
$price = $shrimp | ForEach-Object { $cols = $_ -split ' '; $cols[-1] }
$under10 = [decimal]$price.Substring(1) -lt 10
if ( $under10 ) { 'Available!' } else { ':(' }

Of course, you can use aliases to make this a bit "shorter", but then we're back in the world of Unix, where precious bytes have to be saved to achieve acceptable performance on 300 baud Teletype terminals:

$p = gc menu.txt | sls 'shrimp' | % { $c = $_ -split ' '; $c[-1] }
if ([decimal]$p.Substring(1) -lt 10) {'Available!'} else {':('}


Given this menu:

ps1$cat menu.txt

the menu

steak burrito $11.95
pasta and shrimp $9.75
caesar salad $6.50
shrimp salad $9.99
alfredo shrimp $10

My Nix programmer does this:

awk -F$ '/shrimp/{if ($NF < 10){ print $1}}' menu.txt

pasta and shrimp
shrimp salad

awk rules!


if you are looking for meals under 10 bucks, as shown in ($NF < 10), why isn't caesar salad($6.50) in the output ?


Why are you guys alwas using awk for field separation? There ist cut -d" " -f3 that is nice as well


"test returns an exit status code of 1 if the condition passes or 0 if no match." It is actually backwards:


My favourite pattern for (metaphorically) selecting dishes from a menu and printing their price is to use perl regex matching with capturing groups:

perl -ne 'print "$1\n" if /shrimp.*?\$([\d\.]+)/' menu.txt

This is why two unix programmers will never eat together, because they will start to discuss which is the best way to order the shrimps


Bob should've rounded up. He probably ran off when he remembered he didn't factor taxes in!
Oh & btw, he should've done a case insensitive grep grep -i shrimp menu.txt. Although if he's using awk anyway, then he might have included that there too and avoid all those unnecessary pipes! awk '$0~/shrimp/{print $NF}


You need to replace your double quotes with literal single quotes. Here's why.

$ echo "Available!"
bash: !": event not found

$ echo 'Available!'


This article is awesome. It really shows all the possibilities in Linux. I am quite surprised by the comments here where people are trying to correct you. Anyways, good job


It took me a while to realize that the price is truncated to an integer only because 'test' can't handle decimals. That might deserve a mention.


Or, he could merely 'read' visually the menu??? Didn't realize this was a lesson in Unix coding until the end... :-) Laugh at me...


I don't like how there's no code for how to scrape the website into menu. txt. Incomplete solution! ;-)

code of conduct - report abuse