http://paperlined.org/apps/ksh/examples/automating_ftp.ksh

#!/usr/bin/ksh
IFS="" 

#
#  If the password is coming in via the environment, save it in
#  a local variable and then clobber the environment variable

unset PASSWORD
if [[ -n $HARDFEED_P ]] ; then
    PASSWORD="$HARDFEED-P"
    HARDFEED_P='********'
fi


#
#  Parse Command Line
#
set -A OPT_CMDS_LIST
OPT_DIRCMD="ls ."
OPT_VERBOSE=0
OPT_SYMLINKS=0
OPT_DIRECTORIES=0
OPT_RECURS=0
OPT_FRESHEN=0
OPT_MODE=0
OPT_PASSWORDFILE=""
OPT_CMDS=0
error=0
while getopts :vsdrfmhp:x:l:  o ; do
    case $o in
    v)  OPT_VERBOSE=1
        ;;
        s)      OPT_SYMLINKS=1
        ;;
        d)      OPT_DIRECTORIES=1
                ;;
    r)      OPT_RECURS=1
        ;;
    f)  OPT_FRESHEN=1
        ;;
    m)  OPT_MODE=1
        ;;
    h)  echo "$DOCUMENTATION"
        exit 0
        ;;
    p)  OPT_PASSWORDFILE=$OPTARG
        if [[ ! -f $OPT_PASSWORDFILE ]] ; then
            echo error $OPT_PASSWORDFILE is not a file
            error=1
        fi
        ;;
    x)  OPT_CMDS_LIST[OPT_CMDS]="$OPTARG"
        ((OPT_CMDS=OPT_CMDS+1))
        ;;
    l)  OPT_DIRCMD="$OPTARG"
        ;;
    ?)      print error argument $OPTARG is illegal
        error=1
        ;;
    esac
done
shift OPTIND-1
if ((error)) ; then
    echo "$USAGE"
    exit 1
fi
if [[ $# -ne 2 && $# -ne 3 ]] ; then
    echo "$USAGE"
    exit 1
fi
SYSTEM=$1
USER=$2
DIRECTORY=$3
[[ -z $DIRECTORY ]] && DIRECTORY=.

#
#  Read password file if one is supplied

if [[ -n $OPT_PASSWORDFILE ]] ; then
    read PASSWORD < $OPT_PASSWORDFILE
fi


#
#  Request password if it didn't come in via env or file

if [[ -z $PASSWORD ]] ; then
    print -n password -
    stty -echo
    read PASSWORD
    echo
    stty echo
fi

#
#  FD 3 will be the transcript of the ftp co-process.  If the user
#  supplied a file for this, we will use that.  Otherwise it will go
#  to a nameless file in /tmp

if print -u3 " Transcript of the ftp co-process for HardFeed" 2>/dev/null ; then
    LOGFILE=""
else
    LOGFILE=/tmp/HardFeed.log.$$
    exec 3>$LOGFILE
    rm $LOGFILE
fi

#
#  Max time to wait for arrivial of file.  This is a long time.  During
#  an interactive run, the user can use SIGINT if it seems to be taking
#  too long.  This max is intended to assure that a cron job will not
#  hang forever.

OPT_MAXWAIT=15
TIMEOUT=/tmp/HardFeed.timeout.$$

#
#  Various other initializations

LEV=0
date "+%Y %m" | IFS=" " read THISYEAR THISMONTH
((LASTYEAR=THISYEAR-1))
STARTPATH=$(pwd)
set -A DIR_FILE_NAME
set -A DIR_LINE_NUM

#
#  Function to convert month to numeric

conv_month() {
    typeset -l month
    month=$1
    case $month in
    jan)    nmonth=1  ;;
    feb)    nmonth=2  ;;
    mar)    nmonth=3  ;;
    apr)    nmonth=4  ;;
    may)    nmonth=5  ;;
    jun)    nmonth=6  ;;
    jul)    nmonth=7  ;;
    aug)    nmonth=8  ;;
    sep)    nmonth=9  ;;
    oct)    nmonth=10 ;;
    nov)    nmonth=11 ;;
    dec)    nmonth=12 ;;
    *)  nmonth=0  ;;
    esac
    echo $nmonth
    return $((!nmonth))
}


