Showing posts with label zsh. Show all posts
Showing posts with label zsh. Show all posts

Wednesday, December 17, 2008

Color Completion Using Zsh Modules On Linux Or Unix

Hey There,

How's this for a long-overdue follow up. If my powers of site searching don't fail me, we haven't touched on the Z Shell (zsh) since our post, from July 2008 about multiple stream output in zsh which links back to another post on zsh's extended globbing functionality. This post is more of a follow up to that one than the other, but who's counting? ;)

Here's another cool thing you can do with zsh that involves color. Personally, I'm happy with a two-color scheme that makes my eyes sizzle (like blue on red - or red on blue - both of which look somewhat 3 dimensional), but you have to make room for color in your life. It's not all black and white, even though it seems that way most of the time ;)

In order to make use of this functionality, you'll have to use the modular functionality of zsh. Today, we'll be looking at color completion, but this isn't to say that this is the limit of what you can do with the shell. Just the limit of what I can manage to type today... Doing color completion in zsh requires two modules: complist and colors. The first requires you to set the extendedglob option to true and the second isn't really necessary, unless you don't want to remember what number equals what color in the ansi color scheme. Below, some quick examples of how you can set this up at the command line (or put in your .zshrc if you find you like it and dont' want to hassle with this constantly). Note that both options require that you load up additional modules within zsh:

host # setopt extendedglob
host # zmodload -a colors
host # zmodload -a autocomplete
host # zmodload -a complist


The -a option isn't absolutely necessary, but keeps you from having to remember to type:

zmodload zsh/complist

and

autoload colors

which, as shown, require two different forms to enter. Since they may as well both be auto-loaded, zmodload's -a option makes sense (especially since it stands for autoload... kind of).

Here are a few handy aliases for you to use that can make your zsh shell experience more enjoyable (if color does that for you ;). Of course, we didn't make all these up ourselves. Most are standard and can be found in the manpage for zshmodules. If you forget this, since zsh has 500 separate sub-manpages (j.k. ;), you can always find the name for this manpage by just invoking:

host # man zsh

and you'll see the reference to the zshmodules manpage in there.

You'll note the use of "zstyle" (which is explained more-than-fully in the same zshmodules manpage) to set up all these shell color aliases (not all of these will work on all systems) and is not restricted to the use of list-colors and the zsh completion feature. There are a lot more ways, built in to zsh, to make use of color in the shell:

zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS}
zstyle ':completion:*' list-colors 'reply=( "=(#b)(*$VAR)(?)*=00=$color[green]=$color[bg-green]" )'
zstyle ':completion:*:*:*:*:hosts' list-colors '=*=30;41'
zstyle ':completion:*:*:*:*:users' list-colors '=*=$color[green]=$color[red]'
zstyle ':completion:*' list-colors ''
<-- For when you get sick and tired of all the colors and just want them to go away!

Hope you enjoy these (if you use zsh - we're not trying to get you away from ksh or bash if you like 'em like most of us do) and have fun using colorful language the next time you code ;)

Cheers,

, Mike




Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Wednesday, July 23, 2008

Simple Multiple-Stream Output Redirection With Zsh On Linux and Unix

Hey there,

Following up on yesterday's post regarding using zsh's extended globbing functionality, today we're going to look at another feature supported by zsh, that can't be done nearly as easily in most other shells. Today we're going to look at some unique ways you can manipulate input and output redirection, even with multiple streams, while avoided a lot of extra code, and maybe a temp file or three :) This stuff is actually quite cool and I still count myself among the surprised that these things don't work in bash or ksh. The end objectives can be accomplished by all of the shells, of course, but we're looking for the most direct approach.

Again, these test were done on bash, ksh and zsh on Solaris Unix and RedHat Linux, so your results may vary. They probably won't though, because I couldn't find any version of bash or ksh that can do most of this stuff (see bullet point 4, later... :) So, without any further fanfare, let's get on with the zsh input/output redirection enhancements!

1. Redirecting output to two or more files on the command line: Generally, in bash and ksh (heretofore assumed to be the "other" shells), you can redirect output to multiple files using a variety of different methods. However, if you limit yourself to the standard output redirect operator, the following would not work (the output being limited to the last redirection to STDIN, since they just overwrite each other in succession):

