ed.awk

Alexander Weld

Project link: https://gitlab.com/xicalango/awk-ed/

An ed implementation in AWK??

Some time ago I needed to log into a container based one of those minified docker images to check some logs or debug in production or something strange like that. This activity required looking and/or editing a file. I tried everything: more, less, vim, nano, pico, ed and even emacs. Given the state of minified docker images, all of these attempts were greeted with command not found.

So what else was there left to do than implementing my own editor? And of course I needed to base it on ed, the standard text editor.

Since it was supposed to run on minified docker images, I needed to find something that had a high likelyhood to be widely installed, and was not (ba)sh (because reimplementing ed was already one crazy thing to do, I did't need the additional crazyness of bash).

Ever since I learned about a tetris implementation in AWK I wanted to do a bigger project in AWK and this seemed the right fit for it. Especially since AWK is bound to be installed on most POSIX compliant systems (and, in this case, on the docker container I needed to debug), I went and implemented ed in AWK.

Features

Basic invocation with ./ed.awk will open an empty document. ./ed.awk FILENAME will open the given file.

The following operations are available, following mostly the same semantics as your standard ed implementation:

  • print (p)
  • print with line number (n)
  • insert (i)
  • append (a)
  • write buffer to file (w)
  • write buffer to file with filename (w )
  • deleting lines (d)
  • range addressing (i,j); i, j can be:
    • line numbers
    • . for current line
    • $ for the last line
    • + for next line
    • - for previous line
    • speical character % will be replaced with 1,$
  • quit (q)
  • basic error reporting (printing ? on anything it doesn't understand)

Minified source code

The source code is 1636 bytes in size, so you can just copy and paste it from here:

#!/usr/bin/awk -f
function PE(){print"?"}function LF(){F=ARGV[1];if(length(F)==0){B[0]="";E=-1;T=0}else{i=0;while(getline){B[i]=$0;i++}print i;E=i-1;T=i-1}}function PC(){pc=match(U,"[a-z]");if(pc==0){C="";rs=U}else{C=tolower(substr(U,pc));rs=substr(U,1,pc-1)}n=split(rs,ra,",");if(rs=="%"){S=1;N="$"}else if(length(rs)==0){S=".";N="."}else if(n==0||n==1){S=rs;N=rs}else if(n==2){S=ra[1];N=ra[2]}return 0}function EL(l){if(l=="."){l=T}else if(l=="$"){l=E}else if(l=="+"){l=T+1}else if(l=="-"){l=T-1}else{l--}if(l<0||l>E){return -1}return l}function DL(){D=1;j=S;for(i=N+1;i<=E;i++){B[j]=B[i];j++}E=E-(N-S+1)}function WF(){n=match(C," ");if(n!=0){F=substr(C,n+1)}if(length(F)==0){return -1}if(E==-1){print"">F}else{for(i=0;i<=E;i++){print B[i]>F}}D=0;return 0}function QQ(){if(D){D=0;return -1}else{R=0;return 0}}function SI(){if(C=="i"){P=N}else if(C=="a"){P=N+1}M=1;iT=0;return 0}function FI(){j=0;for(i=0;i<P;i++){t[j++]=B[i]}for(i=0;i<iT;i++){t[j++]=I[i]}for(i=P;i<=E;i++){t[j++]=B[i]}E=E+iT;for(i=0;i<=E;i++){B[i]=t[i]}delete t;D=1;M=0;T=T+iT;return 0}function EC(){S=EL(S);N=EL(N);C1=substr(C,1,1);if(C1=="q"){return QQ()}else if(C1=="w"){return WF()}else if(C1=="i"||C1=="a"){return SI()}if(S==-1||N==-1||S>N){return -1}T=N;if(length(C)==0){print B[T]}else if(C1=="p"){for(i=S;i<=N;i++){print B[i]}}else if(C1=="n"){for(i=S;i<=N;i++){print i+1,B[i]}}else if(C1=="d"){DL()}return 0}BEGIN{LF();R=1;M=0;while(R){s=getline U<"/dev/stdin";if(s==0){print"EOF received. Exiting...";exit}if(M==0){s=PC();if(s==-1){PE();continue}s=EC();if(s==-1){PE();continue}}else if(M==1){if(U=="."){s=FI();if(s==-1){PE();continue}}else{I[iT]=U;iT++}}}}

Or download it: ed.awk

"But how do I write it into a file without having an editor?", you might ask? Well you could for example use bash's heredoc feature:

cat <<EOF > ed.awk
<paste ed.awk>
EOF