diff/cmp for remote files

December 24, 2009

rdiff, rcmp – diff remote files

rdiff and rcmp extends the diff and cmp utilities to remote files.
Each file or directory argument is either a remote filename of the form [[user@]host1:]file, or a local filename.

Note: the scp program is used to retrieve a remote file. Setting up ssh authentication (~/.ssh/authorized_keys on the remote host ) may be required. Google ’ssh authorized keys’ for info.
Note2: rdiff can be hardlink’d to rcmp (ie: ln rdiff rcmp). The filename is used to determine functionality.

Example usages:
    $ rdiff -b produser@prodhost:/home/prod/bin/xyz.sh xyz.sh
    $ rcmp produser@prodhost:/home/prod/javalib/xyz.jar $HOME/dev/java/lib/xyz.jar
    $ rcmp ruser@rhost:/usr/local/abc:def /usr/local/abc:def

#! /bin/sh
#  Name:
#  Synopsis:    rdiff [-b] [[user@]host1:]file1 [[user@]host2:]file2
#               rcmp [[user@]host1:]file1 [[user@]host2:]file2
#  Description: Run diff/cmp on remote files.
#               Note: This script uses scp or rcp to copy remote files.
#                     (~/.ssh/.authorized_keys, or ~/.rhosts may need to be configured)
#               Note2: Simple pattern matching is used to determine if a file is remote or local
#                      The filename matches a "user@host:" or "host:" prefix pattern.  To force
#                      a match for a local file, specify the full path with the leading '/'.

getfile()
{
        if perl -e 'exit !(($ARGV[0] =~ /.*\@.*:\/.*/) || ($ARGV[0] =~ /.*:\/.*/)) &&
                         !($ARGV[0] =~ /\/.*/);' $1; then
                scp "$1" "$2"
                # rcp "$1" "$2"
        else
                ln "$1" "$2";  # This is slight overkill, after all we already
                               # have the file.  But, it simplifies file cleanup...
        fi
}

# Main
case $0 in
  *rdiff) cmd=diff;;
  *rcmp) cmd=cmp;;
esac

while getopts b opt; do
        case $opt in
          b) options="${options} -b";;
        esac
done
shift `expr $OPTIND - 1`

TMPDIR=$HOME/.tmp
tmpfile1=$TMPDIR/r${cmd}1.$$
tmpfile2=$TMPDIR/r${cmd}2.$$
trap "rm -f $tmpfile1 $tmpfile2" 0 15

if getfile "$1" "$tmpfile1" && getfile "$2" "$tmpfile2"; then
        eval $cmd $options $tmpfile1 $tmpfile2
fi

clone.c

December 12, 2009

Here’s a fun, short program.
The primary goal is to write a program that outputs the source code of the program when it is executed (ie: clone itself). A secondary goal is to make the program as small as possible.

Here’s a sh/ksh/csh/bash implementation:
cat $0

Here’s a version written in C:
char *s="char *s=%c%s%c; main() { printf(s,34,s,34); putchar(0x0a); }"; main() { printf(s,34,s,34); putchar(0x0a); }

getopts.java is a simple parser for java. It is based on the Unix getopts shell utility of the same name.

Example usage: See main() below.

/* Name:        $Id$
 * Description: getopts is a simple command line parser based on the getopts shell parser.
 */

package HHjlib;

public class getopts
{
        private String argv[];
        private int argc;
        private int optind = 0;
        private int optind2 = 0;
        private String optarg;

        /** Initialize the getopts parser with the command line argument array */
        public getopts(String args[])
        {
                argv = args;
                argc = args.length;
                optind = 0;
                optind2 = 1;
        }

        /** The getOption() method parses positional parameters.
            The optstring parameter contains the option characters to be recognized;
            If a character is followed by a colon, the option is required to have an argument.
            Note: The colon and question mark characters may not be used as option characters.
            Each time it is invoked, getOption() returns the next option.  If an option requires
            an argument, the argument may be retrieved using the getOptionArg() method after
            getOption() processing.  If an invalid option is seen, getOption() returns a '?'.
            When  the  end  of options is encountered, getopts returns the null character.
         */
        public char getOption(String optstring)
        {
                if (optind >= argc || argv[optind].charAt(0) != '-')
                        return '';

                char argv_option = argv[optind].charAt(optind2);
                int optlen = optstring.length();
                for (int indx = 0; indx < optlen; ++indx) {
                        char opt = optstring.charAt(indx);
                        if (argv_option == opt) {
                                int argv_length = argv[optind].length();
                                if ((indx + 1 < optlen) && (optstring.charAt(indx + 1) == ':')) {
                                        if (optind2+1 < argv_length) {
                                                optarg = argv[optind].substring(optind2+1);
                                                optind++;
                                                optind2 = 1;
                                        } else if (optind+1 < argc) {
                                                optarg = argv[optind+1];
                                                optind += 2;
                                                optind2 = 1;
                                        } else {
                                                optarg = Character.toString(opt);
                                                return ':';
                                        }
                                } else {
                                        if (optind2 + 1 < argv_length) {
                                                optind2++;
                                        } else {
                                                optind++;
                                                optind2 = 1;
                                        }
                                }
                                return opt;
                        }
                }
                optarg = Character.toString(argv_option);
                return '?';
        } 