bashksh # echo hi >file1 >file2
bashksh # cat file1
bashksh # cat file2
hi


although it will work in zsh, and for more than just two files (pick a number... I'm not sure how high you can go with this):

zsh # echo hi >file1 >file2 >file3
zsh # cat file1
hi
zsh # cat file2
hi
zsh # cat file3
hi


2. Redirecting output from two or more files on the command line: The same rules that applied to example 1, work the same way in reverse (with the each instance of redirection from STDOUT overwriting the previous). For instance, you'll get this output from bash and ksh:

bashksh # cat <file1 <file2
this is the content in file2


while in zsh, you get this:

zsh # cat <file1 <file2 <file3
this is the content in file1
this is the content in file2
this is the content in file3


3. Using a pipe to "cat," instead of the "tee" command, if you want to pump output to the screen while running an operation on a file. In other shells, you would normally use "tee" to capture output and watch as a process ran through it's paces. That would work great, but trying to do it with a pipe and "cat" would fail:

bashksh # pwd |tee filename
/home/techserv/mg131
bashksh # cat filename
/home/techserv/mg131
bashksh # pwd >filename|cat
bashksh # cat filename
/home/techserv/mg131


Note that, above, the "cat" command doesn't spit any output to the screen during the process' run; only to the file. In zsh, you can use the pipe to "cat" (shown above) as a substitute for "tee":

zsh # pwd >filename|cat
/home/techserv/mg131
zsh # cat filename
/home/techserv/mg131


4. And, lastly, we'll take a look at the redirection of command output (within parentheses) and go straight to the complicated stuff. This is actually listed as a feature of zsh (and, technically, it is), but my tests with ksh and bash have produced equal results, so it doesn't fit my definition of a "feature" of zsh (that being something unique to zsh, and not directly reproducible in other shells). For instance, this line returns the exact same output in all three shells:

bashkshzsh # sort <(grep -v "^#" <(paste -d: <(cut -d: -f1 /etc/passwd) <(cut -d: -f3 /etc/passwd) ) ) |egrep ':0|:1|:2|:3'
bin:2
daemon:1
listen:37
root:0
sys:3


While this is definitely cool stuff (And the redirection of lists generated by commands within parentheses can be used as multiple streams of input and output for all three shells, as well), my conclusion, to date, is that most advanced shells have this functionality built in. If you "must" see it fail, you can always rely on straight-up vanilla Bourne shell:

sh # sort <(grep -v "^#" <(paste -d: <(cut -d: -f1 /etc/passwd) <(cut -d: -f3 /etc/passwd) ) ) |egrep ':0|:1|:2|:3'
syntax error: `(' unexpected
sh # bin:2
bin:2: not found
sh # daemon:1
daemon:1: not found
sh # listen:37
listen:37: not found
sh # root:0
root:0: not found
sh # sys:3
sys:3: not found


Now, if bash and ksh had come back with that, I would have no reservations about labeling this type of multiple-stream output redirection and massaging a feature of zsh.

Yes, the ending to this post "was" anti-climactic, but at least you can be reasonably sure I'm remaining objective ;)

Cheers,

, Mike

Tuesday, July 22, 2008

Fancy Globbing With Zsh On Linux and Unix

Hey there,

Today we're going to look at some stuff that's probably not much of an education for the zsh aficionado, but that I find pretty cool (Considering that I'm hopelessly stuck on the Korn shell no matter how many so-called "improvements" they keep making to all the other ones ;) I did what I could to verify that this stuff only works with zsh (or, doesn't work on sh, ksh, ash, bash, etc - didn't test csh/tcsh) and hope you find it as interesting as I did ...and do :) Zsh actually has quite a few features which (while you can get around them other ways in other shells) are very convenient for everyday use.

We started looking at this stuff in an older post regarding I/O redirection differences between shells and will definitely come back around to that. But, for today, globbing is going to give us plenty of material :)

1. Filename globbing: Aside from the standard "ls *.sh" type of globbing done by most shells (which it supports), zsh also supports extended globbing. It generally isn't turned on by default, but if you just type this on the command line (or add it to your .zshrc), you can do a lot of cool things:

zsh # setopt extendedglob

Now, since we're trying not to cover stuff other shells can do (and this is the last time I'm going to write that ;), we'll blow by *.extension and *.[range] type matches, as these can be done in a few other shells. With extended globbing in zsh, you can do all sorts of other things, as well.

2. Listing opposite globs (match everything but the glob pattern using the ^ symbol, like in a sed range):

zsh # ls 
a.txt b.wri c.txt d.doc
zsh #ls ^*.txt
b.wri d.doc


3. Listing crazy extended numeric ranges:

zsh # ls           
file1 file1234 file1235 file1236 file1278 file1299 file2
zsh # ls file<1234-1236>
file1234 file1235 file1236
zsh # ls file<1234-1299>
file1234 file1235 file1236 file1278 file1299


4. Listing files using Perl-style piped OR's:

zsh # ls
file1 file1235 file1278 file2
file1234 file1236 file1299 folder1234
zsh # ls (file|folder)1234
file1234 folder1234


5. Searching subdirectories for globbed filenames recursively (Almost like find, except for the default output style):

zsh # find . -name "*bob*"
./subdir/a/bob.txt
./subdir/ab/c/bob2.txt
./subdir/ab/bob1.txt
zsh # ls **/*bob*
subdir/a/bob.txt subdir/ab/bob1.txt subdir/ab/c/bob2.txt


6. Using qualified suffixes to pick out files of certain types. For instance, you can use the "(*)" suffix to list out all executables, or, alternately, add the "(x)" suffix to your search to do the same thing, like this:

zsh # ls -l
total 8
-rwxr-xr-x 1 mg131 techserv 3 Jul 21 14:50 file1
-r--r--r-- 1 mg131 techserv 0 Jul 21 15:07 file1234
-rw-r--r-- 1 mg131 techserv 0 Jul 21 15:08 file1235
-rw-r--r-- 1 mg131 techserv 0 Jul 21 15:07 file1236
-rw-r--r-- 1 mg131 techserv 0 Jul 21 15:07 file1278
-rw-r--r-- 1 mg131 techserv 0 Jul 21 15:07 file1299
-rwxr-xr-x 1 mg131 techserv 3 Jul 21 14:50 file2
-r--r--r-- 1 mg131 techserv 0 Jul 21 15:10 folder1234
drw-r-wr-- 4 mg131 techserv 4096 Jul 21 15:13 subdir
zsh # ls *(*)
file1 file2
zsh # ls *(x)
file1 file2


readable and writable files can also be found this way (note that the files "file1234" and "folder1234" don't show up under the write ("w") qualifier). You can also combine qualifiers:

zsh # ls *(r)
file1 file1235 file1278 file2
file1234 file1236 file1299 folder1234

subdir:
a ab
zsh # ls *(w)
file1 file1235 file1236 file1278 file1299 file2

subdir:
a ab
zsh # ls *(rwx)
file1 file2


You can also list only the contents of directories:

zsh # ls *(/)
a ab


Pretty much every symbol that you can find in the output of "ls -F" can be used as a qualifier to your ls statement in this fashion. This is how we derived the "(*)" qualifier for executable files and "(/)" for directories. The same character replacement holds true for all the different filetype indicators listed by "ls -F":

zsh # ls -F
file1* file1235 file1278 file2* subdir/
file1234 file1236 file1299 folder1234


Interesting to note is that, insofar as r, w an x are concerned, we're only matching if the file is readable, writable or executable by the owner. If you want to match for others, just use the capital version (I'm not sure you can differentiate between the "group" bit and "other" bit in this manner, but I may very well be wrong:

zsh # ls *(X)
file1 file2
zsh # ls *(Xw)
file1 file2
zsh ls *(XR)
file1 file2
zsh # ls *(XW)
zsh: no matches found: *(XW)
<-- This is okay since, according to our ls output above, no file matches this qualification (being executable by "others" AND writable by "others")

There are actually a lot more cool things you can do with zsh, and not do with any other shell, but we'll leave those for another time. Hope you enjoyed this little glob of info-tainment and can put it to some good use :)

Cheers,

, Mike

Friday, July 4, 2008

Basic I/O Redirection Differences In Sh/Ksh, Bash and Zsh On Linux And Unix

Hey there,

Today we're going to look at four different shells (all tests were run on a Solaris 8 box and an old RedHat Linux 9 server) and some very basic I/O redirection (which can come in handy if you're ever stuck trying to read and write files in a blind shell), to show you how differently, and commonly, they all handle the same information. The only reason I find this sort of stuff remarkable is that it's so common, sometimes its hard to wrap my head around the fact that any distinctions exists (Okay, they're not HUGE, but the differences, where there are any, are enough to make you wonder ;) For instance, in our two experiments, we're only going to find one difference each. But, since the material we're working with is so basic (not meaning "simple"), it's a bit disconcerting (...oh, the drama ;)

First of all, we'll narrow down our list of shells and consider sh and ksh to be the same, since all of my testing, with regards to these two shells, yielded exactly the same results. Note that, for all of our tests, we only tried to create two conditions in a variety of ways; the first was to create an empty file, and the second was to redirect standard error (STDERR) to a file. See what I mean? This is very familiar ground for almost all Unix or Linux admins.

Now let's take a look at a how sh/ksh, bash and zsh are the same, when it comes to basic I/O redirection.

First test: Create an empty file. The following methods worked in all 4 shells (producing the expected output from the file and "wc -l" commands of "empty file" and 0, respectively):

host # : > FILE

This creates an empty file by using the colon (:) placeholder (which, technically, is a simple notation for "true") as output to be redirected into the blank FILE

host # /bin/true >FILE

Pretty much the same as the previous example, except we're running /bin/true explicitly.

host # /bin/false >FILE

Again, the same as the previous example, except we're using /bin/false for a goof. It's another program that doesn't produce output and is mainly used to generate a desired return status.

host # cat /dev/null >FILE

Just catting nothing to the FILE, and it ends up empty, as expected.

host # <>FILE

and this works as well (specifying empty redirection of STDIN and STDOUT) on all 4 shells.

host # echo <> FILE

While this, finally, spit a line of output to the screen, it still produced an empty file in all 4 shells, even with extra commands to echo, since they all spill out to the screen instead of going into the file. Kind of a waste of resources, but interesting somewhat ;)

host # echo how are ya <> FILE
how are ya


Now's here's the only basic thing I could find where they differed, in this respect:

host # >FILE

This produces an empty file on Solaris sh, ksh and bash (generally, it's shorthand for "cat /dev/null >FILE"), but results in a perpetual hang with zsh (zsh will not infer /dev/null (or true) from a lack of argument preceding the > redirection symbol).

Second test: Redirecting STDERR to a file. The similarities.

host # truss echo hi >FILE

This produces a non-empty file (with the word "hi" in it) in all 4 shells, since truss sends its output to STDERR and we're only redirecting STDOUT. This doesn't really prove any part of the experiment, but I wanted to put it out as a baseline, so we know that the truss output is going where we expect it to before we start goofing around :)

host # truss echo hi >FILE 2>&1

This, tying STDOUT and STDERR together to put all output into the file, works on all 4 shells as well :)

But, the following command only "really" works as expected in bash, and produces remarkably different results for the other shells:

host # truss echo hi &>FILE

In bash, the &> notation is shorthand for ">FILE 2>&1" - but this doesn't work for our other shells. In Solaris sh and ksh, the command spits output to the screen, since the & activates the shells' backgrounding feature, the job completes and the output file is completely empty. On zsh, "hi" gets spit to the output terminal and only STDERR goes into the output file (???)

Remember, these have just been really basic examples. Thinking about this sort of stuff makes you realize how truly "different" different shells are. Of course, among these very basic examples, we could only find two major
differences, but that's still a good number considering how rudimentary the shell functions are. In other words, these are I/O redirection issues which you can't change, except by script-wrapping (which probably isn't worth it, since, for each difference we found at least 2 similarities between the 4 shells and both experiments only tested one property).

If you're not sleeping yet, I'll assume this article was, at least somewhat, entertaining and/or helpful ;)

Have a Happy 4th of July!

, Mike

Friday, June 6, 2008

Piped Variable Scoping In The Linux Or Unix Shell

Hey There,

Today we're going to look at variable scoping within a piped-while-loop in a few Linux and Unix shells. We're actually almost at this point in our series of ongoing posts regarding bash, Perl and awk porting.

Probably the most interesting thing about zsh (and shells that share its characteristic, in this sense) is that the scope of variables passed through a pipe is slightly different than in other shells, like bash and ksh (Note that not all vendor's versions are equal even if they have the same name! For instance, HP-UX's sh is the Posix shell, while Solaris' is not) I'm taking care to separate the piping construct from the "is the while loop running in a subshell?" argument, as I don't want to get too far off course. And, given this material, that can happen pretty fast.

For a very simple demonstration of whether the scoping issue is a "problem" (defining problem as either a bug or a feature ;) with the while-loop or pipes, we'll look at a very simple "scriptlet" that sticks to using a while-loop, without any piping, like this:

while true
do
bob=joe
echo " $bob inside the while"
break
done
echo " $bob outside the while"


And we can see, easily, that the value of the "bob" variable stays the same, even after the while loop breaks, for all 3 of our test shells. If the while loop, alone, was the issue, bob shouldn't be defined when the while loop breaks:

host # zsh ./test.sh
joe inside the while
joe outside the while
host # ksh ./test.sh
joe inside the while
joe outside the while
host # bash ./test.sh
joe inside the while
joe outside the while



If we change this scriptlet slightly to make it "pipe" an echo to the while-loop, the behaviour changes dramatically:

echo a|while read a
do
bob=joe
echo " $bob inside the while"
break
done
echo " $bob outside the while"


Now, if we use zsh, the value assigned to the "bob" variable inside our while loop (which has been created on the other side of the pipe) actually maintains it state when coming out of the loop, like this:

host # zsh ./test.sh
joe inside the while
joe outside the while


On most other shells, because of variable scope issues with the pipe, an empty value of the "bob" variable is printed after they break out of the while loop, even though it does get correctly defined within the while loop. This is because (and here's where the technicality, and subtle differences between myriad shells, usually becomes a hotbed of raging debate ;) after the pipe, the read command (as opposed to the while loop) runs in a subshell, like so:

host # bash ./test.sh
joe inside the while
outside the while
host # ksh ./test.sh
joe inside the while
outside the while


Notice, again, that the "echo $bob outside the while" statement in these two executions prints an empty variable when the value bob is declared outside the while loop, even though it is set within the while loop.

For most shells, this is easy to get around in one aspect. The main problem stems from the fact that the value is being piped to the while loop, and not a direct fault of the while loop itself. Therefore, a fix like the following should work, and does. Unfortunately, with the command-pipe (such as an echo statement), you won't be able to use a while-loop in many cases, and would have to substitute a for-loop, like so (In most shells, redirecting at the end of a while loop with << will either result in an error or clip the script at that line):

for x in 1
do
bob=joe
echo " $bob inside the while"
done
echo " $bob outside the while"


host # zsh ./test.sh
joe inside the while
joe outside the while
host # ksh ./test.sh
joe inside the while
joe outside the while
host # bash ./test.sh
joe inside the while
joe outside the while


This gets worse (usually hangs) if you try to get around the pipe by doing some inline subshelling with backticks, like:

while read `echo 1`

However, the following solution (awkward though it may be) does actually do the trick (substitute any other fancy i/o redirection you want, as long as you "avoid the pipe"):

exec 7<>/tmp/bob
echo -n "a" >&7
while read -r line <&7
do
bob=joe
echo " $bob inside the while"
done
echo " $bob outside the while"
exec 7<&-
exec 7>&-
rm /tmp/bob

host # zsh ./test.sh
joe inside the while
joe outside the while
host # ksh ./test.sh
joe inside the while
joe outside the while
host # bash ./test.sh
joe inside the while
joe outside the while


For more examples of input/output redirection, check out our older post on bash networking using file descriptors.

Now, when it comes to reading in files, the case is a bit easier to remedy. If you're in the habit of doing:

cat SOMEFILE |while read x
...


You'll run into the same scoping problem. This is also easily fixed by using i/o redirection, which would change our script to this:

while read x
do
bob=joe
echo " $bob inside the while"
done < SOMEFILE
echo " $bob outside the while"


Assuming the file SOMEFILE had one line of content, you'd get the same results as we got above with the for loop.

And that's about all there is to that (minus the highly-probably ensuing arguments ;). There are, I'm sure, a couple more ways to do this, but using the methods that fixed the "problem" of variable scope in bash and ksh is probably better practice, since zsh (and shell's that share its distinction in this case) is a rare exception to the rule (even though zsh may very well be doing things the "proper" way) and the bash/ksh fix works in zsh, while the opposite is not true.

At long last, good evening :)

, Mike


Thanks for this comment from Douglas Huff, which helps to clarify the underbelly of the process:

A friend of mine pointed me to this article and the
previous one in the series that you wrote [on variable scoping]...

I had two comments on these articles but you seem to have
comments disabled, so I figured I'd email them to you.

First, calling it a "scoping" issue is a bit misleading.
While technically true, understanding the underlying
reasons why this doesn't work as "expected" is key to
understanding how you can work around it in POSIX sh or in
ksh without the zsh/bash syntatical sugar for doing so.

What's going on is that a process cannot modify the
environment of it's parent.

When you do:

something | while read blah; do blah; done

What the shell is doing is first executing a subshell
(separate process) that runs the while with stdin
redirected to read from the unnamed pipe. Then in another
subshell it runs "something" with standard out redirected
to the unnamed pipe.

Knowing this it's quite easy to replicate the behaviour
from bash 2/3 and zsh in POSIX sh and ksh with a bit of
understanding of the underlying mechanics. The trick is to
keep the while inside of the original process (since it is
run by the interpretter and does not require a separate
process) and execute the other command in a subshell.
Which is exactly what the syntactical sugar does for you
behind the scenes in bash2&3/zsh.

Thursday, May 29, 2008

Simple Shell One-Liner To Enumerate File Types In Linux and Unix

Hey there,

Lately, we've been focusing a lot on Perl "one liners," from mass file time syncing to name and IP resolution and I thought it was only fair that we should write a post about a shell "one liner" for once. After all, most standard Unix and Linux shells are perfect for that purpose :)

Here's a very quick way to take an inventory of all the different file types (including directories, sockets, named pipes, etc) that exist in a given directory tree and provide a tally of each file type. I'm not entirely sure that I care if I have 15 ASCII text files and 2 Perl scripts in my current working directory, but this little piece of code must be able to help someone somewhere accomplish something, even if it is only to use as a smaller part of a larger organism ;)

This works in pretty much any standard shell on every flavour of Linux and/or Unix I've tested (ash, sh, bash, jsh, ksh, zsh, even csh and tcsh, which is huge for me since I never use those shells. One day, soon, I will redouble my efforts and just learn how to use them well, which, hopefully, won't result in my writing a whole bunch of posts about "cool" stuff that everyone has known about for the past few decades ;).

This one-liner could actually be written as a script, to make it more readable, like this:

#!/bin/sh
find . -print | xargs -I var file "var"|
awk '{
$1="";
x[$0]++;
}
END {
for (y in x) printf("%d\t%s\n", x[y], y);
}' | sort -nr


But, since I'm a big fan of brevity (which is about as far away from obvious as possible if you consider my writing style ;), I would run it like this:

host # find . -print|xargs file|awk '{$1="";x[$0]++;}END{for(y in x)printf("%d\t%s\n",x[y],y);}'|sort -nr

In my terminal, that all comes out on one line :) And here's the sort of output you can expect:

host # find . -print|xargs file|awk '{$1="";x[$0]++;}END{for(y in x)printf("%d\t%s\n",x[y],y);}'|sort -nr
23 Bourne-Again shell script text
11 ASCII text
10 perl script text
5 pkg Datastream (SVR4)
4 directory
4 ASCII English text
2 UTF-8 Unicode English text, with overstriking
2 Bourne shell script text
1 RPM v3 bin i386 m4-1.4.10-1


and, just to confirm the file count, we'll change the command slightly (to total up everything, instead of being particular -- another "one-liner" that's, admittedly, completely unnecessary unless you're obsessive/compulsive like me ;) and compare that with a straight-up find:

host # find . -print|xargs file|awk 'BEGIN{x=0}{x++}END{print x}'
62
host # find .|wc -l
62


Good news; everything appears to be in order! Hope this helps you out in some way, shape or form :)

Cheers,

, Mike

Thursday, April 3, 2008

Setting Static And Dynamic Window Titles From The Shell

Hey There,

Following in the tradition of our "terminal beautification" posts on using color in the shell and making fancy looking menus with tput, today we're going to take a look at setting the title of your terminal window from within the shell.

This is actually a slight variation from what we've looked at before, but it can be just as helpful, since you'll be using your shell to modify the "content" of your terminal's title in Windows (or whatever OS you use). This can be a great help in keeping organized. Most terminal clients provide functionality like this as a standard, but don't allow you to modify the defaults (like every window will have the title of the host you're connected to, etc) without jumping through hoops on the Windows (or Mac) end of things.

The simplest thing to do is to set static window titles in "xterm." In fact, if you're running X-Windows, you don't even have to do anything fancy, since most widely-distributed versions of it contain command line arguments specifically for doing this. For instance, the following command line (invoking xterm) would bring it up with the title "This XTerm Is Mine" (and, for goofs since it's an available option, an icon titled "This Icon Is Mine, Too!"):

host # xterm -T "This XTerm Is Mine" -n "his Icon Is Mine, Too!" &

Simple enough. It's also fairly simple to set static window titles for almost any terminal emulator (like PuTTY or SecureCRT) by typing at the command prompt or including a line in your .profile, .bashrc or .zshrc (Or whatever initialization file you use if you use an alternate shell).

Here again, we come back to some concepts that we first went over when we looked at using color in the terminal. Your window title is created by using very similar "echo -e" syntax (include the initial "escape") and can be done at the command line by typing the following:

host # echo -e "\033]0;This Is My Window Title And My Icon Title\007" <--- This pretty much works all the time
host # echo -e "\033]1;This Is Just My Icon Title\007" <--- This works sometimes depending on your terminal
host # echo -e "\033]2;This Is Just My Window Title\007" <--- This pretty much works all the time

In the above three lines, we set the window and icon title, icon title and window title, respectively. Some of these statements may not work on your terminal, so you'll have to test, but (in my experience so far) at least one of them should. Usually the first one (setting both the icon and title) is your best bet.

Now, if we want to have our window and icon title change dynamically (we're going to forget about the icon-only and window-only settings, since they can be derived by just substituting the "0" in the following lines with either "1" or "2," per the examples above) all we have to do is integrate the escape sequences into our command prompt. In some shells this functionality has been accounted for. In others, we can force it.

1. Bash - To set this in bash, we'll just set the value of the PROMPT_COMMAND variable, either at the command prompt or in our .bashrc (or .profile) for permanence:

host # PROMPT_COMMAND='echo -ne "\033]0;${HOSTNAME}:${PWD} # \007"';export PROMPT_COMMAND

and, you'll notice that your window and icon titles are now something like:

host1:/users/user1 #


and that the window and icon titles will change whenever you change directories :) We'll do the same command line string for the other shells, so this post maintains some consistency.

2. Zsh - To set this in zsh, all we need to do is set the value of the precmd function at the command prompt or in our .zshrc, like so:

host # precmd () {print -Pn "\e]0;%n:%~\a"}

3. Ksh - To set this in ksh, all we need to do to get the same result is type the following at the command line, or include it in our .profile (Admittedly, this can be a little tricky since you have to insert the actual control characters into the prompt - For more info on how to do that, check out our previous post on how to really represent control characters within files). Note, also, that setting PS1 will change your main command prompt, so, after the ctl-G, I've added "${PWD}# '" which will be our actual command prompt, as opposed to our new window title. You should replace this with whatever your command prompt already is if you want to keep it the same:

host # PS1='^[]0;${USER}@${HOST}: ${PWD}^G${PWD}# '

And you can use that for most other shells, except...

4. Csh/Tcsh - To set this in csh/tcsh, you can use the "settitle" alias (Again, you'll need to manually put in the real control characters and the title is slightly different since my tcsh/csh knowledge is fair-to-middlin' in this respect ;)

host # alias settitle 'set t=$cwd:h;echo -n "^[]2;${HOST}: "!*"^G"'

And that should be all there is to getting yourself all set up to have dynamic and/or static window and icon titles for whenever you want or need. Here's hoping you can find a window and icon naming scheme you can live with and enjoy :)

Best wishes,

, Mike