See README.txt as well. It tells you how to build brash from the source code. Updated: 05/20/2016 BRASH ===== Brash is a bourne shell clone written using threads instead of fork/exec. It has many bash-like features, particularly the keyboard handling, and will eventually evolve to become more like bash. Brash has a variety of unix commands built into it as well. These commands always begin with a leading '.' character to let you know they are not exact duplicates of the unix command by the same name. Version 2.1.0 lacks the following bash features: * the tilde character, "~", when used as the leading part of a filename, works -- but only for the current user. The ~username/ syntax is not supported. * the keyboard handling lacks the depth of bash's configurability, but is fairly full featured. See "Console Window Edit Keys", below. * the set command is very limited. Use the command "builtins" to get a list of what is supported. * shopt * bg ( the & operator works to execute commands in the background ) * caller * enable * env only lets you list the environment, not manipulate it * exec * help * history (instead use fc -l range...) * the jobs command is very limited * local * mapfile ( but read -f can simulate ) * printf * readarray -- use read -a instead * readonly * set has limited options * suspend * test ( though [ expression ] does work ) * times * trap * typeset * umask * ulimit * wait Brash understands the Windows file naming convention and lets you use it they it was meant to be used -- in a case insensitive manner. It also interprets forward slash in file names as if they were two backlashes -- especially when you use the Tab-completion feature of the console. For help, use "brash -h". WHY BRASH ========= Of course, you could just use CYGWIN. But the source code for GNU bash requires the full GNU unix simulation of fork/exec and all the libraries and dlls to install. Brash is a single executable with no other libraries and tools to install. To install brash, you technically only need the brash.exe file. But in practice, having a well thought out .brashrc file will be immensely helpful. Brash also includes a few builtin-commands, see below, that allow you to use linux-like directory listings, file printing, etc: ".ls", ".cat", ".regex". Brash's console reader, though which you enter commands, understands the nature of both unix style file names and microsoft windows filenames. And since you are on windows, the TAB-completion function generates microsoft filenames -- which contain backslashes instead of forward slashes. And since backslash is an escape character in brash, the directory separator is a double backslash to the tab-completion feature. This feature is not perfect, however. If you have directories with spaces in the names, it is not as perfect as would be desirable. Still it works most of the time, and does understand spaces in filenames starting with the current directory. Another way that brash reduces the need to download the entirety of the GNU tools package is that it provides several new builtin commands that behave, in most key ways, like the very important unix command line tools that are often used in writing scripts. See "DIFFERENCES WITH BASH / BOURNE SHELLS". The source code for brash is available on the web and it is written in the normal multithreading style for Microsoft Windows. You can thus add your own builtin commands -- and additional syntaxes, if needed. COPYRIGHT ========= Brash is mostly copyrighted with my personal copyright, but parts of it are covered by the GNU General Public License as published by the Free Software Foundation version 2. This copyright applies to 3 things: GNU regex.c and .h GNU fnmatch.c GNU parse_date.c My copyright is as follows: Copyright 2002-2015, Lowell Boggs Jr. This file or directory, containing source code for a computer program, is Copyrighted by Lowell Boggs, Jr. 987 Regency Drive, Lewisville TX (USA), 75067. You may use, copy, modify, and distribute this source file without charge or obligation so long as you agree to the following: 1. You must indemnify Lowell Boggs against any and all financial obligations caused by its use, misuse, function, or malfunction. Further, you acknowledge that there is no warranty of any kind, whatsoever. 2. You agree not to attempt to patent any portion of this original work -- though you may attempt to patent your own extensions to it if you so choose. 3. You keep this copyright notice with the file and all copies of the file and do not change it anyway except language translation. You are responsible for enforcing your own compliance with these conditions and may not use this source file if you cannot agree to the above terms and conditions. Other Copyrights include: Copyright David L. Nugent, 1996 -- for parse_date.c * http://www.gnu-darwin.org/sources/src/usr.sbin/pw/psdate.c Copyright WikiBooks.org: LCS Algorithm used in .diff * https://en.wikibooks.org/w/index.php?title=Algorithm_Implementation/Strings/Longest_common_subsequence BRASH BEHAVIOR ============== Brash only reads .brashrc on startup, not .profile or .bashrc. The .brashrc file must be placed in the $USERPROFILE directory. If ever ported to linux or unix, the file will be placed in the $HOME directory. An example .brashrc should come with your brash.exe executable. At this time, brash only runs on Microsoft Windows but is not fundamentaly different than a program that could easily execute on Linux or unix. In fact, brash is designed using Unix programming techniques as much as is possible, but implemented using Microsoft Windows environment. The .brashrc file can be used to suppress the introductory banner by definiting the variable, BRASH_NO_BANNER,to any non-empty value. DIFFERENCES WITH BASH / BOURNE SHELLS ------------------------------------- Brash will evolved to be very similar to bash but at this time it is like a cross between bash and bourne with a few extras, limitations and gotchas. Command Line Differences ------------------------ Use "brash -h" get help on the brash command line processing. The "-c" option is interpreted the same way that brash interprets it but other than that, things are much different on the command line. Syntax limitations ------------------- Brash's syntax is as close to the bourne shell's as possible, but there are some obvious limitations: "if" and "while" statements accept only 1 command as the condition, not multiple as bash does. In bash and the bourne shell, multiple command condition statements are allowed -- although this feature seems rarely used. In brash, when multiple command statements must be used to comprise the condition, the group of statements must be surrounded by curly braces in order to approximate the bash/bourne behavior. Bash example: if command 1 ; command 2 ; command 3; then ... fi Brash example if { command 1 ; command 2 ; command 3; } then ... fi To be clear: If your condition has only one command, then you do not need the curly braces. Command line interaction ------------------------ Brash keyboard interaction is very much like bash's, but it does not have a "vi" mode, and ^V does not currently do anything. Sending escape sequences to terminals and printers is not the way things are typically done windows. See "Console Window Edit Keys", below. New builtin commands -------------------- Brash also includes some builtin commands that mimic common unix command line tools: brash | | builtin | | command | unix command | Notes ============+==================+========================================= | | builtins | man brash | Get a list of builtin commands | | basename | basename | same as unix, understands MS paths | | dirname | dirname | same as unix, understands MS paths | | sleep | sleep | sleeps for a number of seconds. parm is float. | | .attrib | chmod | Uses the windows command syntax, but isn't | | recursive. See -h. | | .ls | ls | The key features, but not an exact copy | | .regex | grep/sed/nl | The key features of each | | .cat | cat | The key features | | .rm | rm | Not recursive, and won't delete directories | | .stat | basename, | Print info about pathnames, including, | dirname, | file size, time, etc. Designed for use | stat | with the eval command to get info about | | files into script variables. | | .head | head | print lines at the begining of files | | .cut | cut | print fields / columns from files | | .expand | expand | expands tabs in files or compresses spaces | unexpand | to tabs | | .date | date | print the date in a user specified format | | .diff | diff | compare files and print the minimal change | | set that would convert the first file to the | | second. | | .wc | wc | Word count | | .stopwatch | ? | saves, prints, compares the current time. | | .solve | solve | solves sets of simultaneous linear equations | | using guassian elimination. | | .linreg | ? | linear regression, prediction. | | .uniq | uniq | given sorted input selects uniq, or duplicated lines. Use the -h option to these new commands for more information. Note that are several builtin commands whose names begin with . which are not described above. Use builtins to get the list. Pipe Operator ------------- One fundamental difference is the pipe operator: in Brash, the right most pipe member is executed in the current shell not a subshell. So if you want to truly duplicate bourne's syntax, all the brash pipe's would have be written like this: ( cmd1 | cmd2 | cmd3 ... | cmdN) Instead of just cmd1 | cmd2 | cmd3 ... | cmdN Because, for brash, cmdN is executed in the current shell not a subshell. While this makes an important difference, requiring review of all scripts, it has the signficant advantage of allowing a pipe to populate the current shell's environment variables with data produced in pipe. Consider the following pipe. In bourne shell, it is useless: echo a | read b In bourne, the "echo a" and the "read b" are done in sub-shells, but in brash, the variable b will become populated with 'a'. For example: type zip | while read zip is location In bourne shell (and bash) location is undefined, but in brash, location is now populated with the pathname of zip.exe (on windows). Given that this is not supposed to work in bourne, and the fact that it would be very confusing to write a script that set location before executing the above "type zip" example, it seems unlikely that this minor change in program logic will actually result in signifcant portability issues. (Of course, I have been mistaken before -- and will be again.) For and Select statements ------------------------- At this time, the for and select statements require the "in" operator. WRITING SCRIPTS =============== Writing brash scripts is essentially the same as writing bash scripts. To create a script, create a file in your PATH somewhere and you have two choices: * name the file command.brash * name the file just command If the file's name ends in .brash, brash will find it in your path when you execute a command linke this: command args If you chose not to use the .brash extension, you must add a special command at the top of the file: #!/usr/bin/brash Actually, you don't need the /usr/bin/brash part. Just starting the file with #! is sufficient, but it is customary to use the string as shown. This approach was used so that there would be at least some chance that existing bash/bourne scripts will work. To find the contents of the last parameter to the current command you can do something like this: scriptName() { : "$@" # do nothing to th all the parameters echo $_ is the last parameter } The colon command does nothing EXCEPT set the last parameter variable. Bash does this too. DOH STUPID ... =============== This is a list of brash annoyances 0. On windows, of course, directory nodes are separated by backslash characters. This is painful to work with of course in bourne style script language interpreters because the backslash characters serves the all important role of "escaping" the character that follows it -- thus protecting it from being interpreted by brash. There are some helpful work arounds to this profound annoyance: * When you press the tab key to complete a partial file name, the CommandConsole readLine function turns your /'s into pairs of \'s unless your string begins with apostrophe, in which case it only replaces it with the 1. * Get in the habit of using two backslashes to separate pathnames (unless you are in an apostrophe quoted string... sigh) * Use the apostrophe character, habitutally, to surround all file names. Apostrophe doesn't recognize \ slashes as an escape character. * Learn to use the string substitution feature of the shell variable expansion to programatically convert the / to \, or \\ to single, as needed. For example: ${var//\//\\} # in var, replace all / with \ $(var//\\\\/\\} # replace all cases where 2 backslashes are found with 1 backslash # may to do do this multiple times to get all repetitions in a string 1. On windows, sometimes file or directory names have odd UTF16 encodings and as a result the sort program gets confused unless someone fixes the text for it. This means when you do this: for f in * ; do echo $f ; done | sort It will usually work, but not always. If the file/directory names have non-trivial utf16 encodings, sort just hangs. Thanks Microsoft for putting off the work of fixing your own program on your own operating system to someone else -- and not also for not providing an option to just sort in ascii. brash reads the strings directly from the windows function and writes them directly to stdout, and thus some sort of hang occurs in sort when a strangely encoded name is found. I personally just created a sort function and put it in my $HOME/.brashrc file: function .sortHelper ( declare -A map while read line do curval="${map[$line]}" if [ "$curval" = "" ] then map["$line"]=1 else let count="${map["$line"]}" let count=++count map["$line"]=$count fi done for f in "${!map[@]}" do count="${map[$f]}" if [ "$count" = "" ] then echo "bad count at f='$f'" echo "map elements are: " for g in "${!map[@]}" do echo -e "\t[$g]" done break fi while (( count > 0 )) do echo "$f" let --count done done ) function sort { if [ $# = 0 ] then .sortHelper else .cat "$@" | .sortHelper fi } This script conflates the utf encoded text to the normal ascii form -- leaving out the umlauts and other curlycues. See ../bin/utf8dec.cxx for the source. It gets built automatically. Of course, you can't use this sort alias if you really do have utf encoded data to work with. 2. Many standard Windows commands are actually implemented as part of the cmd.exe program. Here's a list of aliases that are likely to be needed in your .brashrc alias cls='cmd /c cls' alias rm='cmd /C rm' alias mkdir='cmd /C mkdir' alias rmdir='cmd /C rmdir' The del command is particularly picky about backslash vs forward slash, so here is a script that works better with it: del() ( for f in "$@" do f1="${f//\//\\}" # convert forward slash to backslash file="${f1//\\\\/\\}" # convert multiple backslash into single # backslash cmd /c del "$file" done ) The attrib command is implementable like this: alias attrib='.attrib' See the next item for an explanation of why. 3. In releases 01.*.*, if-statements and while-statements do not allow multiple commands in the if clause. But you can use {} or () to group multiple statements into one for this purpose. 4. In releases 01.*.*, arithmetic operators are limited to + - * / % > < == >= <= ^ & | && || 5. Drive letters as commands In windows, drive letters are used as commands to switch to the drive. For example, E: This command makes drive E the current drive and the current directory on drive E as the current directory. Brash does not directly support this behavior because for brash, there is only a current directory. There is no separate concept of a current drive. You could hard code, in your .brashrc file, a bunch of helper functions: function a: { pushd a:/ } function b: { pushd b:/ } But making 26 of them creates quite a clutter in your .brashrc file. Here is a code fragment that defines only the needed function: # # The following code creates functions for all mounted file systems # # These functions are named: a:, b:, c:, p:, q:, etc # c:() { pushd c:/ ; } net use | .regex -m '[A-Z]:' | while read status local rest do # fix up missing status case "$status" in *:) # status was empty # slide the other fields over 1 # and put unknown in the status rest="$local $rest" local="$status" status="unknown" ;; esac case "$status" in OK) echo "${local,,}() { echo pushd $local ; pushd $local/; }" ;; esac done | read -f cmd # # cmd now holds a list of function definitions that create only the needed # drive letter commands # eval "$cmd" unset cmd ENVIRONMENT VARIABLES TO USE AND SET ==================================== BRASH_USER_HOME -- either $HOME on unix, or %USERPROFILE% on Windows BRASH_SHELL -- the name of this executable BRASH_VERSION -- the current version of this executable BRASH_PATH_SEPARATOR -- ';' or ':' depending on the os BRASH_DIR_SEPARATOR -- / or \\ depending on the os BRASH_SYSROOT -- either C:\\ or / depending on the os BRASH_OS -- Either MSWindows or unix depding on the os BRASH_USER -- Either %USERNAME% or your unix user id BRASH_HOSTNAME -- Either %COMPUTERNAME or your unix hostname BRASH_NO_BANNER -- suppress 'welcome screen' at the console PS1 -- the text of the command prompt. The following special character sequences are interpreted: \w: The name of the current directory \W: The basename of the current directory \s: The basename of the brash executable that you are running. Usually brash.exe, \u: the current user name \h: The first node of the hostname. e.g. "node" in node.domain.company.com \H: The full hostname \#: The command history size. CONSOLE WINDOW COMMAND KEYS =========================== Console Window Edit Keys: ^C -- user requests that everything stop HOME -- go to the beginning of the current input line ^A -- ditto END -- go to the end of the current input line ^E -- ditto RightArrow -- move one position to the right in the current line LeftArrow -- move one position to th eleft in the current line ^K -- delete the rest of the line ^W -- move the cursor to the next word in the current line ^Q -- move the cursor to the previous word in the current line UpArrow -- display the previous line from the command history DownArrow -- display the next line from the command history Backspace -- delete the previous character Delete -- delete the current character TAB (^I) -- 'tab completion': find the a file that matches the word to the left of the cursor Console handler bugs: Sometimes control-C doesn't properly display the command prompt.