Search this site


[prev]  Page 3 of 3

Metadata

Articles

Projects

Presentations

bourne (sh, ksh, bash, zsh, ...) file descriptor fun

Yeah, so having just figured this out now (I'm sure it's in a manpage somewhere) -
You can open file descriptors at will by doing:

exec 3> file_output

The 3 here is the number of the file descriptor, and can be any number really. What this lets you do now is write to that file descriptor:

echo "Hello there" >&3

Now, if you look at the contents of file_output you'll see that it contains "Hello there" - neat?

To close a file descriptor do this:

exec 3>&-

again, where 3 is the number of the file descriptor you want to close.

poor man's netcat.

This script requires ksh. It makes use of a feature of ksh (zsh supports this too, but differently) called a co-process. A co-process is started by using the |& pipe at the end of a command. The file descriptor you use to write to is >&p. To read from it, you use <&p. Here's an example:
tr 'a-z' 'A-Z' |&
echo "hello there" >&p
exec 3>&p; exec 3>&-
cat <&p

The output is:
HELLO THERE

WTF?! You might be confused, I was when I first started playing with these. What happens is it runs tr 'a-z' 'A-Z' in the background as a co-process. Then it echos "hello there" to the input of tr. To signify that we are in fact done sending input, we have to close the input file descriptor, this is done through 2 statements: exec 3>&p - this opens file descriptor 3 and has it output to the co-process. The next statement, exec 3>&-, tells ksh you want to close file descriptor 3, which in turn closes the input to the co-process. The last line should be fairly obvious: cat <&p - it sends the output of the co-process through cat.
Ok, so you want that script do ya? Look below.

