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
- Leave a comment
- Subscribe with Google Reader
- Follow me on Twitter
Did you like this article?
-
Trackback from meneame.net on 11 April 2008 at 6:20 pm
-
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. -
> 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:].
-
> 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.
-
Pingback from Twittable scripts « Codeflicker on 13 April 2008 at 9:48 am
-
Pingback from Turulcsirip - macat on 13 April 2008 at 10:38 pm
-
Thanks.
-
Pingback from links for 2008-04-20 | fudge.org on 20 April 2008 at 3:35 pm
-
This is another nice short script I adapted from here http://www.shell-fu.org/lister.php?id=161
echo “Randomly-generated password: `
-
Pingback from Tiny Website Contest | AF-Design on 23 May 2008 at 4:47 pm
-
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” -
Pingback from Tony Thompson » Blog Archive » On Brevity. on 7 February 2009 at 1:34 pm
-
The initial script that kicked all this off: Could someone explain the use of “<<<”
-
Seems that something in your blog software has broken all the ampersands and comparison characters by double escaping them :S
-
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′?
-
I bookmarked this guestbook., Real buy diflucan without no prescription veterinary, [url= http://www.wearediabetic.org/buydiflucanwithouts/bio ]Real buy diflucan without no prescription veterinary[/url], 228681,

34 comments