Ten Tweetable Scripts

Yesterday morning I proposed a contest to create the best one-line program that would fit inside Twitter’s 140-character buffer. To kick things off, I wrote this 105-character script which displays a small animation:

s="-<";while true;do echo -ne "$s\r";s=`sed 's/->$/-<-/;s/^/;s/-<-/;s/>-/->/;'<<<$s`;sleep 0.1;done

Arturo (or Pupi as his friends call him) wrote a 135-character morse code decoder in shell:

m=etianmsurwdkgohvf?l?pjbxcyzq;p=0;while read -sn1 c;do [ -z "$c" ]&&p=0&&echo&&continue;let p+=c;echo -ne \\b${m:$p:1};let p+=p+2;done

Press ‘0′ for dot, ‘1′ for dash, and hit space (or enter) as a char separator. Wow!

I learned a few tricks from Arturo’s script. First, he uses the ${} braces operator to take substrings, like so:

${var:offset:length}

This is incredibly useful! You can actually do shell arithmetic in the offset and length parameters, too. So for example,

${var:i+1:a-3}

is valid for shell variables $i and $a. And to find the length of a string, you can use:

${#str}

So str=”foobar”; echo ${#str} will print “6″. You can read more about the braces operator in the bash info page.

Another thing I learned from Arturo’s script is the versatility of the ‘read’ builtin in bash. Pupi uses the -s argument, which causes read not to echo its input (useful for inputting passwords) and -n1 which tells it to only read one character. Also, Arturo uses [ test ] && operation, which is a handy short-hand for an if statement in shell (and other languages).

Pãdraig Brady wrote this excellent screensaver:

tr -c "[:digit:]" " " < /dev/urandom | dd cbs=$COLUMNS conv=lcase,unblock | GREP_COLOR="1;32" grep --color "[^ ]"

Pãdraig makes use of the square-brace character class operator in tr(1) to filter out all the numerals, which bash also supports.

Building on what I learned from Pupi, here is one I wrote that I call paint.sh:

c=12322123;x=20;y=20;while read -sn1 p;do k=${c:(p-1)*2:2};let x+=$((k/10-2));let y+=$((k%10-2));echo -en \\033[$y\;"$x"HX;done

Use the 1 2 3 and 4 keys to move the cursor around the screen. It's an etch-a-sketch for your terminal!

You can see that I made use of the read -sn1 trick from pupi as well as the braces operator to substring. I also used ANSI escape codes to position the cursor.

And this is one I call rockband.sh:

while read -sn1 p;do s="";for((i=0;i<$p;i++));do s=x$s;done; yes $s > /dev/audio&sleep 0.1;kill %%;done

Use the number keys to play different tones. When you're done, hit Control-c.

The way it works is that the ASCII value of each character you send to /dev/audio specifies the excursion of the speaker diaphragm (roughly). The 'yes' command prints whatever string you give it, followed by a newline character (ASCII 13, pretty low), over and over again. So the longer the string of 'x' characters you pass to 'yes', and which 'yes' prints between newlines, the slower the oscillation of the speaker diaphragm, and the lower the tone. Neat, huh? I learned this trick from my boyhood friend Edward Loper many years ago.

And here's the last one I wrote:

s=" #55755071317011117011117075557";for i in `seq 2 $((${#s}-1))`; do k=${s:i:1}; for b in 1 2 4; do echo -n "${s:(k&b)/b:1}"; done; echo; done

Miguel submitted this tiny function plotter:

for x in `seq -1 .05 1`; do y=`echo "s($x*8)*10+10" | bc -l`; for p in `seq 0 $y`; do echo -n " "; done; echo "*" ;done

And here's another plot:

for x in `seq -5 .5 5`; do y=`echo "$x*$x" | bc`; for p in `seq 0 $y`; do echo -n " "; done; echo "*" ;done

Those last three scripts make use of the venerable "seq" command to generate a series of numbers. Miguel uses fractional steps, but if you only need integers you can also use braces in shell, like this:

sum=0;for i in {1..100}; do let sum+=i; done; echo $sum

Ryan Paul of ArsTechnica fame wrote this Ruby script:

proc{|f|f[proc{|x|x+1},0]}[proc{|x,y|proc{|f,z|x[proc{|w|y[f,w]},z]}} [proc{|f,x|f[f[f[f[f[f[f[x]]]]]]]},proc{|f,x|f[f[f[f[f[f[x]]]]]]}]]

Ryan is using the “proc” primitive in Ruby, which allows you to create an anonymous function (like lambda in lisp), and which I didn’t know about even though I’ve been coding Ruby off and on the last few months. He uses Church encoding to encode the numbers 7 and 6, and lambda calculus to multiply them, thus confirming that he is the most awesome IT journalist working today.

Finally, Jay Wren sent in this C program:

main(x,y){for(;x++;) for(y=2;x%y;)printf( ++y/x+"\0%d\n",x);}

of which he is not the original author (and which I suspect was an IOCCC entry), but which is a very compact way of generating all the prime numbers. The author uses the args to main to save space on variable declaration, and the leading null-terminator in the string is a really clever way to select whether or not to print the output without an if statement. Lots of cleverness in there (though the algorithm to find primes is just brute force).

There were too many good entries to declare a winner, and maybe a contest was the wrong idea anyway. But this was a lot of fun. If you want to send me a script on twitter, be sure to send a “@natfriedman” message after, so that I notice you.

Thanks to Fahim Zahid for help creating the mini screencasts.

Update: Be sure to read More Tweetable Scripts for more goodies!

Posted on 11 April 2008

34 comments

  1. Jason Brownlee’s avatar

    Amazing!

    Reminds me of the 6 word stories printed in Wired in 2006 titled: Very Short Stories.

    Reply

  2. Steve Krenzel’s avatar

    Awesome post Nat.

    Jason,
    Funny you linked to that wired article, I had read it a while ago and thought it’d be cool to have a site for people to write their own six word stories, comment on others, rate them, etc… so I wrote SixIsEnough. Check it out, it just went live a week or two ago.

    Reply

  3. root’s avatar

    > tr -c “[:digit:]” ” ” … grep –color “[^ ]”
    >
    > Pádraig makes use of the square-brace character class operator in bash to filter out all the numerals.

    The [:digit:] syntax is part of tr, not part of bash. Look at the tr manpage for more details, like [:upper:] and [:lower:].

    Reply

  4. Ralph Corderoy’s avatar

    > Pádraig makes use of the square-brace character class operator in bash to filter out all the numerals.

    No, he’s using a character class in tr(1) to do that; bash isn’t involved. And I’m unclear why he has `lcase’ as one of dd(1)’s conversions when nothing but digits will be read by dd(1). It’s redundant.

    Reply

  5. Phill MV’s avatar

    That was last C program is ridiculous awesome.

    Reply

  6. nat’s avatar

    @root, @Ralph – Whoops, good point! Thanks, I fixed that in the text.

    @Jason, @Steve – The 6-word Hemingway story still haunts me.

    Reply

  7. Justin’s avatar

    I don’t use twitter, but this would fit:

    s=.o0O0o.o0O0o.o0O0o.o0O0o.o0O0o.o0O0o.o0;n(){ for x in `seq $1 $2 $3`;do notify-send ${s:0:x}; done };while :;do n 1 2 39;n 39 -2 1;done

    it could be made to be more obfuscated I suppose, but I already had it lying around, I just removed the newlines :)

    Reply

  8. nat’s avatar

    Woah, @Justin – nice one!

    Reply

  9. Ryan Paul’s avatar

    Thanks. :-)

    Reply

  10. Arturo Espinosa’s avatar

    Hey, Nat.

    Here goes a new one, plenty of color… 139 chars, rainbow.sh

    a=1;for i in {1..34};do printf %$[40-${#a}]s”$(eval $(echo $a*$a|bc|sed ’s/$/0/;s/\([0-9]\)/tput setab \1; echo -n \\ ;/g’))”\\n;a=1$a;done

    Reply

  11. Chan’s avatar

    I really enjoyed reading this article!
    Nice blog!!!

    Reply

  12. Antonio Ognio’s avatar

    This is another nice short script I adapted from here http://www.shell-fu.org/lister.php?id=161

    echo “Randomly-generated password: `

    Reply

  13. Binny V A’s avatar

    For a change, here is something useful. A less than 140 char script to post a tweet to twitter
    curl –basic –user “[User]:[Password]” –data-ascii “status=`echo $@|tr ‘ ‘ ‘+’`” “http://twitter.com/statuses/update.json”

    Reply

  14. Lars Holm’s avatar

    To root (6) about using [:digit] – I thought it was a part of regexpr, and that I could use it in bash, grep, gres not only in tr. Instead of writing–> while true; do I thought You could write–> : [expr]. Perhaps I’m mixing bash, ksh, zsh and tcsh together.

    Reply

  15. Woedoggy’s avatar

    The initial script that kicked all this off: Could someone explain the use of “<<<”

    Reply

    1. Nat Friedman’s avatar

      Ah, good question. That’s a cool feature of the shell.

      When you run a command like cmd < << str, then str is shell-expanded and fed to cmd as standard input.

      For example:

      $ bc < << "7+3"
      10

      Thanks for asking!

      Reply

  16. Priit Laes’s avatar

    Seems that something in your blog software has broken all the ampersands and comparison characters by double escaping them :S

    Reply

    1. Nat Friedman’s avatar

      Fixed, thanks for pointing this out. Wordpress does this from time to time – it feels like using Frontpage sometimes.

      Reply

  17. Renee’s avatar

    Can anyone tell me why ‘b’ is the same note as ‘4′ while all the other alphabet keys are the same notes as ‘0′ and ‘1′?

    Reply

  18. Joe Ryan’s avatar

    I continue to get an error when running the first script. I am still a Linux/Shell newb so it may be me missing something obvious. Here is the error I get.

    sed: -e expression #1, char 19: unknown option to `s’

    Reply