        /** Return a cmdline option argument
            or the option character if command line parsing failed */
        public String getOptionArg() { return optarg; } 

        /** Return the index of the next cmdline array element to be processed */
        public int getOptionIndex() { return optind; } 

        public static void main(String args[])
        {
                getopts cmdline = new getopts(args);
                char option;
                while ((option = cmdline.getOption("abcd:e:")) != '') {
                        switch(option) {
                        case 'a':
                        case 'b':
                        case 'c':
                                System.out.println("Option='" + option + "'");
                                break;
                        case 'd':
                        case 'e':
                                System.out.println("Option='" + option +
                                        "', Argument='" + cmdline.getOptionArg() + "'");
                                break;
                        case '?':
                                System.err.println("Error: Invalid option '-" +
                                        cmdline.getOptionArg() + "'");
                                System.exit(1);
                        case ':':
                                System.err.println("Error: Missing option argument for '-" +
                                        cmdline.getOptionArg() + "'");
                                System.exit(1);
                        }
                }

                for (int indx = cmdline.getOptionIndex(); indx < args.length; ++indx) {
                        System.out.println(args[indx]);
                }
        }
}
/*
:!javac %
:!java `basename % .java` -a abc
:!java `basename % .java` -a -b a b c
:!java `basename % .java` -ab -c a b c
:!java `basename % .java` -a -bc a b c
:!java `basename % .java` -abc arg1 arg2
:!java `basename % .java` -ddarg arg1;  # error: missing option argument
:!java `basename % .java` -d darg arg1;  # error: missing option argument
:!java `basename % .java` -ddarg arg1 -e earg;  # error: missing option argument
:!java `basename % .java` -q; # Invalid option
:!java `basename % .java` -d; # error: missing option argument
*/

Here’s a very simple utility program that I use to debug how the shell interpreter parses quoted arguments in shell scripts. Shell quoted strings can be one of the trickiest things to get right in a script. White-space characters in filenames and directory names, string concatenation, nested single, double, back-tick quotes are only a few of the complications!

Note: To compile the code    $ cc printargs.c -o printargs

Here’s an example of debugging using printargs:
  $ cp $file $target

This works fine as long as the $file or $target name does not contain any white space characters. But if they do, the command will fail. For example: turn on trace ’set -x’ and set $file=’Star Trek IV.mp4′. The trace will display ‘+ cp Star Trek IV.mp4 destdir’ which looks right, but actually is wrong. Let’s prefix the cp command with printargs to see why.

$ file=’Star Trek IV.mp4′; target=destdir
$ printargs cp $file $target
+ printargs cp Star Trek IV.mp4 destdir
1 : ‘cp’
2 : ‘Star’
3 : ‘Trek’
4 : ‘IV.mp4′
5 : ‘destdir’

The copy command is actually trying to copy three files {Star, Trek, IV.mp4} to the dest directory. It should be just one file (‘Star Trek IV.mp4′). The fix is to quote both $file and $target ie: cp “$file” “$target”.

$ file=’Star Trek IV.mp4′; target=destdir
$ printargs cp “$file” “$target”
1 : ‘cp’
2 : ‘Star Trek IV.mp4′
3 : ‘destdir’

After debugging, remove the printargs prefix from the command and the script should be good to go.

/* Name:        %I%
 * Synopsis:    printargs -c -i -q commandline
 * Description: printargs displays cmdline arguments.  This is useful for debugging
 *              shell scripts that escape the space, backslash, quote chars ('"`)
 *              and other wildcard characters (eg: "[*?]").
 *                Options: -c  Echo the command line
 *                         -i  Do not display argument indices
 *                         -q  Do not display single quotes surrounding each argument
 */

#include
#include 

