Date Category hacks Tags py1

One should use the right tool for the right task. But Learning 300 tools is counterproductive, so one needs a fallback. To be generic enough that fallback must be scriptable. So we have AWK, Perl, Sed, TCL… and their read-only languages.

Enters py1, my new project. It’s a “Python AWK”.

You can use it to write Python one-liners. Just use {{ and }} instead of identation, ; instead of line feed:

py1 "for x in range(4): {{ print x; print x*2 }}"

You promised AWK

In most case you want to start with something, do something for each line, then conclude. py1 provides a template for AWK-like usage.

py1 --begin 'count=0' --each-line "if 'cow' in L: count += 1" --end 'P(count)'

To ease hacking, py1 provides you a set of 1&2-letters variables and functions. In the example above we have L , the current line. We also have P() , an alias for print() . They can be overriden, for examples lines are read from F :

py1 -b 'F=zip(open("file1"), open("file2")' -b 'count=0' -l "if 'cow' in L: count += 1" -e 'P(count)'

Nothing magic, they’re just Python variables.

Sustainable scripting

If you find yourself writing a longer than readable snippet, you can transform it in regular Python code than can easily be refactored for later reuse.

If your 1 liner becomes 500 character long, turn it into a Python module and keep the code flowing!

py1 -b 'count=0' -l "if 'cow' in L: count += 1" -e 'P(count)' --dump-code

Gives you:

## Program conveniency variables.
ENV=os.environ   # ENV: dict of Environment strings
# Input
F = sys.stdin    # F: input File, defaults to stdin
WS = None        # WS: Word Separator, defaults to whitespace
WRE = None       # WS: Word RegExp separator, overrides WS if set
# Output
OF = sys.stdout  # OF: Output File, defaults to stdin
OWS = None       # OWS: Output Word Separator, defaults to whitespace
OLS = None       # OLS: Output Line Separator, defaults to \n

def P(*args, **kwargs):
  """Like print() but honors OWS, OLS & OF."""
  kwargs.setdefault("sep", OWS)
  kwargs.setdefault("end", OLS)
  kwargs.setdefault("file", OF)
  print(*args, **kwargs)

def M(pattern, string=None, flags=0):
  """Returns all capture groups starting with the full match."""
  string = R if string is None else string
  matched =, string, flags)
  if matched is None:
    return None
  return (, ) + matched.groups()

def S(pattern, repl, string=None, count=0, flags=0):
  """Substitute pattern with repl in string (or R if string is None)."""
  string = R if string is None else string
  return re.sub(pattern, repl, string, count, flags)
## --begin
count =0

## Main loop
# LN: Line Number; R: Raw Line
for LN, R in enumerate(F):
  ## Line conveniency variables.
  L = R.strip()           # L: Line without whitespace arround.
  if WRE is None:
    W = L.split(WS)       # W: Words split on WS
    W = re.split(WRE, L)  # W: Words split on WRE
  NW = len(W)             # Number of words

  ## --each-line
  if 'cow'in L :count +=1

## --end
print count

I want it!

pip install py1

The documentation is here, the code on github.


comments powered by Disqus