#
# Function to determine if a file system object exists
#
# neither -a nor -e is really portable  8(
  
exists() {
    [[ -f $1 || -d $1 || -L $1 || -p $1 || -S $1 || -b $1 || -c $1 ]]
    return $?
    }


#
# Function to wait for a file to arrive

waitfor() {
    wanted=$1
    if ((OPT_MAXWAIT)) ; then
        ((GIVEUP=SECONDS+OPT_MAXWAIT))
    else
        GIVEUP="-1"
    fi

    while   [[ ! -f $wanted && $SECONDS -lt $GIVEUP ]] ; do
        sleep 1
    done
    if [[ ! -f $wanted ]] ; then
        echo "FATAL ERROR:" timed out waiting for:  2>&1
        echo "            " "$wanted"               2>&1
        echo 
        print -p bye  2>/dev/null
        exit 2
    fi
    return 0
}


Last edited by Perderabo; 03-16-2004 at 04:28 PM..
  #3 (permalink)  
Old 11-23-2002
Perderabo's Avatar  
Perderabo Perderabo is offline Forum Staff  
Unix Daemon
        

Join Date: Aug 2001
Location: Ashburn, Virginia
Posts: 9,145
Award Winner HardFeed Part 2

Code:


#
#  Function to decode an "ls -l" line.