main(int argc, char *argv[])
{
        int opt;
        extern int optind;
        int echo_cmdline = 0;
        int display_indices = 1;
        int indx;
        char *quotes = "'";
        char *spaces = "";

        while ((opt = getopt(argc, argv, "ciq")) != EOF) {
                switch(opt) {
                case 'c':
                        echo_cmdline = 1;
                        break;
                case 'i':
                        display_indices = 0;
                        break;
                case 'q':
                        quotes = "";
                        break;
                default:
                        fprintf(stderr, "%s: Invalid option '%c'\n", opt);
                        fprintf(stderr, "Syntax: %s [-ciq] command command_options command_args ..."
);
                        exit(1);
                }
        }

        --argc;
        if (echo_cmdline == 1) {
                printf("CmdLine: ");
                for (indx = optind; indx <= argc; ++indx)
                        printf("%s%c", argv[indx], indx < argc ? ' ' : '\n');
        }

        for (indx = optind; indx <= argc; ++indx) {
                if (display_indices == 1)
                        printf("%d : ", indx - optind + 1);
                printf("%s%s%s\n", quotes, argv[indx], quotes);
        }

        exit(0);
}

Generate an index (or tag) file of AVS functions to allow these items to be easily located by a text editor (vi).

/*  Synopsis:     avstags [ avs_file ...]
 *  Description:  Create vi Style tags for OpenLink Endur/Findur AVS files
 */

%{
#include
#include
#include
#include "emsg.h"
static void reset_input_line();
%}
%option noyywrap

  static char *avs_filename = "";
  static int braces = 0, comment = 0;
  static char identifer[32 + 1], input_line[2048];
  static int id_indx = 0, line_indx = 0;
  static int ch, prev_ch;
  static char tag[sizeof(identifer)], tag_line[sizeof(input_line)];
  static int tagline_indx = 0, tag_flag = 0, tagline_flag = 0;

%%

\/\*    { comment = 1; }
\*\/    { comment = 0; }
\/\/    { strcat(input_line, yytext); line_indx+= yyleng;
          while ((ch = input()) != '\n')
             input_line[line_indx++] = ch;
          reset_input_line();
        }
\{      { if (comment == 0) ++braces;  input_line[line_indx++] = yytext[0]; }
\}      { if (comment == 0) --braces;  input_line[line_indx++] = yytext[0]; }
\"      { if (comment == 0) {
             ch = yytext[0];
             do {
                input_line[line_indx++] = ch;
                prev_ch = ch;
             } while (!(((ch = input()) == '"') && prev_ch != '\\'));
          }
        }
\(      { if (comment == 0 && braces == 0) {
             tagline_flag = 1;
             strcpy(tag, identifer);
          }
          input_line[line_indx++] = yytext[0];
          debug2_printf(("Matched '(' tag='%s', braces=%d, comment=%d\n", tag, braces, comment));
        }
\)      { input_line[line_indx++] = yytext[0];
          debug2_printf(("Matched ')' tag='%s', braces=%d, comment=%d\n", tag, braces, comment));
          while (comment == 0 && braces == 0 && ((ch = input()) != EOF)) {
             input_line[line_indx++] = ch;
             if (ch == '{') {
                tag_flag = 1;
                ++braces;
                break;
             } else if (ch == ';') {
                tag_flag = 0;
                break;
             } else if (ch == '\n') {
                reset_input_line();
             }
          }
        }
[a-zA-Z_][a-zA-Z0-9_]*   {
          strncpy(identifer, yytext, sizeof(identifer) - 1);
          strcat(input_line, yytext);
          line_indx += yyleng;
          debug2_printf(("ident='%s'\n", identifer));
        }
.       { input_line[line_indx++] = yytext[0]; }
\n      { reset_input_line(); }
%%

static void
reset_input_line()
{
        debug_printf(("reset_input_line() tagline_flag=%d, tag_flag=%d, tag='%s', tag_line='%s'\n", tagline_flag, tag_flag, tag, tag_line));
        if (tagline_flag)
                strcpy(tag_line, input_line);
        if (tag_flag) {
                printf("%s\t%s\t/^%s$\n", tag, avs_filename, tag_line);
        }
        tagline_flag = tag_flag = 0;
        memset(input_line, 0, sizeof(input_line));
        line_indx = 0;
}

static void
avstags(char *filename, FILE *fp)
{
        avs_filename = filename;
        yyin = fp;
        yylex();
}

