Shell Programming
Ch Shell (ch) is a C compatible shell;
C Shell (csh) is a C-like shell.
Shell programming with scripts is effective
to automate repetitive tasks such as system administration,
regression test, and rapid prototyping.
The language syntax of
conventional Windows MS-DOS and Unix shells such as Bourne shell,
C shell, and Korn shell are awkward.
Similar to symbolic mnemonic forms of lower level assembly languages,
special meanings for clusters of metacharacters
in MS-DOS/Unix shells are difficult to remember.
Therefore, large shell scripts written in these MS-DOS/Unix shell programming languages
are hard to read and difficult to change and maintain. In addition, the power
for these MS-DOS/Unix shell programming languages is limited.
Like millions of hard-to-program VCRs blinking
in American living rooms,
only a fraction of the capabilities of these shell programming languages
are used by the majority of MS-DOS/Unix users.
Most users treat these MS-DOS/Unix shells as command interpreters;
their day-to-day programming tasks are handled
in C or other conventional programming languages.
Only the very unfortunate will have to read and write scripts in these conventional MS-DOS/Unix shells.
Ch is an interpretive implementation of C as Unix/MS-DOS shell.
ch can also be used as a login shell the same as Unix shells.
Ch bridges
the gap between the C language and Unix shell.
Unix shell commands,
such as vi, ls, mv, grep, find, awk, and sed can run in Ch in
both Windows and Unix, they are integrated into ch script command.
It is designed for both interactive command interpretation and
shell programming. Ch is a true VHLL for programming while it retains the
shell features such as command-line editing, completion, history, aliases etc.
When Ch is used in a single command mode,
most features of Ch are the same as conventional Unix shells
such as Bourne shell, C shell, and Korn shell, and MS-DOS shell in Windows.
However, since Ch is a superset of C,
shell scripts written in C and extended Ch shell programming features
are much more powerful, efficient and easy to maintain.
Ch Shell versus C Shell
In terms of shell programming,
Ch will be as
close to C shell as possible, but no closer.
Ch shell is a genuine C shell and portable in Windows
and Unix.
The semantics of history and alias features
in Ch are similar to C-shell.
But the syntax is different from C-shell because
Ch is a superset of C.
The following table highlights the syntax differences
between Ch shell and C shell, but with the same semantics.
| C Shell | Ch Shell |
| $#argv | _argc |
| $argv[*] | strjoin(" ", _argv[1], _argv[2], ..., _argv[_argc]) |
| $argv | strjoin(" ", _argv[1], _argv[2], ..., _argv[_argc]) |
| $* | strjoin(" ", _argv[1], _argv[2], ..., _argv[_argc]) |
| $argv[1-n] | strjoin(" ", _argv[1], _argv[2], ..., _argv[n]) |
| $0 | _argv[0] |
| $argv[n] | _argv[n] |
| $1 $2 ... $9 | _argv[1] _argv[2] ... _argv[9] |
| $argv[$#argv] | _argv[_argc] |
| set prompt = "ch> " | _prompt = "ch> " |
| set path = (/usr/bin /bin) | _path = "/usr/bin /bin" |
| umask 022 | umask(022) |
| setenv PATH "/usr/bin /bin" | putenv("PATH=/usr/bin /bin") |
| echo $PATH | printf("%s\n", getenv("PATH")) |
| echo $PATH | echo $(getenv("PATH")) |
| echo $PATH | echo $PATH |
| echo ${PATH} | echo ${PATH} |
| echo $path | printf("%s\n", _path) |
| echo $path | echo $_path |
| unsetenv PATH | remenv("PATH") |
| printenv PATH | getenv("PATH") |
| unset path | remvar _path |
| unset path | #pragma remvar(_path) |
| unset i | remvar i |
| unset i | #pragma remvar(i) |
| set i =90 | int i = 90 |
| set i =91 | i = 91 |
| set hostnam =`hostname` | string_t hostnam =`hostname` |
| set host =`hostname` | _host =`hostname` |
| alias rm "rm -i" | alias("rm", "rm -i") |
| alias rm | alias rm |
| alias rm | alias("rm") |
| alias | alias |
| alias | alias() |
| unalias rm | unalias rm |
| unalias rm | alias("rm", NULL) |
| alias find "f . -name \!:1 -print" | alias("f", "find . -name _argv[1] -print") |
| alias e "echo \!* \!$ \!#" | alias("e", "echo _argv[*] _argv[$] _argv[#]") |
| eval ls | streval("`ls`") |
| eval ls | system("ls") |
| eval setenv NAME VALUE | streval("putenv(\"NAME=VALUE\")") |
| /usr/bin/ls * | /usr/bin/ls * |
| ./cmd -option | ./cmd -option |
| status | _status |
| ls ~ * | ls ~ * |
| ls > output | ls > output |
| ls | tar -cf tarfile | ls | tar -cf tarfile |
| ls $PATH | ls $(getenv("PATH")) |
| ls $PATH | ls $PATH |
| ls $path | ls $_path |
| history = 100 | _histsize = 100 |
| history | history |
| $cmd | $cmd |
| !l | !l |
| !l -agl | !l -agl |
| !3 | 13 |
| !-1 | !-1 |
| !! | !! |
| vi `grep -l "str1 str2" *.c` | vi `grep -l "str1 str2" *.c` |
/* use $ inside ` ` */
set filename="test.txt"
set tmpFilename=$filename
echo `wc -w $tmpFilename` |
string_t filename = "test.txt";
string_t tmpFilename = filename;
echo `wc -w $tmpFilename`; |
| .cshrc .login .logout | .chrc .chlogin .chlogout |
| .cshrc .login .logout | .chsrc .chslogin .chslogout |
| Bash and Korn Shell | Ch Shell |
eval $(date "+month='%B' mon='%m' year='%Y'")
echo $month $mon $year
|
string_t month, mon, year;
streval(`date '+month="%B", mon="%m", year="%Y"'`);
echo $month $mon $year
|
|
ls /non_exist_directory >junk 2>&1
|
ls /non_exist_directory >junk 2>&1
|
Ch Extensions to C for Shell Programming
Ch extends C with following features, which make it
especially suitable for shell programming.
-
Ch can be used in either command shell or program
mode interpretively.
Ch language environment can be used as a command shell.
In command shell,
history substitution, event designator, quick substitution, command
substitution, aliases, and file or directory
name completion using a tab key
are available in Ch.
An example of Ch shell in action is given as follows:
> hello.c
Hello, world!
(execute the famous hello.c program without compilation)
> int i, *p;
> p = &i;
> *p = 90;
> printf("i = %d\n", i);
i = 90
>if (i == 90) printf("good\n"); else printf("bad\n");
good
> int **p2
> p2 = &p
> printf("**p2 = %d\n", **p2)
**p2 = 90
> ls * > junkfile
(all file names go to junkfile)
> i**p
8100
> array int a[2][3]
> a = (array int [2][3])5
> a
5 5 5
5 5 5
> 2*transpose(a)
10 10
10 10
10 10
> which ls
ls is aliased to ls -F // if ls is an alias set in ~/[_]chrc
>
-
POSIX
Ch supports POSIX for across platform directory and file processing.
Sample code can be found here.
-
String data type
C is a small language; it has no string data type.
Pointers to char are handled as strings in C.
Ch provides an alternative string data type for pointers.
It eliminates potential bugs before they happen.
The string data type can also be used with existing C functions
and C data type of 'pointers to char'.
For shell programming,
a string data type
is added to C in Ch.
It is seamlessly merged
with pointer to char.
All functions defined in the standard C library header string.h
are valid for both pointers to char and strings.
String is a first-class object in Ch.
For example, the following code fragment
string_t s, a[3];
s = "great string"
s = stradd("greater ", s)
strcpy(a[0], s);
printf("a[0] = %s\n", a[0]);
will display
greater great string.
-
Foreach-loop is added for shell programming.
The code below will create three directories
dir1, dir2
and
dir3
in the current directory.
string_t token, str="dir1 dir2 dir3";
foreach(token; str) {
mkdir $token
}
-
Verbatim output.
The verbatim output can be achieved using
the code block feature of function fprintf.
The Ch function sendApplet() below generates a C program.
void sendApplet(char *x, char *y, char *expr) {
fprintf(stdout, "#include\n");
fprintf(stdout, "int main() {\n");
fprintf(stdout, " double x = %s;\n", x);
fprintf(stdout, " double y = %s;\n", y);
fprintf(stdout, " printf(\"x = %%f, \", x);\n");
fprintf(stdout, " printf(\"y = %%f \\n\", y);\n");
fprintf(stdout, " printf(\"%s = %%f\\n\", %s);\n", expr, expr);
fprintf(stdout, "}\n");
}
The above function sendApplet() can be rewritten in Ch as follows.
void sendApplet(char *x, char *y, char *expr) {
fprintf stdout << ENDFILE
#include
int main() {
double x = $x;
double y = $y;
printf("x = %f", x);
printf("y = %f\n", y);
printf("$expr = %f\n", $expr);
}
ENDFILE
}
-
Combination of shell commands with C code
Shell commands such as sed, awk, wc, grep, etc. can be combined
with C code to run in Ch.
Executable programs can also be used directly in a Ch script.
Below is an example:
#!/bin/ch
string_t result1;
char result2[50];
grep "test" myfile.txt;
if ( _status == 0 )
{
printf(" 'test' is found in myfile.txt\n");
}
else
{
printf("Cannot find 'test' in myfile.txt\n");
}
result1=`wc -l /etc/passwd`;
echo $result1;
strcpy(result2, `wc -l /etc/passwd`);
printf("%s\n", result2);
The output for result1 and result2 are the same in the above program.
-
Safe Ch is designed for the growing need of internet computing.
In addition to restrictions imposed by conventional restricted shell,
many other features are disabled in safe Ch
for across-network internet computing.
A system administrator may grant limited access to
users using safe Ch.
-
System variables
_argc and _argv
for command line argument interface.
System variables
_argc and _argv
are equivalent to arguments
argc and argv in startup C function
int main(int argc, char *argv[]);
Assume program
cpfile.ch
contains the following statements.
#!/bin/ch
cp /dir/source/$(_argv[1]) /dir/dest/
Program
cpfile.ch
can be used to copy any file located
at
directory
/dir/source
to directory
/dir/dest.
For example, the following commands
> cpfile file1
> cpfile file2
will copy files
file1 and
file2
from
directory
/dir/source
to directory
/dir/dest.
-
Example 1
The following Ch shell script can be
used to backup file systems.
#include<stdio.h>
int main () {
printf("Hello, world!\n");
echo Ch is cool! I can now use a C program to backup files
dump 0ubdsf 80 50000 50000 /dev/nrst0 /dev/sd0a
}
-
Example 2
The example below will print out all sub-directories and files
in the current directory, as well as the full command path
for command
gzip.
#!/bin/ch
#include <stdio.h>
char *s;
int i=0;
foreach (s;`ls`) {
i++;
echo $s;
}
printf("the total number of directory and files are %d\n",i);
s =`which gzip`;
echo $s;
-
Example 3
In this example,
the controlling variable of the
switch
statement is a string
instead of integral type.
#!/bin/ch
char *ch="SoftIntegration Ch";
float version=2;
string_t hostname=`hostname`;
switch (hostname) {
case "mymachine":
printf("Hello world\n");
break;
case "testmachine":
fprintf stdout << ENDPRINT
Welcome to use $ch version $version
SoftIntegration, Inc.
ENDPRINT
echo Ch is cool! I can now use a C program to backup files
dump 0ubdsf 80 50000 50000 /dev/nrst0 /dev/sd0a
break;
default:
break;
}
Example 4
Below is another sample shell script.
#!/bin/ch
int main() {
char *s = "pointer to char";
int i;
float f, *p;
f = 6;
p= &f;
printf("s = %s\n", s);
printf("*p = %f\n", f);
/* output from the following statement is "6 6 pointer to char o" */
echo $f $(*p) $s $(*(s+1));
pwd
ls > junk
}
Example 5
The following shell commands in Bourne Shell sh
#!/bin/sh
command argument << EOF
input from the console command line
abcd
EOF
can be handled in Ch as follows:
#!/bin/ch
#include
FILE *fp;
string_t command_args="input from the console command line\nabcd";
fp = popen("command", "w");
fwrite(command_args, strlen(command_args), sizeof(char), fp);
pclose(fp);
Example 6
List all commands available anywhere in your path in Unix.
#!/bin/ch
#include
string_t dir, file; /* define string type */
foreach (dir; `echo $PATH | tr ":" " "`) {
foreach (file; `ls $dir`) {
if (access (dir/file, X_OK) ==0 ) {
echo $dir/$file;
}
}
}
Example 7
Find and compile all .c files into .o
in the current directory when the .o is old or absent.
#!/bin/ch
#include
struct stat cstat, ostat;
string_t c, o;
foreach (c; `find . -name "*.c"`) {
o=`echo $c | sed 's/.c$/.o/'`;
stat(o, &ostat);
stat(c, &cstat);
if (ostat.st_mtime > cstat.st_mtime) {
echo "compiling $c to $o";
gcc -c -o "$o" "$c";
}
}
|