Cygwin, Part Two: Linux-like Shells

Your favorite Linux shell — bash, zsh, ksh, and more — is probably part of the Cygwin package.

Microsoft’s shell, CMD, is
essentially the original MS-DOS shell with
some extensions that make it more usable. (The January 2004
“Power Tools” column, “Cross-Platform
Command-Lines,” tells more.) As you saw last month, though,
installing the freely-available Cygwin
package on your Windows system gives you a
much more powerful command-line. In fact, Cygwin comes with a
number of major Linux shells — if you install them, that is.
Here’s a list of available shells, generated from within
Cygwin:

$ cd /bin; ls *sh.exe
ash.exe      ksh.exe    tclsh.exe
autossh.exe  pdksh.exe  tcsh.exe
bash.exe     sh.exe     wish.exe
csh.exe      ssh.exe    zsh.exe

These shells are virtually identical to the ones on your Linux
system.

Let’s look at some of their features and settings that
will help you get more work done more quickly (and with more fun)
on a Windows system. We’ll also see a script for finding
programs by entering part of their name. (A lot of this is also
useful under Linux.)

Handling File Types

Unix and Linux generally don’t
require filename extensions (filenames ending with a dot and three
or four characters that tell the file type). For instance, text
filenames don’t need to end with .txt.
(The January 2005 “Power Tools” column,
“It’s (Not) Magic,” has details.)