main(int argc, char *argv[])
{
        int indx;
        FILE *fp;

        if (argc == 1) {
                avstags("", stdin);
        } else {
                for (indx = 1; indx  tags
*/

SQL pretty printer

November 9, 2009

A very simple SQL pretty printer.

Example usage:
  $ ppsql query1.sql
  $ cvs co -p query.sql | ppsql

Compilation instructions:
  1 $ lex ppsql.l
  2 $ cc -g lex.yy.c -o ppsql

static char rev[] = "#(@) $Id: ppsql.l,v 1.8 2005/04/28 13:48:55 howard Exp $";
/* Synsopsis     ppsql [file_containing_SQL_statement ...]
 * Description:  A simple SQL Pretty Printer
 */

%{
#include <stdio.h>
#include <string.h>

enum {
        TOKEN_SELECT = 1,
        TOKEN_IDENTIFIER,
        TOKEN_COMMA,
        TOKEN_FROM,
        TOKEN_WHERE,
        TOKEN_AND,
        TOKEN_OR,
        TOKEN_OPAREN,
        TOKEN_CPAREN,
        TOKEN_STRING,
        TOKEN_INTEGER,
        TOKEN_DOUBLE,
        TOKEN_OPERATOR,
        N_TOKENS
};
%}
%option noyywrap

%%

select                  { return TOKEN_SELECT; }
","                     { return TOKEN_COMMA; }
\".*\"                  { return TOKEN_STRING; }
\'.*\'                  { return TOKEN_STRING; }
[0-9]+                  { return TOKEN_INTEGER; }
[0-9]+"."[0-9]*         { return TOKEN_DOUBLE; }
from                    { return TOKEN_FROM; }
where                   { return TOKEN_WHERE; }
and                     { return TOKEN_AND; }
or                      { return TOKEN_OR; }
"("                     { return TOKEN_OPAREN; }
")"                     { return TOKEN_CPAREN; }
[a-zA-Z][a-zA-Z0-9_.]*  { return TOKEN_IDENTIFIER; }
"+"|"-"|"*"|"/"|"="|""|"!="|">"|"<"|"="    {
                        return TOKEN_OPERATOR; }

[ \t\n]+                /* Eat Whitespace */

.                       printf("?%c?", yytext[0] );   /* Hmmm, what's this? */

%%
typedef enum {
        BEGIN_STATE,
        PARSE_SELECT,
        PARSE_FROM,
        PARSE_WHERE,
        END_STATE,
} parse_state;

typedef struct int_stack_s {
        int size;
        int top;
        int stack[1];  /*  Actually int stack[size];   ref: new_int_stack()  */
} int_stack_t;

int_stack_t *
new_int_stack(int size)
{
        int_stack_t *stack;

        if (stack = malloc(sizeof(int_stack_t) + (size - 1) * sizeof(int))) {
                stack->size = size;
                stack->top = 0;
        }
        return stack;
}

static int
push(int_stack_t *stack, int val)
{
        if (stack->top size) {
                stack->stack[stack->top++] = val;
                return stack->top;
        }
        return -1;
}

static int
pop(int_stack_t *stack, int *val)
{
        if (stack->top > 0) {
                *val = stack->stack[--stack->top];
                return stack->top;
        }
        return -1;
}

ppsql(FILE *fp_in)
{
        int_stack_t *indent_stack = new_int_stack(255);
        parse_state state;
        int token_type, space_flag;
        int indent = 0, col = 0;

        yyin = fp_in;
        state = BEGIN_STATE;

        while (token_type = yylex()) {
                switch(token_type) {
                case TOKEN_SELECT:
                        fputs("select ", stdout);
                        indent = col;
                        col += 7;
                        space_flag = 0;
                        state = PARSE_SELECT;
                        break;
                case TOKEN_FROM:
                case TOKEN_WHERE:
                case TOKEN_AND:
                case TOKEN_OR:
                        fputc('\n', stdout);
                        for (col = 0; col < indent; ++col) { fputc(' ', stdout); }
                        if (col == 0) {
                                if (token_type == TOKEN_AND) {
                                        fputs("  ", stdout);
                                        col += 2;
                                } else if (token_type == TOKEN_OR) {
                                        fputs("   ", stdout);
                                        col += 3;
                                }
                        }
                        fputs(yytext, stdout);
                        fputc(' ', stdout);
                        col += (strlen(yytext) + 1);
                        space_flag = 0;
                        break;
                case TOKEN_OPAREN:
                        if (space_flag) { fputc(' ', stdout); }
                        push(indent_stack, indent);
                        fputs("( ", stdout);
                        col += (2 + space_flag);
                        indent = col;
                        space_flag = 0;
                        break;
                case TOKEN_CPAREN:
                        fputs(" )", stdout);
                        col += 2;
                        pop(indent_stack, &indent);
                        space_flag = 1;
                        break;
                case TOKEN_COMMA:
                        /*fputs(((state == PARSE_SELECT) ? ",\n" : ", "), stdout);*/
                        fputs(", ", stdout);
                        col += 2;
                        space_flag = 0;
                        break;
                case TOKEN_OPERATOR:
                        fputc(' ', stdout);
                        fputs(yytext, stdout);
                        fputc(' ', stdout);
                        col += (strlen(yytext) + 2);
                        space_flag = 0;
                        break;
                default:
                        if (space_flag) { fputc(' ', stdout); }
                        fputs(yytext, stdout);
                        col += (strlen(yytext) + space_flag);
                        space_flag = 1;
                        break;
                }
        }
        printf("\n");
        return 0;
}

main( argc, argv )
int argc;
char **argv;
{
        int retcode;

        if (argc == 1) {
                retcode = ppsql(stdin);
        } else {
                int indx;
                FILE *fp;
                for (indx = 1; indx < argc; ++indx) {
                        if (fp = fopen(argv[indx], "r")) {
                                if (argc > 2) { printf("=== %s ===\n", argv[indx]); }
                                retcode = ppsql(fp);
                                fclose(fp);
                                if (retcode != 0)
                                        break;
                        }
                }
        }

        return retcode;
}

/*
:!lex % && cc -g lex.yy.c && rm -f lex.yy.c
:!echo 'select col1, string, (a+b)/c, literal from table , table2 where x = y and a = b or  c in (select distinct abc from def where alphabet = 1) and 1 =  1' | a.out

Sample SQL:
select distinct tran_schedule_delivery.  delivery_id, 455854
from tran_schedule_delivery, ab_tran, schedule_delivery_detail
where ( tran_schedule_delivery.delivery_id = schedule_delivery_detail.delivery_id
        and schedule_delivery_detail.deal_tracking_num = ab_tran.deal_tracking_num )
  and ( tran_schedule_delivery.volume_type <> 0
        and ( ( ab_tran.ins_type in ( select id_number
                                      from instruments
                                      where id_number in ( 45001, 45002, 45006, 45149, 45160 )
                                         or base_ins_id in ( 45001, 45002, 45006, 45149, 45160 ) )
                and ab_tran.buy_sell = 0 )
           or ( ab_tran.ins_type in ( select id_number
                                      from instruments
                                      where id_number in ( 45003, 45008, 45137, 45146 )
                                         or base_ins_id in ( 45003, 45008, 45137, 45146 ) ) )
           or ( ab_tran.ins_type in ( select id_number
                                      from instruments
                                      where id_number in ( 45001, 45002, 45006, 45149, 45160 )
                                         or base_ins_id in ( 45001, 45002, 45006, 45149, 45160 ) )
                and ab_tran.buy_sell = 1 ) )
        and tran_schedule_delivery.gmt_start_date_time  &tt; '01-jan-2003 00:00:00'
        and tran_schedule_delivery.volume_type in ( 4 , 6 ) )
  or not exists ( select *
                  from query_result qr2
                  where qr2.unique_id = 455854
                    and qr2.query_result = tran_schedule_delivery.delivery_id )
*/

hexdump.c utility

November 9, 2009

Dump files out in hex format.
Useful for displaying binary files and debugging weird input/output.

Example usages:
  $ hd unicode.txt
  $ cat unicode.txt | hd

/* Name:         $Id: hd.c,v 1.1 2003/05/02 23:56:11 howard Exp $
 * Synopsis:     hd file ...
 * Description:  Dump files in hex/ascii format.
 */

#include <stdio.h>
#include <ctype.h>

#define SEP ' '
#define SEP2 ' '
#define SEP3 "  "

static void
chardump(int *buf, int len)
{
        int indx = 0;

        for (indx = 0; indx < len; ++indx) {
                if (isprint(buf[indx]))
                        putchar(buf[indx]);
                else
                        putchar('.');
        }
}

static int
nibble2hex(int nibble)
{
        switch(nibble) {
          case 0: return '0';
          case 1: return '1';
          case 2: return '2';
          case 3: return '3';
          case 4: return '4';
          case 5: return '5';
          case 6: return '6';
          case 7: return '7';
          case 8: return '8';
          case 9: return '9';
          case 10: return 'A';
          case 11: return 'B';
          case 12: return 'C';
          case 13: return 'D';
          case 14: return 'E';
          case 15: return 'F';
        }

}
static void
hexdump(int ch)
{
        putchar(nibble2hex((ch >> 4) & 0xF));
        putchar(nibble2hex(ch & 0xF));
        putchar(SEP);
}

static int
dump(FILE *fp)
{
        int indx = 0;
        int buf[16];

        memset(buf, 0, sizeof(buf));
        while ((buf[indx] = fgetc(fp)) != EOF) {
                hexdump(buf[indx++]);
                if (indx == 8)
                        putchar(SEP2);
                else if (indx == 16) {
                        fputs(SEP3, stdout);
                        chardump(buf, indx);
                        putchar('\n');
                        indx = 0;
                }
        }
        if (indx > 0) {
                int indx2;
                for (indx2 = indx; indx2 < 16; ++indx2) {
                        if (indx2 == 8) { putchar(SEP2); }
                        putchar(' '); putchar(' '); putchar(SEP);
                }
                fputs(SEP3, stdout);
                chardump(buf, indx);
                putchar('\n');
        }
        return 0;
}

main(int argc, char *argv[])
{
        int indx;
        FILE *fp;

        if (argc == 1) {
                dump(stdin);
        } else {
                for (indx = 1; indx < argc; ++indx) {
                        if (fp = fopen(argv[indx], "rb")) {
                                dump(fp);
                                fclose(fp);
                        }
                }
        }
        exit(0);
}
/*
:!cc % -o hd
:!gcc -g % -o hd
*/

1. hgrep displays grep results in highlights.
2. rgrep runs grep on all the files in a directory tree.

Example usage:
  $ hgrep main *.c; # Search for main in all C files
  $ rgrep -e ‘[Ff]lour’ -e cornstarch $HOME/recipes; # Search all files in the recipes directory for flour or cornstarch

— hgrep —

#! /bin/sh
#  Synopsis:    hgrep pattern file ...
#  Description: Hilighted grep

smso=`tput smso`
rmso=`tput rmso`
pattern="$1"; shift
grep "$pattern" "$@" | sed -e "s@\(${pattern}\)@${smso}\1${rmso}@"

— rgrep —

#! /bin/sh
# Name:         $Id: rgrep,v 1.1 2009/10/11 20:37:21 hhong Exp $
# Synopsis:     rgrep pattern directory ...
# Synopsis:     rgrep [-e pattern ...] pattern directory ...
# Description:  Run grep on all the files of a directory tree

while getopts e:i opt; do
        case $opt in
          e) pattern="$pattern -e '$OPTARG'";;
          i) ignorecase='-i';;
        esac
done
shift `expr $OPTIND - 1`
if [ -z "$pattern" ]; then
        pattern="'$1'"; shift
fi

for arg; do
        if [ ! -d "$arg" ]; then
                echo "$0: Error '$arg' is not a directory." >&2
                exit 1
        fi
done 

find "$@" -type d -print | while read dir; do
        edir=`ls -A "$dir"`
        if [ -n "$edir" ]; then  # Check for empty directory
                # Use eval in case $pattern or $dir contain whitespace characters
                eval grep $ignorecase $pattern "'$dir'/*" /dev/null;
        fi
done

Perform number base conversions and calculations.
Note: The script behaves differently, depending on the filename (the executable may still be the same file). The following hard-links ($ ln math.sh …) to the math.sh script provides the following shortcuts…

  • x2d (hex to decimal), x2o (hex to octal), x2b (hex to binary)
  • d2x (decimal to hex), d2o (decimal to octal), d2b (decimal to binary)
  • o2x (octal to hex), o2d (octal to decimal), o2b (octal to binary)
  • b2x (binary to hex), b2d (binary to decimal), b2o (binary to octal)

Example usage:
  $ d2x;  # (Interactive) decimal to hex conversions
  $ x2d a b c d e f 10/2
  $ echo ‘f+1 ff+2′ | x2d

#! /bin/sh
#  Synopsis:    Math.sh [-i inputbase] [-o outputbase] [math-expression | Number]  ...
#  Description: Math.sh performs base number conversions, and 'bc -l' calculations
#               Note1: Spaces should be avoided in math-expressions.  A space character in
#                      an expression may be intepreted as a command line field separator.
#               Note2: Renaming or (hard/symbolic) ln'ing to the script alters the default input
#                     and output base (ie: b2o,b2d,b2x, o2b,o2d,o2x, d2b,d2o,d2x, x2b,x2d,x2d)
#                                          (b=binary, o=octal, d=decimal, x=hexadecimal)
#  Example Usage: $ math.sh;  # Interactive decimal calculator
#                 $ math.sh '1.11111111^2'
#                 $ echo 'ff ff+1' | x2d 

obase=10
ibase=10
case $0 in
  *b2o) ibase=2;  obase=8;;
  *b2d) ibase=2;  obase=10;;
  *b2x) ibase=2;  obase=16;;
  *o2b) ibase=8;  obase=2;;
  *o2d) ibase=8;  obase=10;;
  *o2x) ibase=8;  obase=16;;
  *d2b) ibase=10; obase=2;;
  *d20) ibase=10; obase=8;;
  *d2x) ibase=10; obase=16;;
  *x2b) ibase=16; obase=2;;
  *x2o) ibase=16; obase=8;;
  *x2d) ibase=16; obase=10;;
  *[Mm]ath*)
        while getopts i:o: opt; do
                case "$opt" in
                  i) ibase=$OPTARG;;
                  o) obase=$OPTARG;;
                esac
        done
        shift `expr $OPTIND - 1`
        ;;
