shudder's guide to network programming in bash ============================================== 1. What's this? I'm kind of a maniac when it's about doing nifty things with tools not originally designed for that.that's why I wrote the "guide". maybe there are some other resources that document this (not counting the bash manual page) but I wanted to express my own impressions on the possibilities of bash. 2. What's bash? err...if you really don't know what's bash, then maybe you shouldn't read the rest of the document. 3. Aren't you going to start? so as I wrote in the beginning I'll document the part of bash where you could do some sort of network programming (or sort of). why writing a complex program in C (perl is out of the question ;) to open a socket to pass some data through it when all this could be done in a few lines with the good old bash? (don't answer that) actually sometimes it's better to do that in C but let me show you the alternative. let's start from the beginning. for a program to read and write, it should use some technique to deal with several streams that point to different resources. to do that a "file descriptor" is used when this is needed. a descriptor is just an integer that correspondents to the opened stream. when a program is started three descriptors are opened for I/O.they are: 0 - input 1 - output 2 - error (diagnostic) output now if we want to write something on the screen we could use descriptors 1 or 2.we can look what descriptors are open in a running program by checking that in /proc//fd. the diagnostic output is used by programs to distinguish the normal messages from the error messages.for example if we have a file called blah in our current working directory and do a `ls blah' the result will be printed in the standard output.but if there's no file by that name the result will be something like: ls: blah: No such file or directory and it will be printed using the standard error. we can check that by redirecting the output.as we know (or maybe not?) bash can redirect streams with a simple techniques.here's what the man page of bash says about redirection: Redirecting Input Redirection of input causes the file whose name results from the expansion of word to be opened for reading on file descriptor n, or the standard input (file descriptor 0) if n is not specified. The general format for redirecting input is: [n]word let's do this again.we suppose that there's a file named blah in the current working directory: $ ls blah blah and there's no file named whop: $ ls whop ls: whop: No such file or directory so the next should be correct: $ ls blah whop ls: whop: No such file or directory blah $ ls blah whop 2> /dev/null blah $ ls blah whop 1> /dev/null ls: whop: No such file or directory $ ls blah whop &> /dev/null $ to redirect one descriptor to another we should use: [n]>&word and [n]<&word to close one: [n]<&- everything should be clear now.except for the &> redirection.it redirects both output and error messages.read the bash manual for more details. now as we know how to redirect (at least the basis) let's learn how to open streams.the form is: [n]<> word where n is the number of the new descriptor and word is the name of the file. lets open a stream to the file whop and write something in it: $ exec 3<> whop $ echo "hello" 1>&3 $ exec 3<&- exec is used for the opening and closing to be in the current shell in the second line I'm redirecting the standard output of `echo' to the descriptor 3 which points to the newly created file - whop now lets read what we have written: $ exec 3<> whop $ read 0<&3 $ echo $REPLY hello $ exec 3<&- voila.it's simple, right? but, where's the network part of all of this?! bash handles two special "files" which are used to open network sockets. the manual page of bash says: /dev/tcp/host/port If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open a TCP connection to the corresponding socket. /dev/udp/host/port If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open a UDP connection to the corresponding socket. well actually you'll not going to find directories named /dev/tcp and /dev/udp (unless you create them, but don't).they are valid only in bash.let's suppose that you have port 80 (www) open. we could improvise a simple web browser in bash by opening a tcp connection like this: $ exec 3<> /dev/tcp/127.0.0.1/80 now descriptor 3 points to port 80 of our machine (if you're connected to the Internet you can open sockets to remote hosts by simply providing their hostname/address and port) lets send the http request: $ echo "GET /index.html HTTP/1.0" 1>&3 $ echo 1>&3 now the result is waiting for us to get it: $ while read 0<&3; do echo $REPLY; done this reads up a line until EOF and prints it on screen now I suppose you know what `network programming in bash' is. but if you're new to it I recommend you to read the manual page first then guides like this. 4. Network programming in bash is cool.Now write something useful! this is a very simple irc bot.all it does is to stay in a channel and keep the connection to the server open.feel free to modify this script to do something better (see rfc1459 for more information on IRC) #!/bin/bash # # configuration nick="`basename $0`$$" # nickname name="$0 $*" # real name chan="#sh #bash" # channels to join mode="+i" # irc mode # end of configuration # use our login name if there's no nickname nick="${nick:-$USER}" # see if we can find a realname for our nickname name="${name:-`grep $nick /etc/passwd | cut -d : -f 5`}" host="$1" port="$2" # redirect error messages to file `irc-errors' exec 3<> irc-errors 2>&3- if [ ! "$2" ]; then echo "usage: `basename $0` [hostname] [port]" exit 1 fi # try to connect if ! exec 3<> /dev/tcp/$host/$port; then echo "`basename $0`: unable to connect to $host:$port" exit 1 fi # duplicate standard input and output with the newly created socket exec 0<&3 1>&3- # register to the server echo "USER $nick ${mode:-+iw} $nick :$name" echo "NICK $nick" # join channels for c in $chan; do echo "JOIN $c"; done while read; do set -- ${REPLY//$'\r'/} # answer the critical ping request # otherwise the server will disconnect us [ "$1" == "PING" ] && echo "PONG $2" # your code should go here done exec 1<&- 2<&-