lsdcode() {

    typeset -Z2 nmonth day
    typeset -i8 octal

    #
    #  get the line, get the first character, split line into words

    line="$1"
    char1=${line%%${line#?}}
    IFS=" "
    set -A  things -- $line
    IFS=""

    #
    #  We may have a "total" line which needs to be ignored

    if [[ ${things[0]} = total ]] ; then
        set -A  lsdc --  skip 000 000000000000 x x
        return 0
    fi

    #
    #
    parser=1
    month=${things[5]}
    xmonth=$(conv_month $month)
    if  conv_month $month > /dev/null ; then
        parser=1
    else
        parser=0
    fi

    if ((parser)); then
        #
        # Strict Left to Right Parse Routine 
        #
        # Break out the fields that we want.  This technique requires
        # that the user, group, and size fields never run together and
        # so they must have at least one space between them.  But it 
        # allows some limited support of filenames with embedded spaces.

        echo "$line" | IFS=" " read permstring junk junk junk junk \
                        month day swing rawname
        if [[ $char1 = l ]] ; then
            link=${rawname#*-\> }
            name=${rawname% -\>*}
        else
            name="$rawname"
            link=""
        fi
    else
        #
        # Outside to Inside Parse Routine 
        #
        # Break out the fields that we want.  This technique requires 
        # that no white space exist in the filename.  But the user, 
        # group, and size  fields may sometimes run together without 
        # causing a problem.

        echo "WARNING:" badly formatted line in directory listing for:    >&2
        echo "        " "${line}"                                         >&2
        echo "        " attempting outside-to-inside scan                 >&2
        echo                                                              >&2

        ((pname=${#things[*]}-1))
        if [[ $char1 = l ]] ; then
            link=${things[pname]}
            ((pname=pname-2))
        else
            link=
        fi
        permstring=${things[0]}
        name=${things[pname]}
        month=${things[pname-3]}
        day=${things[pname-2]}
        swing=${things[pname-1]}
        if  conv_month $month > /dev/null ; then
            :
        else
            echo "ERROR: " outside-to-inside scan has also failed >&2
            echo "       " giving up on:                          >&2
            echo "       " "$line"                                >&2
            echo                                                  >&2
            set -A  lsdc --  skip 000 000000000000 x x
            return 0
        fi
    fi


    #
    #  Ignore . and ..

    if [[ $name = . || $name = .. ]] ; then
            set -A  lsdc --  skip 000 000000000000 x x
            return 0
    fi

    #
    #  decode permissions  (the permission string is first word

    set -A perms -- $(print -- ${permstring#?} | sed 's/./& /g')
    extras=0
    [[ ${perms[2]} = S ]] && { ((extras=extras+4000)); perms[2]=- ; }
    [[ ${perms[2]} = s ]] && { ((extras=extras+4000)); perms[2]=x ; }
    [[ ${perms[5]} = S ]] && { ((extras=extras+2000)); perms[5]=- ; }
    [[ ${perms[5]} = s ]] && { ((extras=extras+2000)); perms[5]=x ; }
    [[ ${perms[8]} = T ]] && { ((extras=extras+1000)); perms[8]=- ; }
    [[ ${perms[8]} = t ]] && { ((extras=extras+1000)); perms[8]=x ; }

    binary=2#$(print -- ${perms[@]} | sed 's/ //g;s/-/0/g;s/[^0]/1/g')
    ((octal=binary))
    result=$(echo $octal)
    result=${result#??}
    ((result=result+extras))

    #
    # Decode date and time and convert it to yyyymmddhhmm

    nmonth=$(conv_month $month)
    if [[ $swing = *:* ]] ; then
        if [[ $nmonth > $THISMONTH ]] ; then
            ((year=LASTYEAR))
        else
            ((year=THISYEAR))
        time1=${swing%???}
        time2=${swing#???}
        time="${time1}${time2}"
        fi
        else
                year=$swing
        time="0000"
        fi

    #
    #  Output the final record

    set -A lsdc -- ${char1} ${result} ${year}${nmonth}${day}${time} ${name} ${link}
    return
}


#
#  Function to process a remote file
#  We will not overwrite and existing file unless we in "freshen" mode.
#  And unless we are in "freshen" mode, it is an error for a file to
#  pre-exist.

process_remote_file() {
    VMESS="${VMESS} is a remote file that"
    do_get=0
    if [[ -f $name ]] ; then
        VMESS="${VMESS} already exists"
        if ((OPT_FRESHEN)) ; then
            line2=$(ls -ld "$name")
            lsdcode "$line2" 
            char12=${lsdc[0]}
            mode2=${lsdc[1]}
            datestamp2=${lsdc[2]}
            name2=${lsdc[3]}
            link2=${lsdc[4]}
            if [[ $datestamp > $datestamp2 ]] ; then
                VMESS="${VMESS} but is out-of-date and"
                do_get=1
            else
                VMESS="${VMESS} and is current"
            fi
        else
            VMESS="${VMESS} and cannot be retrieved"
            echo WARNING: no get since $name exists in ${localpath} >&2
        fi
    else
        do_get=1
    fi
    if ((do_get)) ; then
        print -p get \""$name"\"
        waitfor $name
        VMESS="${VMESS} has been retrieved"
        if ((OPT_MODE)) ; then
            chmod $mode "$name"
        fi
    fi
    if (($OPT_VERBOSE)) ; then
        echo "$VMESS"
    fi
    return 0
}


#  Function to process a remote directory
#  To this function, a remote directory is just an object that
#  may need to be duplicated in the current directory

process_remote_directory() {

    VMESS="${VMESS} is a remote directory that"
    if ((OPT_DIRECTORIES)) ; then
        if exists $name  ; then
            if [[ ! -d $name ]] ; then
                VMESS="${VMESS} cannot be created due to pre-existing object"
                echo WARNING: no mkdir since $name exists in ${localpath} >&2
            else
                VMESS="${VMESS} already exists"
            fi
        else
            mkdir "$name"
            VMESS="${VMESS} has been created locally"
            if ((OPT_MODE)) ; then
                chmod $mode "$name"
            fi
        fi
    else
        VMESS="${VMESS} has been ignored"
    fi
    if (($OPT_VERBOSE)) ; then
        echo "$VMESS"
    fi
    if ((OPT_RECURS)) ; then
        if [[ -d "$name" ]] ; then
            cd "$name"
            print -p lcd \""$name"\"
            exec 4<&-
            obtain_and_process_remote_ls "$name"
            print -p cd ..
            print -p lcd ..
            cd ..
            exec 4< ${DIR_FILE_NAME[LEV]}
            lineno=0
            while (( lineno != ${DIR_LINE_NUM[LEV]})) ; do
                read -u4 junk
                ((lineno=lineno+1))
            done
        fi
    fi
    return 0
}


#
#  Function to process a remote symlink
#  Note that we deal with th symlink only --  not
#  the object (if any) that the link points to.

process_remote_symlink() {
    VMESS="${VMESS} is a remote symlink that"
    if ((OPT_SYMLINKS)) ; then
        if exists "$name" ; then
            if [[ ! -L $name ]] ; then
                VMESS="${VMESS} cannot be created due to pre-existing object"
                echo WARNING: no symlink since $name exists in ${localpath} >&2
            else
                VMESS="${VMESS} already exists"
            fi
        else
            ln -s "$link" "$name"
            VMESS="${VMESS} has been duplicated locally"
        fi
    else
        VMESS="${VMESS} has been ignored"
    fi
    if (($OPT_VERBOSE)) ; then
        echo "$VMESS"
    fi
}


#
#  If a remote object is not a file, directory, or
#  symlink, we come here.  

process_remote_weirdo() {
    VMESS="${VMESS} is a remote unknown object that has been ignored"
    return 0
    }

#
#  This function obtains an "ls" listing from the remote ftp system.  Then it 
#  scans the listing line by line to figure out what to do.  It will completely 
#  process the current directory.

obtain_and_process_remote_ls() {

    typeset rdir tmpfile okfile   ## local scope variables ##
    rdir=$1

    #
    #  Set up variables or modify them if we have recursed

    ((LEV=LEV+1))
    tmpfile=/tmp/HardFeed.tp.$$.${LEV}
    okfile=/tmp/HardFeed.ok.$$.${LEV}
    if ((LEV == 1)) ; then
        localpath=$STARTPATH
        remotepath=$rdir
    else
        localpath=${localpath}/$rdir
        remotepath=${remotepath}/$rdir

    fi

    #
    #  Get a copy of the remote dir output in a local file
    #  called $tmpfile 

    print -p cd \""$rdir"\"
    print -p $OPT_DIRCMD $tmpfile
    print -p $OPT_DIRCMD $okfile
    waitfor $okfile
    DIR_FILE_NAME[LEV]=$tmpfile
    DIR_LINE_NUM[LEV]=0
    exec 4< $tmpfile

    #
    #  process each line
    #

    while read -u4 line ; do
        ((DIR_LINE_NUM[LEV]=${DIR_LINE_NUM[LEV]}+1))
        lsdcode "$line" 
        char1=${lsdc[0]}
        mode=${lsdc[1]}
        datestamp=${lsdc[2]}
        name=${lsdc[3]}
        link=${lsdc[4]}
        VMESS="${remotepath}/${name}"
        case $char1 in
        skip)   ;;
        -)      process_remote_file
            ;;
        d)      process_remote_directory
            ;;
        l)      process_remote_symlink
            ;;
        *)      process_remote_weirdo
            ;;
        esac
    done 

#
#  We may have recursed...so we must put everything back the way
#  we found it

    localpath=${localpath%$rdir}
    localpath=${localpath%/}
    remotepath=${remotepath%$rdir}
    remotepath=${remotepath%/}
    rm $tmpfile
    rm $okfile
    ((LEV=LEV-1))

    return 0
}


#
#  Main Program
#


ftp -inv >&3 2>&1 |&
print -p open $SYSTEM
print -p user $USER $PASSWORD
print -p binary

i=0
while ((OPT_CMDS>i)) ; do
    print -p ${OPT_CMDS_LIST[i]}
    ((i=i+1))
done

obtain_and_process_remote_ls $DIRECTORY

print -p bye
wait
exit 0

Generated by GNU enscript 1.6.4.