esac

{
echo "obase=$obase"
echo "ibase=$ibase"
if [ $# -eq 0 ]; then
        cat
else
        echo "$*"
fi | tr 'a-f ' 'A-F\n'
} | bc -l 

This is a utility script that operates similarly to the find file (ff.sh) script previously presented. But instead of searching a directory tree, it searches the components of a $PATH environment variable.

Example usage:
    $ ffpath javac jdb; # Find the Java compiler and debugger
    $ ffpath -m myscript1 myscript2; # Display the myscript1 and myscript2 scripts using more
    $ ffpath -v PERL5LIB -s’;’ -e ‘pod2text’ sybdbi.pm; # Find and convert the Sybase DBi Perl doc to text

#! /bin/sh
#  Name:        $Id: ffpath,v 1.4 2008/10/09 12:18:51 hhong Exp $
#  Synopsis:    ffpath.sh [-v envvar] [-s field_separator] [-[l1cm]] file ...
#  Description: Search $PATH for file(s)
#               Options: -v envvar  Specify environment variable
#                        -s fs      Use 'fs' for the directory field separator
#                        -l         list file info using 'ls -l' long format (default)
#                        -1         list one file per line ie: 'ls -1'
#                        -c         Use 'cat' to display the file
#                        -m         Use 'more' to display the file
#                        -e prog    Run 'prog' on file

while getopts v:s:l1cme: opt; do
        case "$opt" in
          v) envvar=$OPTARG;;
          s) sep=$OPTARG;;
          l) display='lsl';;
          1) display='ls1';;
          c) display='cat';;
          m) display='more';;
          e) display='exec'; exec=$OPTARG;;
          \?) exit 1;;
        esac