Under Cygwin, it’s easy to use Linux-type filenames
without extensions: simply create and use files as you normally
would on your Linux box. For example, if you create a shell script
in a file named foo, there’s no need
to name it foo.sh (although you might want
to for easy recognition); simply use the normal name, make the file
executable with chmod+x, and run the script
by typing foo (or ( "c">./foo) at the prompt in a Cygwin shell window.

If your shell’s PATH includes
Windows folders like C:\windows\system32
— and it probably does, because the Windows "c">PATH is copied into Cygwin shells — you’ll
be able to run Windows executables from your shell prompt. If the
Windows executable folders are listed before the Cygwin directories
in your Cygwin PATH, you’ll get (for
instance) the Windows version of find
instead of GNU find. (You don’t need
to type the .exe extension for an executable
program; simply type (say) find.)

If you’ll be working with files and folders created by
Windows applications, Windows extensions like "i">.txt will appear in your Cygwin shell. (As we saw last
month, an exception is Windows shortcut files, or filenames ending
with .lnk. Cygwin hides "i">.lnk extensions as it treats those files as symbolic
links.)

Your shell may let you “hide” some types of files
— that is, files with certain extensions — during
globbing (wildcard expansion) and filename completion. For
instance, the bash shell variable
GLOBIGNORE is a colon-separated list of
patterns that tells the shell which types of files not to include
in wildcard matches. Here’s a demonstration:

$ cd /cygdrive/c/windows
$ ls v*
vb.ini  vbaddin.ini  vmmreg32.dll
$ GLOBIGNORE=’*.ini:*.dll’
$ ls v*
ls: v*: No such file or directory
$ unset GLOBIGNORE

The bash variable "c">FIGNORE controls filename completion. It’s a
colon-separated list of file suffixes (not
complete filename glob patterns, but just suffixes) that
won’t be considered when you complete a filename, by pressing
TAB, for instance. As an example, if you use "i">Emacs, you might not want to match backup files
(filenames ending with tilde, ~) from the
shell’s command line. You’d set "i">FIGNORE like this:

FIGNORE=’~’

(Although the ls utility isn’t part
of the shells, you probably use it often from a command prompt.
GNU ls has its own options to control which
files are listed. We’ll see more in next month’s
column.)

In tcsh, spelling correction of command
names ignores filename extensions. For instance, if you’re
trying to run the Windows findstr command
(which works something like grep) and you
mistype its name, tcsh will ask if you want
to run findstr.exe but it won’t
mention the .exe:

[jpeek@nt /d/tmp]$ findst "error" *.txt

CORRECT>findstr "error" *.txt (y|n|e|a)?

In general, if you use the Cygwin shell the way you’d use
a shell under Linux, you’ll be okay in most cases.

Slashes vs. Backslashes

One area where Linux and Windows shells can’t easily be
compatible is the difference between Linux and Windows pathname
separators. A Linux filesystem uses forward slashes ( "c">/directory/file), while Windows uses backslashes
(\folder\file.ext). Configuring Cygwin at
install time to use forward slashes in pathnames will probably make
things easiest for Linux fans.

Beware of places where you need to type a Windows-like pathname,
though. You may need to use quoting or pairs of backslashes on a
command line– for instance, "c">’\windows\afile.txt’ or "c">\\windows\ \afile.txt.

There’s a related problem in the "i">bash and zsh built-in command
read, which reads a line from the terminal
and saves it to a shell variable. It treats a backslash as an
escape character. So, in the following shell script fragment:

echo -n "Enter the Windows pathname: "
read winpath

If you run the script and enter "c">C:\windows\xyz, then $winpath
contains C:windowsxyz. You can work around
this problem by using read –r winpath
instead of just read winpath. The option
–r disables backslash escapes.

Cygwin comes with a program named cygpath
that translates pathnames from Windows style (with "c">\) to POSIX style (with /) and
vice versa. For example:

$ cygpath -w /cygdrive/c/windows/foo.exe
c:\windows\foo.exe
$ cd $(cygpath -p ’c:\windows’)
$ pwd
/cygdrive/c/windows

To find out more, use man cygpath.

Starting Shells

Cygwin’s installation process can install a shortcut on
the Windows desktop and the Start Menu. The shortcut points to a
Windows batch file (CMD script file) named "i">cygwin.bat. The batch file starts "i">bash after first setting the Windows drive. The shortcut
looks like this:

@echo off
C:
chdir C:\p\cygwin\bin
bash ––login –i

The batch file sets the options "c">––login (start a” login” shell that will
read the .bash_profile setup file) and
–i (be sure the shell is interactive).
That batch file isn’t as straightforward as starting the
shell executable directly from a Windows shortcut icon on the
desktop or the Start Menu.

Figure One shows the Properties dialog
for a desktop Z shell icon. Setting the shortcut key combination
(here, to Ctrl-Alt-Z) gives another way to open a shell
quickly.

Once you’ve made a shortcut icon that points directly to
an executable file, you can also drag the icon to the Quick Launch
area next to the Start icon in the toolbar; this copies the shell
icon and lets you launch it with one click. To put the shell in the
list of applications at the Start Menu’s top left corner,
right-click the shortcut icon and choose Pin to
start menu
, as shown in Figure Two.
(Windows apparently doesn’t let you pin a "i">.bat file shortcut to the Start Menu, but a "i">.exe shortcut works fine.)

Using Shell Features that GUIs Don’t
Have

One of the advantages of a shell– versus a graphical interface
like Windows Explorer– is the shells’
programmable features. There’s no need to use those features
from a script; you can type them on the command line– edit them
interactively, if needed, with the shells’ built-in editors–
and let the shell do the work while you do do something else.

Want to copy a lot of big folders from several separate
filesystems? It’s easy to give their pathnames to a
for loop and let the copying proceed —
instead of clicking, waiting, clicking, and waiting more. Have a
series of time-consuming unrelated commands to run? Put semicolons
(;) or AND-operators ( "c">&&) between the commands to execute them in
sequence. Test/branch commands can make decisions for you, so you
don’t need to watch a display and wait.

Let’s see three examples. (The >
prompts are secondary shell prompts, presented whenever the shell
is waiting for you to finish typing a command.)

$ for dir in /a/b /c/d /e/f /g/h
> do cp -rp "$dir" /i/backup
> done
$ find /j ...; sort /k...; lpr /m...
$ while sleep 60; do
> test ! something && continue
> do something else...
> done

Where’s that program?

The names of some utilities can be hard to remember. Is that
Netpbm utility named "i">tiftopnm or tifftopnm? What
versions of grep are installed? This is
especially nice to know if you use multiple systems with different
configurations.

Standard system tools like apropos
(man-k) can help, but the answers they give
can be out of sync with the programs that are actually installed
and available in your shell’s search path. A "i">bash script named findcmd does a
better job. It searches each directory in the "i">PATH environment variable for a filename that matches
patterns you enter. Figure Three shows an
example of findcmd matching three
patterns.

Figure
Three:
Running the "i">findcmd script
$ findcmd

>>> findcmd: Enter command name pattern (example: *cmd*).   
Empty line exits:
> *cmd
/home/jpeek/bin/findcmd
/cygdrive/c/WINDOWS/system32/login.cmd
/cygdrive/c/WINDOWS/system32/usrlogon.cmd

>>> findcmd: Enter command name pattern (example: *cmd*).   
Empty line exits:
> *cmd*
/home/jpeek/bin/findcmd
/cygdrive/c/WINDOWS/system32/cmd.exe
/cygdrive/c/WINDOWS/system32/cmdl32.exe
/cygdrive/c/WINDOWS/system32/login.cmd
/cygdrive/c/WINDOWS/system32/usrlogon.cmd

>>> findcmd: Enter command name pattern (example: *cmd*).   
Empty line exits:
> cmdial32.dll
/cygdrive/c/WINDOWS/system32/cmdial32.dll

The first search shows filenames ending with "c">cmd. The second shows names containing "c">cmd. And the third finds the exact name "c">cmdial32.dll. Listing One shows
the important part of the script.

Listing One:
The most important part of the "i">findcmd script

while …
read pattern
for dir in “${pathdirs[@]}”
do
cd “$dir”
set $pattern
if [ "$1" != "$pattern" ]
then
for match
do
test -x “$match” && echo “$dir/$match”
done
fi
done
done

The script prints a prompt and reads a filename pattern.
(Entering patterns with read– instead of on
the command-line– avoids “No match” errors and
too-early filename expansion.) A for loop
steps through each directory in the PATH
(pre-processed to convert null or “.” entries into the
current directory, and to remove missing and duplicate
directories).

The command set $pattern “puts the
pattern onto the command line,” doing filename expansion upon
the pattern as if it had been typed on a command line in this
PATH directory. If there are no matches,
bash will store the unmatched pattern in
$1; the if /
test statement doesn’t print any
output. Otherwise, one or more matches are saved in the positional
parameters $1, $2,
and so on; a inner for loop tests each match
to be sure it’s executable, then outputs it as a full
pathname.

The script also handles explicit filename patterns (with no
*, ?, or "c">[] wildcards in the pattern) by using "c">test –x. To make Listing
Two
less complex, that part of the script isn’t shown.
You can get the complete script at "http://www.linux-mag.com/downloads/2007-01/power/script.txt"
class="story_link">http://www.linux-mag.com/downloads/2007-01/power/script.txt
.

To Be Continued…

Next month, we’ll finish this series with a look at the
Cygwin utilities.

Fatal error: Call to undefined function aa_author_bios() in /opt/apache/dms/b2b/linux-mag.com/site/www/htdocs/wp-content/themes/linuxmag/single.php on line 62