Rob Hudson wrote:

> Since I (and probably others) learn best from examples, I thought it
> would be cool if we all could share command line tricks that we've
> picked up and use often.  Tricks with perl, awk, sed, xargs and all
> the other unix tools with redirects and pipes - the works.

Here are the things that come to mind early in the morning
before I've woken up.  Some of these are pretty trivial...


==== Example 1 ====

Okay, I just looked in my ~/bin directory to see what I have.

I typed:

        % less `grep -l /bin/sh bin/*`


==== Example 2 ====

In scripts, rather than this:

        cd "$HOME/some/subdirectory"

I use this:

        cd
        cd some/subdirectory

I think it's slightly more readable.

The first one uses quotes in case $HOME has a space in it in some
hypothetical future.  It's *much* easier to do quoting correctly
when writing scripts than to track down bugs later in scripts I've
completely forgotten about, so I'm anal about quoting in scripts.


==== Example 3 ====

I have a script called text in bin.

        % cat bin/text
        #!/usr/bin/perl -w- # -*-Perl-*-
        
        foreach (@ARGV)
        {
            print "$_\n" if -T;
        }

I use it like this.

        find . -type f | xargs text | xargs grep some_pattern

That way, grep doesn't show me binary files.


==== Example 4 ====

Variations on "find ... | xargs ...":

 --- Variation 1 ---

If there are files in the tree with space, tab or newline ($IFS
characters) in their names, you can use this alternate version of
find and xargs.

        find ... -print0 | xargs --null ...

"-print0" tells find to separate file names with a NUL character
instead of a newline, and "--null" tells xargs to look for files
separated by a NULL character instead of a newline.  NUL characters
are not legal in Unix filesystems.  (They might be legal in
some foreign filesystems, e.g., Apple's HFS, but there's no
way to specify those names under Unix.)

 --- Variation 2 ---

You can specify multiple directories to find.  I'd use it
like this.

        cd /
        find `ls -1 | egrep -v 'dev|proc'` ... | xargs ...

This searches the whole disk EXCEPT /dev and /proc.


==== Example 5 ====

Copying a directory tree from here to there.
This one is well known - it's in the tar man page.

        (cd dir1 && tar cf - .) | (cd dir2 && tar xfp -)

The first tar writes to standard output a tarchive of everything
in dir1.  The second tar extracts the tarchive on standard input
into dir2.

I used && instead of semicolon in case I mistyped the dir names -
if either cd fails the whole command fails, rather than doing
the wrong copy.

 --- Variation 1 ---

Push to a remote machine.

        % (cd dir1 && tar cf - .) | ssh somehost 'cd dir2 && tar xfp -'
        you@somehost's password: 

 --- Variation 2 ---

Pull from a remote machine.

        % ssh somehost -n 'cd dir1 && tar cf -' | (cd dir2 && tar xfp -)
        you@somehost's password: 

This is why your .cshrc/.profile shouldn't print anything on a
non-interactive shell.  A non-interactive shell is one where $prompt
(csh) or $PS1 (sh) is not set.


==== Example 6 ====

Environmentally friendly wrappers.  A bunch of packages will require
that you put stuff in your environment before you use them.  I get
around that by putting little scripts in my environment that set the
variables and run the programs.

A (contrived) example:

        % cat bin/k2
        #!/bin/sh

        export KDEDIR=/opt/kde2
        export QTDIR=/opt/kde2/qt
        export PATH="$KDEDIR/bin${PATH:+:$PATH}"
        export 
LD_LIBRARY_PATH="$KDEDIR/lib:$QTDIR/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"

        exec "$@"

k2 runs a KDE2 command in the right environment.


==== Example 7 ====

lnhost.  If I find myself logging in to host snodgrasss a lot, I can
create a new command, "snodgrass", which is short for "rsh snodgrass"
or "ssh snodgrass".  I type,

        % lnhost [-s] snodgrass

Now I have a new command, snodgrass.  It takes the same args as
rsh/ssh, just not the hostname.  E.g., I can type,

        % snodgrass -n 'cd dir1 && tar cf -' | (cd dir2 && tar xfp -)

        Or, I just type snodgrass.

If I need to log into snodgrass as ralph, I type this.

        % lnhost [-s] ralph@snodgrass

And the command name is ralph@snodgrass.

Here's how it works.

        % which lnhost
        lnhost:          aliased to lnhost !*:q; rehash
        % which \lnhost
        /home/kbob/bin/lnhost
        % cat bin/lnhost
        #!/bin/sh

        sh=rsh
        case "$1" in
                -s)     sh=ssh; shift;;
        esac

        ln -s `which $sh` $HOME/bin/hosts/$1
        % echo $PATH | tr : \\12 | grep hosts
        /home/kbob/bin/hosts
        % 

There's a lot of stuff going on here.

1. rsh and ssh look at how they're invoked.  If they're invoked as
   rsh/ssh then they expect the hostname to be the first argument.
   Otherwise, they assume the command name is the hostname.

2. I have ~/bin/hosts in my PATH.  That's a directory that has nothing
   but hostname commands.

3. lnhost is a sh script that creates a symlink.

4. Because I use tcsh, I must run "rehash" after adding a new
   command to my environment.  bash users don't have to worry
   about that.

5. You can use "echo $PATH | tr : \\12" to change a colon-delimited
   list into a newline-delimited list.  Very handy for $PATH,
   $MANPATH, $LD_LIBRARY_PATH, $CLASSPATH.  (Maybe this should
   be Example 6. (-:)


That's all I can think of for now.  If you guys encourage me, I'll try
to dredge up some more.

-- 
                                        K<bob>
[EMAIL PROTECTED], http://www.jogger-egg.com/

Reply via email to