done
shift `expr $OPTIND - 1`

for file; do
        eval echo \$${envvar:-PATH} | tr {$sep:-:} '\n' | while read dir; do
                if [ -f "$dir/$file" ]; then
                        case "${display:-lsl}" in
                          ls1) ls -1 "$dir/$file";;
                          lsl) ls -l "$dir/$file";;
                          echo) echo "$dir/$file";;
                          cat) cat "$dir/$file";;
                          more) less -rXe "$dir/$file";;
                          exec) eval $exec "$dir/$file";;
                        esac
                fi
        done
done
  • Set the command line prompt to the current shell level and directory (directory is abbreviated if very long)
    chdir ()
    {
        if [ $# -eq 2 ]; then
            'cd' `pwd | sed -e "s@$1@$2@"`;
        else
            'cd' "$@";
        fi && {
             if [ "${PWD#/*/*/}" = "$PWD" ]; then
                PS1='[${SHLVL} $PWD] ';
            else
                PS1='[${SHLVL} ..${PWD#${PWD%/*/*}}] ';
            fi;
            case "$TERM" in
                xterm)
                    echo -ne "33]0;${PWD}07"
                ;;
            esac
        }
    }
    

    Note: add the following alias and environment variable to your $HOME/.profile script:
    alias cd=’chdir’
    SHLVL=0; # for ksh only, also requires ‘(( SHLVL = $SHLVL + 1 ))’ in the ~/.kshrc startup script

  • Pipe the output of a command in to an editor (vi) instead of using more or less.
    function vip
    {
            tmpfile="$HOME/.tmp/$$.vip"
            trap "rm -f $tmpfile" 0 9 15
            cat > $tmpfile
            vi $tmpfile < /dev/tty > /dev/tty
    }
    

    Note: Requires the $HOME/.tmp directory.
    Example usage: $ ls -l | vip

  • Display the exit code of a program.
    trap "echo ' ' Retcode=\$?" ERR

    Note: Add to your .bashrc or .kshrc startup script

  • List all sub-directories of a specified directory:
    lld ()
    {
        if [ $# -eq 0 ]; then
            ls -ld */;
        else
            for d in "$@";
            do
                ls -ld ${d:-.}/*/;
            done;
        fi
    }
    

    Example usage:
      $ lsdirs; # display list of subdirectories in the current working directory
      $ lsdirs /usr/local $HOME/logs

  • Copy a directory tree to a remote system:
    $ { cd source_dir; tar cvf - .; } | ssh remote_host '{ cd dest_dir; tar xvf -; }
  • A simplified logfile rolling scheme. Remove all ‘xyz’ log files, except the last $keep files sorted by last modified date. Add to cron for automation.
    $ ls -1t "$logdir/xyz.*.log" | sed -e "1,${keep}d" | xargs -d'\n' rm

File Find

October 28, 2009

Here’s a shell script that implements the old Norton Utilities “FileFind” utility. I added a couple of useful additional options.

  • ‘-l’ will display a detailed file listing ala ‘ls -l file’.
  • ‘-c’ and ‘-m’ will display the file using cat and ‘more’ respectively.
  • ‘-e’ option will execute a program on the found file(s).

Example usage:
    $ ff. notes.txt; # find the notes.txt file
    $ ff. -m notes.txt; # display the notes.txt file
    $ ff. -e ‘grep footnote /dev/null’ notes.txt; # grep footnotes from notes.txt files

#! /bin/sh
#  Synopsis:    ff. [-lcm] [-e program] filename ...
#  Description: find files.
#               Options: -l  display directory long listing of file
#                        -c  cat the file
#                        -m  display the file using 'more' (or less)
#                        -e  execute 'program' on the file

while getopts lcme: opt; do
        case "$opt" in
          l) print='-exec ls -l {} \;';;
          c) print='-exec cat {} \;';;
          m) print='-exec less -E {} \;';;
          e) print="-exec $OPTARG {} \;";;
          \?) exit 1;;
        esac
done
shift `expr $OPTIND - 1`

names="-name $1"; shift
for f; do
        names="-name $f -o $names "
done

eval find . '\(' $names '\)' ${print:--print}

A safe rm

October 27, 2009

Here is a script that implements a Windows style ‘Recycle Bin’ delete function. Similarly like Windows delete, it moves deleted files/directories to a temporary directory (default: $HOME/.tmp). After a file is moved to the tmp directory and has not been accessed for N days, it is permanently deleted the next time the rm command is run (think of this as an automated ‘empty recycle bin’ function). It implements all the ‘rm’ options making it an ideal replacement for the /bin/rm command in interactive use.

Example usage:
  $ alias rm=$HOME/bin/rm


#! /bin/sh
#  Synopsis:     rm [-fir] file ...
#  Description:  A Safe rm (move file(s) to a tmp directory for later deletion).  Files in
#                the tmp directory that have not been accessed in N days are permantly deleted
#                the next time the rm command is run.
#                Options: -f  (force) Run "/bin/rm" instead of moving files to the tmpdir
#                         -i  interactive mode
#                         -r  move (recursively) a directory tree to the tmp directory
#                Caveat: mv fails if the filename matches a directory name in the $tmpdir directory.

#set -x
tmpdir=$HOME/.tmp
while getopts fir opt; do
        case $opt in
        f) force="-f";;
        i) interactive="-i";;
        r) recursive="-r";;
        \?) exit 1;;
        esac
done
shift `expr $OPTIND - 1`

if [ "$force" ]; then                            # Run /bin/rm if '-f' is specified!
	/bin/rm -f $interactive $recursive "$@";  # This is useful in shell functions
	exit $?                                   # when you want the real rm executable

for file; do
        if [ -n "$interactive" ]; then
                echo -n "remove '${file}'? "
                read yn
                if [ "$yn" != "Y" -a "$yn" != "y" ]; then
                        continue
                fi
        fi
        if [ -d "$file" ]; then
                if [ -n "$recursive" ]; then
                        mv -f "$file" "$tmpdir" || retcode=2
                else
                        echo "$0: cannot remove '$file': Is a directory"
                        retcode=1
                fi
                continue
        fi
        bf=`basename "$file"`
        mv -f "$file" "$tmpdir"
        touch "$tmpdir/$bf";  # For post cleanup, $file will be deleted N days from today
done

# Prolog: Clean up files, permanently delete files after atime days have elapsed
{ /bin/find "$tmpdir" -type f -atime +7 -exec /bin/rm '{}' \;
  /bin/find "$tmpdir" -mindepth 1 -type d -exec rmdir '{}' \; 2>/dev/null
} &

exit ${retcode:-0}