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 12:00 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.


"I am using Ch shell programming capabilities to automate the download and extraction of data from archive (*.ar) files using the Unix ar command for an automation system running under Win2000. I use ar within a Ch built-in 'for each' loop to extract all files (*.ar) within a given directory. It works beautifully.

I am very impressed with the capabilities of the Ch language, and plan to make more use of it in future in developing automation scripts."

-- Jeremy Walsh, National Institute of Water and Atmospheric Research, New Zealand

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<stdio.h>\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<stdio.h> 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 <stdio.h> 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 <unistd.h> 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 <sys/stat.h> 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"; } }