Bash & Windows
As far as I'm concerned, the death of DOS batch files has been greatly exaggerated. Running a batch file is still the only way to perform a series of actions without the help of either third-party software or a macro-running behemoth like Word, Excel or the Windows Scripting language. Fortunately, the GNU development tools - including bash - have been ported to MS-DOS and Windows systems. This page shows you how to hook bash into the Windows operating system. So you'll be familiar with bash once Linux breaks through ;-)
Freddy Vulto
Aug 28, 2001: | Added Cygwin configuration. |
Aug 17, 2001: | Upgraded to DJGPP bash v2.04. Added DJGPP versus Cygwin comparison. Added NedStat counter. |
Feb 26, 2001: | Updated personal FAQ. |
Feb 18, 2001: | Usage of HTML::EmbPerl & HTML::Toc to generate this page. |
Nov 26, 2000: | Added Vim paragraph. Updated bash configuration after downloading bash 2.03. Modified paragraph layout. |
Jul 18, 2000: | Changed paragraph 'DOS-box & Start;Run' to use 'App Paths' registry key. Changed DJGPP location in examples to D:\Programs\DjGpp. |
Mar 25, 2000: | Moved page location. Updated 'Shell to DOS' for Windows 98. Added shell script 'Create .zip file'. |
Apr 18, 1999: | Added section 'Shell Scripts'. |
Apr 17, 1999: | Modified layout. Updated section 'Shell Script Automation'. |
Bash, short for GNU Bourne-Again SHell, is an sh-compatible command language interpreter that executes commands read from the standard input or from a file. Bash is a part of the GNU software collection, utilizing this collection to reach its full potential. The GNU software is an essential part of the Linux operating system, but is also available on the Windows operating system as it's being ported by either Cygwin or DJGPP.
Both the DJGPP and Cygwin project are offering a complete 32-bit C/C++ development system, based on the GNU development tools. The projects differ in the way they handle the connection with the operating system and in their licensing. DGJPP compiles the applications as DPMI (32-bit DOS) applications which can't access the Win32 API. Software created with DJGPP's libc is licensed under GNU Lesser GPL. Cygwin compiles applications as Win32 applications, utilizing a DLL which acts as a UNIX emulation layer. Software created with Cygwin's libc must be distributed as Free Software.
Choose a distribution, or install both. I suggest Cygwin because its Win32 API access allows you to run even more useful GNU tools - like wget and lynx - from within a DOS box. See Cygwin as the successor of DJGPP.
will contain a link to a downloadable setup.exe
which will
guide you through the process of downloading and installing Cygwin. Take
care when installing everything: I had to clean up my harddisk to make room
for the 250 MB.
will contain all the links to download sites. You can download sources (these files end with 's'), binaries (these files end with 'b') as well as documentation (these files end with 'd'). I haven't done any compilation myself and just downloaded:
bsh204b.zip
which is basically bash. To make full use of the bash shell scripting language, I also downloaded:
fil40b.zip (ported UNIX file utilities: ls, rm, touch, ln, etc.) txt20b.zip (ported UNIX text utilities: cat, sort, grep, wc, etc.)
The version numbers of these zip files may have changed over time: just download the files with the highest number.
command.com
. In order to meet bash' rather large demand of environment space, it's often necessary to enlarge this space by specifying /e:<number>
to command.com
. Although this can be done by putting SHELL=
in config.sys
, I dislike messing with autoexec.bat
and config.sys
, so I'm starting bash from a preliminary bath file cygwin.bat
which enlarges environment space. After the environment space is enlarged, a second batch file _cygwin.bat
is called:
D:\Programs\Cygwin\cygwin.bat -> D:\Programs\Cygwin\_cygwin.bat -> bash.exe
cygwin.bat
contains the following commands:
@echo off REM --- cygwin.bat ------------------------------------------------------------ REM function: Shell command.com in order to start Cygwin. REM args: 1-9: bash shell arguments REM Shell command.com with enough environment space and start _cygwin.bat command.com /e:1024 /k D:\Programs\Cygwin\_cygwin.bat %1 %2 %3 %4 %5 %6 %7 %8 %9
The option /e:1024
gives me 4 times as much environment space as standard command.com
which has the environment space set to 256 bytes. If you ever receive the
error "Out of environment space", you're free to increase this number. The
option /k _cygwin.bat
tells command.com
to start the batch file _djgpp.bat
immediately after command.com
is initialized. The arguments %1
, %2
, etc. will pass optional arguments (e.g. shell scripts) to bash.
@echo off REM --- _cygwin.bat ----------------------------------------------------------- REM function: Start Cygwin REM args: - 1..9: bash shell arguments D:\Programs\Cygwin\bin\bash --login -i %1 %2 %3 %4 %5 %6 %7 %8 %9 exit
/etc/profile
contains global - for all users - bash initialization commands. Make sure to remove the cd $HOME
command, otherwise the Shell to Cygwin won't go to the current directory. My /etc/profile
currently looks like this:
#--- /etc/profile ------------------------------------------------------------- # function: Global bash initialization PATH="/usr/local/bin:/usr/bin:/bin:$PATH" unset DOSDRIVE unset DOSDIR unset TMPDIR unset TMP # NOTE: I'm not using the 'id' command since this will return my Windows # login name 'Freddy Vulto' with a space in it, which I'd like to # avoid. See Cygwin FAQ. #USER="`id -un`" USER=FVu # Set up USER's home directory if [ -z "$HOME" ]; then HOME="/home/$USER" fi if [ ! -d "$HOME" ]; then mkdir -p "$HOME" fi export HOME USER for i in /etc/profile.d/*.sh ; do if [ -f $i ]; then . $i fi done export MAKE_MODE=unix export PS1='\[\033]0;\w\007 \033[32m\]\u@\h \[\033[33m\w\033[0m\] $ ' # Execute commands from ~/.bashrc if file exists test -f ~/.bashrc && . ~/.bashrc
.bashrc
for Cygwin bash currently looks like this:
# Set vi editing mode set -o vi
The bash shell runs on top of command.com
. In order to meet
bash' rather large demand of environment space, it is necessary to enlarge
this space by specifying /e:<number> to command.com
. Although this can be done by putting SHELL= in config.sys
, I dislike messing with autoexec.bat
and config.sys
, so I'm starting bash from a preliminary batch file djgpp.bat
which enlarges environment space. After the environment space is enlarged, a second batch file _djgpp.bat
is called. This file sets a few environment variables - necessary for bash to start - and finally calls bash:
D:\Programs\DjGpp\djgpp.bat -> D:\Programs\DjGpp\_djgpp.bat -> D:\Programs\DjGpp\bin\bash.exe
Bash.bat contains the following commands:
@ECHO OFF REM --- djgpp.bat ------------------------------------------------------------- REM function: Shell command.com in order to start bash REM Shell command.com with enough environment space and start _djgpp.bat command.com /e:1024 /k D:\Programs\DjGpp\_djgpp.bat %1 %2 %3 %4 %5 %6 %7 %8 %9
The option /e:1024 gives me 4 times as much environment space as standard command.com
which has the environment space set to 256 bytes. If you ever receive the
error "Out of environment space", you're free to increase this number. The
option /k _djgpp.bat
tells command.com to start the batch file _djgpp.bat
immediately after command.com
is initialized. The arguments %1
, %2
, etc. will pass optional arguments (e.g. shell scripts) to bash.
The file _djgpp.bat
sets the environment settings, necessary for bash to start, and finally starts bash:
@ECHO OFF REM --- _djgpp.bat ------------------------------------------------------------ REM function: Setup environment for bash shell. Call bash.exe REM args: - 1..9: bash shell arguments REM Set DjGpp home directory (~) SET HOME=D:\Programs\DjGpp REM Set DjGpp environment file SET DJGPP=%HOME%\djGpp.env REM Set path to DjGpp SET PATH=.;%HOME%\Bin;C:\Win98\Command REM Start bash %HOME%\bin\bash %1 %2 %3 %4 %5 %6 %7 %8 %9 exit
The file _bashrc
may contain commands which are executed by bash on startup. Mine looks currently like this:
# Show path to vi (vim.exe renamed) alias vi=D:/Programs/Vim/Vim58/vi.exe # Display ls with colors alias ls='ls --color' # Set prompt PS1='${PWD}$' # Set command line editing mode to vi set -o vi # Add current directory to path PATH=".;$PATH" # Add either 'ActiveState' or 'DJGPP' perl directory to path # Does '~/bin/perl.exe' exists? if [ ! -s "~/bin/perl.exe" ] ; then # No, '~/bin/perl.exe' doesn't exist; # Add path to ActivateState Perl PATH="$PATH;D:/Programs/Perl/Bin" fi # Set temp directories TMP='/tmp' TEMP='/tmp'
In order to be able to type either 'cygwin' or 'djgpp' from the Start;Run window, modify the registry like this:
HKEY_LOCAL_MACHINE |-Software |-Microsoft |-Windows |-CurrentVersion |-App Paths |-cygwin.exe D:\Programs\Cygwin\cygwin.bat
HKEY_LOCAL_MACHINE |-Software |-Microsoft |-Windows |-CurrentVersion |-App Paths |-djgpp.exe D:\Programs\DjGpp\djgpp.bat
In order to be able to run 'bash' from the DOS-box, add a batch file to the properties of the DOS-box via {Properties, tab 'Program', Batch file}, and let this batch file add the bash directory to the path:
PATH=D:\Programs\DjGpp;%PATH%
It would be nice if we could jumpstart bash from a specific folder name
by selecting so from the folder's right-mousebutton-menu. Fortunately, Windows
9x offers us the capabilities. Add the following keys to the registry. 3.3.1 Shell to Cygwin
HKEY_CLASSES_ROOT |-Folder |-shell |-Shell to Cygwin (Default) = "Shell to Cyg&win" |-command "D:\Programs\Cygwin\cygwin.bat"
HKEY_CLASSES_ROOT |-Folder |-shell |-Shell to DJGPP (Default) = "Shell to DJ&GPP" |-command "D:\Programs\DjGpp\djgpp.bat"
And while your at it:
|-Shell to DOS |-command "C:\command.com /k cd %1" (Windows 95) "C:\command.com" (Windows 98) "C:\WinNt\cmd.exe" (Windows NT)
It would be nice if shell scripts could be run by doubleclicking on them or by specifying them via {Start; Run}. Also it would be convenient if shell scripts could be editted by selecting so from the popup menu. These are the necessary registry items to accomplish all this, choose either Cygwin or DJGPP:
HKEY_CLASSES_ROOT |-.sh "Bash.Shell.Script" : |- Bash.Shell.Script |-shell |-edit "&Edit" | |-command "notepad.exe "%1"" |-open "&Run" |-command "D:\Programs\Cygwin\cygwin.bat "%1" "%2" "%3" "%4" "%5" "%6" "%7" "%8" "%9""
HKEY_CLASSES_ROOT |-.sh "Bash.Shell.Script" : |- Bash.Shell.Script |-shell |-edit "&Edit" | |-command "notepad.exe "%1"" |-open "&Run" |-command "D:\Programs\DjGpp\djgpp.bat "%1" "%2" "%3" "%4" "%5" "%6" "%7" "%8" "%9""
The C++ IDE of Microsoft's Developer Studio offers you the ability to
execute 'Custom Build', 'Pre-link step' or 'Post-build step' commands. These
can be bash commands as well. To show the standard output of bash in the
'Output' window of DevStudio, you have to redirect normal output to stderr,
using redirection symbol '>&2'. For example, suppose you want to
execute the script C:\hello.sh
as a Post-build step:
#!/bin/sh #--- hello.sh --------------------- echo "Hello World!" >&2
Start Developer Studio, select {Project;Settings}, go to the rightmost tab 'Post-build step' and type as 'Post-build command(s)':
D:\Programs\DjGpp\djgpp.bat C:\hello.sh
To set bash as the default shell of your GUI Vim, put the commands underneath in your _gvimrc
file. Now you can type :sh[ell]
to invoke bash, or :r !ls
to insert the contents of the current directory, or :make
to make your project.
" Set shell to Cygwin bash set shell=D:\\Programs\\Cygwin\\cygwin.bat " Shell arguments; -c: argument set shellcmdflag=-c " Shell redirect; copy stderr to stdout set shellredir=>%s\ 2>&1 " Shell pipe; copy stderr to stdout set shellpipe=2>&1\|\ tee " Shell quote; embed command within quotes set shellxquote=\"
set shell=D:\\Programs\\DjGpp\\djgpp.bat set shellcmdflag=-c set shellredir=>%s\ 2>&1 set shellxquote=\"
The following shell script creates a .zip file using the PKZIP command line utility, version 2.50, from PKWARE Inc.
#!/bin/sh # --- makeZip.sh -------------------------------------------------------------- # function: Create archive file containing the contents of the MsxC directory. # credits: FVu, Mar-2000 # Name zip destination file ls_ZipFile="../Zip/msxC-1.20.zip" # Does zip file already exist? if [ -e ${ls_ZipFile} ] ; then # Yes, zip file already exists; # Delete it rm $ls_ZipFile fi # Zip pkzip $ls_ZipFile -add "../*" | tail --lines=+6 pkzip $ls_ZipFile -add -dir=specify "../Batch/*" | tail --lines=+6 pkzip $ls_ZipFile -add -dir=specify "../Include/*" | tail --lines=+6 pkzip $ls_ZipFile -add -dir=specify "../Sample/*" | tail --lines=+6 pkzip $ls_ZipFile -add -dir=specify "../Src/*" | tail --lines=+6
(credits: Pontus Goffe)
This shell script determines the current operating system (Windows 95 or Windows NT) bash is running from:
THISOS="Unknown"; export THISOS; WINACTIVE="NO"; export WINACTIVE if test -s $windir/command.com; then $windir/command.com "/c" "ver" | grep "Windows 95" > nul if test !$?; then THISOS="Windows_95"; export THISOS; fi if test -s $windir/command/mem.exe; then $windir/command/mem.exe /c | grep "WIN" > nul if test !$?; then WINACTIVE="YES"; else WINACTIVE="NO"; fi export WINACTIVE fi fi if test -s $SYSTEMROOT/system32/cmd.exe; then $SYSTEMROOT/system32/command.com /c ver | grep "Windows NT" > nul if test !$?; then THISOS="Windows_NT"; export THISOS; WINACTIVE="YES"; export WINACTIVE; fi fi if test $THISOS = "Windows_95"; then if test $WINACTIVE = "YES"; then echo Welcome to bash under $THISOS, Windows is running else echo Welcome to bash under $THISOS, Windows is not running fi elif test $THISOS = "Windows_NT"; then echo Welcome to bash under $THISOS else echo Welcome to bash under unknown OS fi
To create a tar
archive file, compressed with gzip
, I use the following command:
tar --create --gzip t.txt --file=t.tar.gz
or for short:
tar -cz t.txt > t.tar.gz
After installing a new package x, use the following command to add the info files of the x package to the info main menu:
install-info x.info dir
The following test didn't work:
if [ -e "~/_djgpp.bat" ] ; then echo Yes; fibecause for tilde expansion the string has to be unquoted:
if [ -e ~/_djgpp.bat ] ; then echo Yes; fior even better, as the $HOME directory was what I wanted:
if [ -e $HOME/_djgpp.bat ] ; then echo Yes; fiSee chapter 'Tilde expansion' in bash documentation for more information.
Using the Pod::Usage
function pod2usage()
gave me the error:
/usr/bin/pod2man: not found
This happened after I'd put CYGWIN=ntea
into my .bashrc
for testing purposes. Removing CYGWIN=ntea
from my .bashrc
made pod2man
work all right again.