#!/bin/ksh
[ $# -ne 2 ] && echo "Invalid parameters" && exit 1
telnet $1 $2 |&
cat <&0 >&p
sed -e '1,3d' <&p

I won't get into much detail as to how things work here, should be pretty straight-forward. It takes 2 parameters, first being the host and second being the port. It reads from standard input and outputs to standard output.

bourne shell goodness.

So until now I never knew how to pipe things into my shell scripts - I'm smacking myself on the head now for not having figured this out sooner.

In unix, the standard file descriptors stdin, stdout, and stderr are numbered 0, 1, and 2, respectively. I've always done descriptor redirection, such as:

grep ^Poop myfile.txt 2>&1 > /dev/null

This will redirect stderr (fd #2) to stdout (fd #1). stdout is then redirected to /dev/null.

What I never bothered trying was redirecting stdin. (fd #0). Let's take a look at this simple script:

#!/bin/sh

echo "Printing output:"
sed -e 's/^./Output: &/' <&0 

Note the <&0 - this will use our stdin as the stin for sed.
Now, let's try running it:
ls | ./myscript

The output is just how I want:

whack(~/bin) [590] > /bin/ls | sh myls.sh
Printing output:
Output: Mrandom
Output: fix
Output: hostlist
Output: intarweb
Output: logtail
Output: malbums
Output: mclear
Output: minfo
Output: myls.sh

screen/xapply = fun

So I rewrote an 'sshall' script I had been using for some time to use xapply instead, I don't know if I'll keep it this way.

I added screen support to, so doing:
sshall -s
in a screen session will open each ssh command in a new screen window. Woot?

<3 xapply.

xapply 'ping -t 1 -c 1 %1 > /dev/null 2>&1; A=$?; echo -n "%1 - "; [ $A -eq 0 ] && echo "ONLINE" || echo "***DOWN***"' `cat hostlist`

Outputs:

project1 - ONLINE
mokey - ***DOWN***
boober - ONLINE
wimbley - ONLINE
doozer - ONLINE
felix - ONLINE
red - ***DOWN***
sprocket - ONLINE
henchy - ONLINE
falcon - ONLINE
talon - ONLINE
junior - ONLINE
doc - ONLINE
eagle - ***DOWN***

Slight update.. ksh prompt

Found a few issues with the latest changes I made to my ksh prompt...

[ -w "/" ] && Z="#" || Z=">"; export Z
PROMPT='$(A=$?; /bin/echo -n $HOST"("`/bin/pwd | /usr/bin/sed -Ee "s,(/usr)?$HOME,~,"`") [!] "$([ $A -ne 0 ] && /bin/echo -n "!!${A}!! ")$Z" " )'
XTERM=
case $TERM in
  aterm|xterm|dtterm)
    XTERM='$(echo -n "\033]2;'${PROMPT%%$Z*}')\007" 1>&2)'
    ;;
esac 
export PS1=$XTERM$PROMPT

Keep in mind that PROMPT is very long...
Previously, it didn't report exit codes due to kludges that made $? change to 0 due to the exit status of some of the crap that runs inside the prompt.

Huzzah. However, on a sad note I'll point out the obnoxious length of PS1 now...

nightfall(~) [569] > echo "${#PS1}"
257

I am the winner, take THAT ksh!

I've been trying off-and-on for some time now to get dynamically updating xterm titles in ksh. Sure, you could set your prompt to something like:

XTERM="^[]2;Yay for titles^G"
PS1=$XTERM'$HOST($PWD) >'

This works all good and dandy until you try to write something on the command line longer than what's displayable on a single row of text, you'll see that ksh thinks the end of your line is shorter than where it's at. This is due to the problem that your xterm title is still physically a part of your prompt. This was too annoying for me to bother dealing with since my prompt at a minimum is something around 20 characters.
So I gave up trying different ways, and today I had another idea. $() and `` doesn't return anything printed to stderr. We'll use that to our advantage!

PROMPT='$HOST($(/bin/pwd | sed -Ee "s,(/usr)?$HOME,~,")) [!] '$EXITCODE'$Z '
EXITCODE='$(A=$? && [ $A -ne 0 ] && echo "!!${A}!! ")'
XTERM= 
case $TERM in
  aterm|xterm|dtterm)
    XTERM='$(echo -n "\033]2;'$PROMPT'\007" 1>&2)'
    ;;
esac
export PS1=$XTERM$PROMPT

As you can see, the $(echo ...) has stdout redirected to stderr, and therefore our xterm title text gets completely ignored by PS1 when it's executed.
Huzzah!

another lame hack (ALH)

So I got bored and after discovering that java doesn't let you (Or to my knowledge) access enviroment variables.
ENVS=`env | sed -e 's/"/\"/g' | sed -e 's/^(.*)=(.*)$/-D1="2"/' | \
      sed -e 's,.$,& \\,' && echo "$*"`
sh -c "java ${ENVS}" 
Put that in a shell script and call it with the same parameters you'd normally call java with. ie;
./lamehack.sh test
This will run 'java test' except it also passes a bunch of -D definitions for use in System.getProperty(). For example, you could now do: System.getProperty("DISPLAY") to grab the DISPLAY enviroment variable.

Neat ksh prompt

My prompt is something in the form of:
hostname(/path/to/pwd) [###] >

Unless the previous command that was executed returned an exit code other than 0, in which case it will display the exit code just before the > as !#! where # is the exit code.

from my .profile

[ -w "/" ] && Z="#" || Z=">"
EXITCODE='$(A=$? && [ $A -ne 0 ] && echo "!!${A}!! ")'
PS1='$HOST($(/bin/pwd | sed -Ee "s,(/usr)?$HOME,~,")) [!] '$EXITCODE'$Z '

This will let you use pwd and substitute ~ for your home directory. (pdksh currently doesn't support something like this, so the hack was needed)

That's it, here's an example of what it'll look like:

nightfall(~) [471] > 
nightfall(~) [471] > echo "hi there"
hi there
nightfall(~) [472] > ls -asoijo23i5j23
ls: illegal option -- j
usage: ls [-ABCFGHLPRTWabcdfghiklnoqrstu1] [file ...]
nightfall(~) [473] !1! > thisisnotacommand
ksh: thisisnotacommand: not found
nightfall(~) [474] !127! > cd bin
nightfall(~/bin) [475] > cd /nope
ksh: cd: /nope - No such file or directory
nightfall(~/bin) [476] !1! > cd /usr/local/
nightfall(/usr/local) [477] > 

Note the !1! and !127!. These are the exit codes of the previous commands.