• No results found

35 Making sftp Look More Like ftp

The secure version of the file transfer protocol ftp program is included as part of ssh, the secure shell package, but its interface can be a bit confusing for users who are making the switch from the crusty old ftp client. The basic problem is that ftp is invoked as ftp remotehost, and it then prompts for account and password information. By contrast, sftp wants to know the account and remote host on the command line and won't work properly (or as expected) if only the host is specified.

To address this, a simple wrapper script allows users to invoke mysftp exactly as they would have invoked the ftp program, using prompts for needed fields.

The Code

#!/bin/sh

# mysftp - Makes sftp start up more like ftp.

echo -n "User account: " read account

if [ -z $account ] ; then

exit 0; # changed their mind, presumably fi

if [ -z "$1" ] ; then echo -n "Remote host: " read host if [ -z $host ] ; then exit 0 fi else host=$1 fi

# End by switching to sftp. The -C flag enables compression here.

exec /usr/bin/sftp -C $account@$host

Running the Script

As with the ftp client, if users omit the remote host the script continues by prompting for a remote host, but if the script is invoked as mysftp remotehost, the remotehost provided is used instead.

The Results

First off, what happens if you invoke sftp without any arguments? $ sftp

usage: sftp [-vC1] [-b batchfile] [-o option] [-s subsystem|path] [-B buffer_size] [-F config] [-P direct server path] [-S program]

[user@]host[:file [file]]

Useful, but confusing. By contrast, invoke this script without any arguments and you can proceed to make an actual connection:

$ mysftp

Remote host: intuitive.com Connecting to intuitive.com... [email protected]'s password: sftp> quit

Invoke the script as if it were an ftp session by supplying the remote host, and it'll prompt for the remote account name and then invisibly invoke sftp:

$ mysftp intuitive.com User account: taylor Connecting to intuitive.com... [email protected]'s password: sftp> quit

Hacking the Script

There's a trick in this script worth mentioning: The last line is an exec call. What this does is replace the currently running shell with the application specified. Because you know there's nothing left to do after calling the sftp command, this method of ending our script is more efficient than having the shell hanging around waiting for sftp to end.

We'll revisit the sftp command in Script #83, to see how it can be used to securely and automatically synchronize a local and remote directory.

#36 Fixing grep

Some versions of grep offer a remarkable variety of capabilities, including the particularly useful ability to show the context (a line or two above and below) of a matching line in the file. Additionally, some rare versions of grep can highlight the region in the line (for simple patterns, at least) that matches the specified pattern.

Both of these useful features can be emulated in a shell script, so that even users on older commercial Unixes with relatively primitive grep commands can enjoy them. This script also borrows from the ANSI color script, Script #11.

The Code

#!/bin/sh

# cgrep - grep with context display and highlighted pattern matches.

context=0 esc="^[" bOn="${esc}[1m" bOff="${esc}[22m" sedscript="/tmp/cgrep.sed.$$" tempout="/tmp/cgrep.$$" function showMatches { matches=0

echo "s/$pattern/${bOn}$pattern${bOff}/g" > $sedscript

for lineno in $(grep -n "$pattern" $1 | cut -d: -f1) do

if [ $context -gt 0 ] ; then prev="$(($lineno - $context))"

if [ "$(echo $prev | cut -c1)" = "-" ] ; then prev="0"

fi

next="$(($lineno + $context))"

if [ $matches -gt 0 ] ; then echo "${prev}i\\" >> $sedscript echo "----" >> $sedscript fi

echo "${prev},${next}p" >> $sedscript else

echo "${lineno}p" >> $sedscript fi

matches="$(($matches + 1))" done

if [ $matches -gt 0 ] ; then

sed -n -f $sedscript $1 | uniq | more fi

}

trap "/bin/rm -f $tempout $sedscript" EXIT

if [ -z "$1" ] ; then

echo "Usage: $0 [-c X] pattern {filename}" >&2; exit 0 fi

if [ "$1" = "-c" ] ; then context="$2" shift; shift

elif [ "$(echo $1|cut -c1-2)" = "-c" ] ; then context="$(echo $1 | cut -c3-)" shift fi pattern="$1"; shift if [ $# -gt 0 ] ; then for filename ; do echo "--- $filename ---" showMatches $filename done else

cat - > $tempout # save stream to a temp file showMatches $tempout

fi

exit 0

How It Works

This script uses grep -n to get the line numbers of all matching lines in the file and then, using the specified number of lines of context to include, identifies a starting and ending line for displaying each match. These are written out to the temporary sed script, along with a word substitution command (the very first echo statement in the showMatches function) that wraps the specified pattern in bold-on and bold-off ANSI sequences. That's 90 percent of the script, in a nutshell.

Running the Script

This script works either with an input stream (in which case it saves the input to a temp file and then processes the temp file as if its name had been specified on the command line) or with a list of one or more files on the command line. To specify the number of lines of context both above and below the line matching the pattern that you specified, use -c value, followed by the pattern to match.

The Results

$ cgrep -c 1 teacup ragged.txt --- ragged.txt ---

in the wind, and the pool rippling to the waving of the reeds--the rattling teacups would change to tinkling sheep-bells, and the Queen's shrill cries to the voice of the shepherd boy--and the

Hacking the Script