📄 bcsh.sh
字号:
# 1-Feb-86 09:37:35-MST,30567;000000000001# Return-Path: <unix-sources-request@BRL.ARPA># Received: from BRL-TGR.ARPA by SIMTEL20.ARPA with TCP; Sat 1 Feb 86 09:36:16-MST# Received: from usenet by TGR.BRL.ARPA id a002623; 1 Feb 86 9:33 EST# From: chris <chris@globetek.uucp># Newsgroups: net.sources# Subject: Improved Bcsh (Bourne Shell Cshell-Emulator)# Message-ID: <219@globetek.UUCP># Date: 30 Jan 86 17:34:26 GMT# To: unix-sources@BRL-TGR.ARPA## This is a new, improved version of my Bourne shell cshell-emulator.# The code has been cleaned up quite a bit, and a couple of new features# added (now supports 'noclobber' and 'iclobber' variables). A bug with# 'eval' that caused "illegal I/O" error messages on vanilla V7 shells has# also been fixed.# I have posted the program in its entirety because a context diff of the# old and new versions was longer than the new version...# --Chris# Bcsh -- A Simple Cshell-Like Command Pre-Processor For The Bourne Shell## "Copyright (c) Chris Robertson, December 1985"## This software may be used for any purpose provided the original# copyright notice and this notice are affixed thereto. No warranties of# any kind whatsoever are provided with this software, and it is hereby# understood that the author is not liable for any damagages arising# from the use of this software.## Features Which the Cshell Does Not Have:# ----------------------------------------## + command history persists across bcsh sessions# + global last-command editing via 'g^string1^string2^' syntax# + edit any command via $EDITOR or $VISUAL editors# + history file name, .bcshrc file name, alias file name, and number# of commands saved on termination can be set by environment variables# + prompt may evaluate commands, such as `pwd`, `date`, etc.# + the whole text of interactive 'for' and 'while' loops and 'if'# statements goes into the history list and may be re-run or edited# + multiple copies of commands and requests to see command history# are not added to the history list# + the history mechanism actually stores all commands entered in a# current session, not just $history of them. This means that you# can increase $history on the fly and at once have a larger history.### Synonyms:# ---------## logout, exit, bye write out history file and exit# h, history show current history list# # # Aliases:# --------## alias NAME CMND create an alias called NAME to run CMND# unalias NAME remove the alias NAME## There are no 'current-session only' aliases -- all alias and unalias# commands are permanent, and stored in the $aliasfile.## If an alias contains positional variables -- $1, $2, $*, etc. -- any# arguments following the alias name are considered to be values for# those variables, and the alias is turned into a command of the form# 'set - arguments;alias'. Otherwise, a simple substitution is performed# for the alias and the rest of the command preserved. The cshell# convention of using '\!:n' in an alias to get bits of the current# command is mercifully abandoned.## Quotes are not necessary around the commands comprising an alias;# in fact, any enclosing quotes are stripped when the alias is added# to the file.## A couple of typical aliases might be:## goto cd $1;pwd# l ls -F## Note that aliasing something to "commands;logout" will not work -- if# you want something to happen routinely on logout put it in the file# specified by $logoutfile, default = $HOME/.blogout.### Command Substitutions:# ----------------------## !! substitute last command from history list# !!:N substitute Nth element of last command from# history list -- 0 = command name, 1 = 1st arg# !!:$ substitute last element of last command from# history list# !!:* substitute all arguments to last command# from history list# !NUMBER substitute command NUMBER from the history list# !NUMBER:N as above, but substitute Nth element, where# 0 = command name, 1 = 1st arg, etc.# !NUMBER:$ as above, but substitute last element# !NUMBER:* as above, but substitute all arguments# !-NUMBER substitute the command NUMBER lines from the# end of the history list; 1 = last command# !-NUMBER:N as above, but substitute Nth element, where# 0 = command name, 1 = 1st arg, etc.# !-NUMBER:$ as above, but substitute last element# !-NUMBER:* as above, but substitute all arguments# !?STRING substitute most-recent command from history list# containing STRING -- STRING must be enclosed in# braces if followed by any other characters# !?STRING:N as above, but substitute Nth element, where# 0 = command name, 1 = 1st arg, etc.# !?STRING:$ as above, but substitute last element # !?STRING:* as above, but substitute all arguments### Command Editing:# ----------------## CMND~e edit CMND using $EDITOR, where CMND may be found# using a history substitution# CMND~v edit CMND using $VISUAL, where CMND may be found# using a history substitution# " ^string1^string2^ substitute string2 for string1 in last command"# command and run it# " g^string1^string2^ globally substitute string2 for string1 in "# last command and run it# !NUMBER:s/string1/string2/# substitute string2 for string1 in# command NUMBER and run it# !NUMBER:gs/string1/string2/# globally substitute string2 for string1 in# command NUMBER and run it# !?STRING:s/string1/string2/# substitute string2 for string1 in last command# containing STRING and run it# !?STRING:gs/string1/string2/# globally substitute string2 for string1 in last# command containing STRING and run it# # Any command which ends in the string ":p" is treated as a normal# command until all substitutions have been completed. The trailing# ":p" is then stripped, and the command is simply echoed and added to# the history list instead of being executed.## None of the other colon extensions of the cshell are supported.### Shell Environment Variables:# ----------------------------## EDITOR editor used by ~e command, default = "ed"# VISUAL editor used by ~v command, default = "vi"# MAIL your system mailbox# PAGER paging program used by history command, default = "more"# PS1 primary prompt# PS2 secondary prompt# history number of commands in history list, default = 22# histfile file history list is saved in, default = $HOME/.bhistory# savehist number of commands remembered from last bcsh session# aliasfile file of aliased commands, default = $HOME/.baliases# logoutfile file of commands to be executed before termination# inc_cmdno yes/no -- keep track of command numbers or not# noclobber if set, existing files are not overwritten by '>'# iclobber if both noclobber and iclobber are set, the user is# prompted for confirmation before existing files are# overwritten by '>'## Note: if you are setting either noclobber or iclobber mid-session,# set them to 'yes'### Regular Shell Variables:# ------------------------## Shell variables may be set via Bourne or cshell syntax, e.g., both# "set foo=bar" and "foo=bar" set a variable called "foo" with the value# "bar". However, all variables are automatically set as environment# variables, so there is no need to export them. Conversely, there# are NO local variables. Sorry, folks.## A cshell-style "setenv" command is turned into a regular "set" command.### The Prompt:# ----------## You may, if you wish, have a command executed in your prompt. If# the variable PS1 contains a dollar sign or a backquote, it is# evaluated and the result used as the prompt, provided the evaluation# did not produce a "not found" error message. The two special cases# of PS1 consisting solely of "$" or "$ " are handled correctly. For# example, to have the prompt contain the current directory followed# by a space, enter:## PS1=\'echo "`pwd` "\'## You need the backslashed single quotes to prevent the command being# evaluated by the variable-setting mechanism and the shell before it# is assigned to PS1.## To include the command number in your prompt, enter the command:## PS1=\'echo "$cmdno "\'### Shell Control-Flow Syntax:# --------------------------## 'While', 'for', 'case', and 'if' commands entered in Bourne shell# syntax are executed as normal.## A valiant attempt is made to convert 'foreach' loops into 'for' loops,# cshell-syntax 'while' loops into Bourne shell syntax, and 'switch'# statements into 'case' statements. I cannot guarantee to always get it# right. If you forget the 'do' in a 'while' or 'for' loop, or finish# them with 'end' instead of 'done', this will be corrected.## Note that cshell-to-Bourne control flow conversions do not take place# if control is nested -- e.g., a 'foreach' inside a 'while' will fail.## The simple-case cshell "if (condition) command" is turned into Bourne# syntax. Other 'if' statements are left alone apart from making the# 'then' a separate statement, because constructing a valid interactive# cshell 'if' statement is essentially an exercise in frustration anyway.# The cshell and Bourne shell have sufficiently different ideas about# conditions that if is probably best to resign yourself to learning# the Bourne shell conventions.## Note that since most of the testing built-ins of the cshell are# not available in the Bourne shell, a complex condition in a 'while'# loop or an 'if' statement will probably fail.# ## Bugs, Caveats, etc.:# --------------------## This is not a super-speedy program. Be patient, especially on startup.## To the best of my knowledge this program should work on ANY Bourne# shell -- note that if your shell does not understand 'echo -n' you# will have to re-set the values of '$n' and '$c'.## This program may run out of stack space on a 16-bit machine where# /bin/sh is not split-space.## Mail checking is done every 10 commands if $MAIL is set in your# environment. For anything fancier, you will have to hack the code.## Because commands are stuffed in a file before sh is invoked on them,# error messages from failed commands are ugly.## Failed history substitutions either give nothing at all, or a# "not found" style of error message.## A command history is kept whether you want it or not. This may be# perceived as a bug or a feature, depending on which side of bed you# got out on.## If you want a real backslash in a command, you will have to type two# of them because the shell swallows the first backslash in the initial# command pickup. This means that to include a non-history '!' in a# command you need '\\!' -- a real wart, especially for net mail,# but unavoidable.## Commands containing an '@' will break all sorts of things.## Very complex history substitutions may fail.## File names containing numbers may break numeric history sustitutions.## Commands containing bizzare sequences of characters may conflict# with internal kludges.## Aliasing something to "commands;logout" will not work -- if you# want something to happen routinely on logout, put it in the file# specified by $logoutfile, default = $HOME/.blogout.## Please send all bug reports to ihnp4!utzoo!globetek!chris.# Flames will be posted to net.general with 'Reply-to' set to your# ' path... :-) '#### ************* VERY IMPORTANT NOTICE *************## If your shell supports # comments, then REPLACE all the colon 'comments'# with # comments. If it does not, then REMOVE all the 'comment' lines from the# working copy of the file, as it will run MUCH faster -- the shell evaluates# lines starting with a colon but does not actually execute them, so you will# save the read-and-evaluate time by removing them.case "`echo -n foo`" in -n*) n= c="\c" ;; foo) n=-n c= ;; *) echo "Your 'echo' command is broken." exit 1 ;;esachistory=${history-22}savehist=${savehist-22}histfile=${histfile-$HOME/.bhistory}logoutfile=${logoutfile-$HOME/.blogout}EDITOR=${EDITOR-ed}VISUAL=${VISUAL-vi}PAGER=${PAGER-more}aliasfile=${aliasfile-$HOME/.baliases}# the alias file may contain 1 blank line, so a test -s will not workcase "`cat $aliasfile 2> /dev/null`" in "") doalias=no ;; *) doalias=yes ;;esacif test -s "${sourcefile-$HOME/.bcshrc}" then . ${sourcefile-$HOME/.bcshrc}fiif test -s "$histfile" then cmdno="`set - \`wc -l $histfile\`;echo $1`" cmdno="`expr \"$cmdno\" + 1`" lastcmd="`tail -1 $histfile`" copy=false ohist=$histfile while test ! -w "$histfile" do echo "Cannot write to history file '$histfile'." echo $n "Please enter a new history filename: $c" read histfile copy=true done if $copy then cp $ohist $histfile fielse cat /dev/null > $histfile cmdno=1 lastcmd=fi# keep track of command number as the defaultinc_cmdno=${inc_cmdo-yes}# default prompts -- PS1 and PS2 may be SET but EMPTY, so '${PS1-% }' syntax# is not used herecase "$PS1" in "") PS1="% " ;; esaccase "$PS2" in "") PS2="> " ;; esacexport histfile savehist history aliasfile EDITOR VISUAL PAGER cmdno PS1 PS2case "$MAIL" in "") ;; *) if [ -f $MAIL ]; then mailsize=`set - \`wc -c $MAIL\`;echo $1` else mailsize=0 fi ;;esactrap ':' 2trap exit 3trap "tail -$savehist $histfile>/tmp/hist$$;uniq /tmp/hist$$ > $histfile;\rm -f /tmp/*$$;exit 0" 15getcmd=yesmailcheck=exclaim=echoit=mailprompt=while :do run=yes case "$mailprompt" in "") ;; *) echo "$mailprompt" ;; esac case "$getcmd" in yes) : guess if the prompt should be evaluated or not case "$PS1" in \$|\$\ ) echo $n "$PS1$c" ;; *\`*|*\$*) tmp="`(eval $PS1) 2>&1`" case "$tmp" in *not\ found) echo $n "$PS1$c" ;; *) echo $n "$tmp$c" ;; esac ;; *) echo $n "$PS1$c" ;; esac read cmd || cmd="exit" ;; *) ;; esac case "$MAIL" in "") ;; *) : check for mail every 10 commands case "$mailcheck" in 1111111111) mailcheck= if [ -f $MAIL ]; then newsize="`set - \`wc -c $MAIL\`;echo $1`" else newsize=0 fi if test "$newsize" -gt "$mailsize"; then mailprompt="You have new mail" else mailprompt= fi mailsize=$newsize ;; *) mailcheck=1$mailcheck ;; esac ;; esac hist=no case "$cmd" in "") continue ;; sh) sh run=no ;; !!) cmd=$lastcmd echoit=yes getcmd=no continue ;; *:p) cmd="`expr \"$cmd\" : '\(.*\):p'` +~+p" getcmd=no continue ;; foreach[\ \ ]*) while test "$line" != "end"; do echo $n "$PS2$c" read line cmd="${cmd};$line" done echo "$cmd" > /tmp/bcsh$$ ed - /tmp/bcsh$$ << ++++ s/end/done/ s/foreach[ ]\(.*\)(/for \1 in / s/)// s/;/;do / w++++ ;; for[\ \ ]*|while[\ \ ]*) # try to catch the most common cshell-to-Bourne-shell # mistakes echo $n "$PS2$c" read line case "$line" in *do) line="do :" ;; *do*) ;; *) line="do $line" ;; esac cmd="${cmd};$line" while test "$line" != "done" -a "$line" != "end" do echo $n "$PS2$c" read line case "$line" in end) line=done ;; esac cmd="${cmd};$line" done echo "$cmd" > /tmp/bcsh$$ ;; if[\ \ ]*) while test "$line" != "fi" -a "$line" != "endif" do echo $n "$PS2$c" read line case "$line" in *[a-z]*then) line="`expr \"$line\" : '\(.*\)then'`;then" ;; endif) line=fi ;; esac cmd="${cmd};$line" done echo "$cmd" > /tmp/bcsh$$ case "`grep then /tmp/bcsh$$`" in "") # fix 'if foo bar' cases ed - /tmp/bcsh$$ << ++++ s/)/);then/ s/.*/;fi/ w++++ ;; esac ;; case[\ \ ]*) while test "$line" != "esac" do echo $n "$PS2$c" read line cmd="${cmd}@$line" done cmd="`echo \"$cmd\" | tr '@' ' '`" echo "$cmd" > /tmp/bcsh$$ ;; switch[\ \ ]*) while test "$line" != "endsw" do echo $n "$PS2$c" read line cmd="${cmd}@$line" done echo "$cmd" > /tmp/bcsh$$ ed - /tmp/bcsh$$ << '++++' 1,$s/@/\/g g/switch.*(/s//case "/ s/)/" in/ 1,$s/case[ ]\(.*\):$/;;\ \1)/ 2d 1,$s/endsw/;;\esac/ g/breaksw/s/// 1,$s/default.*/;;\ *)/ w++++ cmd="`cat /tmp/bcsh$$`" ;; *!*) hist=yes ;; esac case "$hist" in yes) # deal with genuine exclamation marks, go back and parse again case "$cmd" in *\>![\ \ ]*|*\\!*) cmd="`echo \"$cmd\" | sed -e 's@\\!@REALEXCLAMATIONMARK@g'`" exclaim=yes getcmd=no continue ;; esac # break command into elements, parse each one tmp= for i in $cmd do # find element with !, peel off stuff up to ! case "$i" in !) # most likely a typo for !!, so fix it front= $i=!! ;; !!*) front=
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -