| 1 | #! /bin/bash
|
|---|
| 2 | #
|
|---|
| 3 | # original from:
|
|---|
| 4 | # @(#) ncp.ksh,nmv.ksh 1.1 94/07/23
|
|---|
| 5 | # 92/01/18 john h. dubois iii ([email protected])
|
|---|
| 6 | # 92/01/31 added check for no args left after shifts
|
|---|
| 7 | # 92/02/17 added help
|
|---|
| 8 | # 92/02/25 remove path component from filename before tacking it onto dest.
|
|---|
| 9 | # 92/03/15 exec mv or cp
|
|---|
| 10 | # 93/07/13 Added -i
|
|---|
| 11 | # 93/09/29 Made abort if file exists optional.
|
|---|
| 12 | # 93/11/19 Exit before invoking mv if no files to move
|
|---|
| 13 | # 94/01/03 Added o option
|
|---|
| 14 | # 94/04/13 Added x option.
|
|---|
| 15 | # Fixed appending of source filename, broken by earlier change.
|
|---|
| 16 | # 94/07/23 Append only the filename part of the source path.
|
|---|
| 17 | #
|
|---|
| 18 | # conversion to bash v2 syntax done by Chet Ramey
|
|---|
| 19 |
|
|---|
| 20 | false()
|
|---|
| 21 | {
|
|---|
| 22 | return 1
|
|---|
| 23 | }
|
|---|
| 24 |
|
|---|
| 25 | true()
|
|---|
| 26 | {
|
|---|
| 27 | return 0
|
|---|
| 28 | }
|
|---|
| 29 |
|
|---|
| 30 | phelp()
|
|---|
| 31 | {
|
|---|
| 32 | echo "$name: do a $cmd with extra checking and options.
|
|---|
| 33 | $Usage
|
|---|
| 34 | $name is used as a front end for $cmd to get the [icfo] options, and so
|
|---|
| 35 | that a trailing / will force the last component of the path to be
|
|---|
| 36 | interpreted as a directory, so that $name foo bar/ will fail if bar is
|
|---|
| 37 | not an existing directory, instead of changing the name of foo to bar.
|
|---|
| 38 | Effectively, $name foo bar/ is short for $name foo bar/foo
|
|---|
| 39 | Options:
|
|---|
| 40 | -h prints this help.
|
|---|
| 41 | -c checks first for the existence of each file, and fails if it exists.
|
|---|
| 42 | -i is like -c except that if the file exists and stdin and stdout are a
|
|---|
| 43 | tty, a query is printed and a reply is read; a file is overwritten only
|
|---|
| 44 | if the reply begins with 'y'.
|
|---|
| 45 | -f unsets -c and -i (in case $cmd is aliased to $name).
|
|---|
| 46 | -o (overwrite only) checks that the named file(s) exist and fails for any
|
|---|
| 47 | that do not. It is the complement of the -c option.
|
|---|
| 48 | Whichever of [cifo] comes later on the command line determines the behaviour.
|
|---|
| 49 | Any of these options must come before any standard $cmd options."
|
|---|
| 50 | }
|
|---|
| 51 |
|
|---|
| 52 | # interactive: Attempt to overwrite file should result in interactive
|
|---|
| 53 | # query rather than automatic failure.
|
|---|
| 54 | # noover: Do not overwrite files (if interactive is true, query, else fail)
|
|---|
| 55 | # overwrite: Only overwriting is allowed, not creation of new files.
|
|---|
| 56 | # debug: Print debugging info.
|
|---|
| 57 | typeset interactive=false noover=false overwrite=false debug=false
|
|---|
| 58 | name=${0##*/}
|
|---|
| 59 |
|
|---|
| 60 | case "$name" in
|
|---|
| 61 | ncp|nmv) cmd=/bin/${name#?} ;;
|
|---|
| 62 | *) echo "$name: Must be invoked as ncp or nmv." 1>&2 ; exit 2;;
|
|---|
| 63 | esac
|
|---|
| 64 |
|
|---|
| 65 | Usage="Usage: $name [-cfhio] $cmd-cmd-line"
|
|---|
| 66 |
|
|---|
| 67 | while getopts :cfhiox opt; do
|
|---|
| 68 | case $opt in
|
|---|
| 69 | h) phelp; exit 0;;
|
|---|
| 70 | x) debug=true ;;
|
|---|
| 71 | c) noover=true ;;
|
|---|
| 72 | i) noover=true ; interactive=true ;;
|
|---|
| 73 | f) noover=false ; interactive=false ;;
|
|---|
| 74 | o) overwrite=true ; noover=false ; interactive=false;;
|
|---|
| 75 | +?) echo "$name: options should not be preceded by a '+'." 1>&2; exit 2;;
|
|---|
| 76 | ?) echo "$name: $OPTARG: bad option. Use -h for help." 1>&2 ; exit 2;;
|
|---|
| 77 | esac
|
|---|
| 78 | done
|
|---|
| 79 |
|
|---|
| 80 | # remove args that were options
|
|---|
| 81 | shift $((OPTIND - 1))
|
|---|
| 82 |
|
|---|
| 83 | if [ $# -lt 2 ]; then
|
|---|
| 84 | echo -e "$Usage\nUse -h for help."
|
|---|
| 85 | exit
|
|---|
| 86 | fi
|
|---|
| 87 |
|
|---|
| 88 | Check()
|
|---|
| 89 | {
|
|---|
| 90 | if [ ! -f "$1" ] && $overwrite; then
|
|---|
| 91 | echo "$name: $1: File does not exist." 1>&2
|
|---|
| 92 | return 1
|
|---|
| 93 | elif [ -f "$1" ] && $noover; then
|
|---|
| 94 | if [ $interactive = false ] || [ ! -t 0 ] || [ ! -t 1 ]; then
|
|---|
| 95 | echo "$name: $1: File exists." 1>&2
|
|---|
| 96 | return 1
|
|---|
| 97 | else
|
|---|
| 98 | while :; do
|
|---|
| 99 | echo -n \
|
|---|
| 100 | "$name: $1: File exists. Overwrite? (y)es/(n)o/(a)bort/(Y)es for all: " 1>&2
|
|---|
| 101 | read reply
|
|---|
| 102 | case "$reply" in
|
|---|
| 103 | y*)
|
|---|
| 104 | echo "$name: Overwriting $1."
|
|---|
| 105 | return 0
|
|---|
| 106 | ;;
|
|---|
| 107 | Y*)
|
|---|
| 108 | echo "$name: Overwriting $1."
|
|---|
| 109 | interactive=false
|
|---|
| 110 | noover=false
|
|---|
| 111 | return 0
|
|---|
| 112 | ;;
|
|---|
| 113 | [nN]*)
|
|---|
| 114 | echo "$name: Skipping $2."
|
|---|
| 115 | return 1
|
|---|
| 116 | ;;
|
|---|
| 117 | [aA]*)
|
|---|
| 118 | echo "$name: Aborting."
|
|---|
| 119 | exit 1
|
|---|
| 120 | ;;
|
|---|
| 121 | *)
|
|---|
| 122 | echo "$name: Invalid response." 1>&2
|
|---|
| 123 | ;;
|
|---|
| 124 | esac
|
|---|
| 125 | done
|
|---|
| 126 | fi
|
|---|
| 127 | else
|
|---|
| 128 | return 0
|
|---|
| 129 | fi
|
|---|
| 130 | }
|
|---|
| 131 |
|
|---|
| 132 | # i is the index of the filename being examined
|
|---|
| 133 | # lastarg is the index of the last filename before the dest directory name
|
|---|
| 134 | typeset -i i=0 lastarg=$(($#-1))
|
|---|
| 135 |
|
|---|
| 136 | # Sets argv[0..$#-1]
|
|---|
| 137 | argv=("$@")
|
|---|
| 138 | $debug && echo argv = "${argv[@]}" 1>&2
|
|---|
| 139 | dest=${argv[lastarg]}
|
|---|
| 140 |
|
|---|
| 141 | if $debug; then
|
|---|
| 142 | echo \
|
|---|
| 143 | "interactive=$interactive noover=$noover overwrite=$overwrite debug=$debug
|
|---|
| 144 | lastarg=$lastarg dest=$dest name=$name cmd=$cmd
|
|---|
| 145 | files=$*" 1>&2
|
|---|
| 146 | fi
|
|---|
| 147 |
|
|---|
| 148 | if $noover || $overwrite; then
|
|---|
| 149 | $debug && echo "checking for existance of directories..." 1>&2
|
|---|
| 150 | # If the destination is not intended to be a directory...
|
|---|
| 151 | if [ $# -eq 2 ] && [ ! -d "$dest" ]; then
|
|---|
| 152 | Check "$dest" "$1" || exit 0 # No files to copy
|
|---|
| 153 | else
|
|---|
| 154 | while [ $i -lt $lastarg ]; do
|
|---|
| 155 | Check "$dest/${argv[i]##*/}" "${argv[i]}" || unset argv[i]
|
|---|
| 156 | let i+=1
|
|---|
| 157 | done
|
|---|
| 158 | fi
|
|---|
| 159 | fi
|
|---|
| 160 |
|
|---|
| 161 | [ ${#argv[@]} -lt 2 ] && exit 0
|
|---|
| 162 |
|
|---|
| 163 | # If only 2 args are given, mv/cp will not insist that the destination
|
|---|
| 164 | # be a directory, which we want if the destination ends in "/" or if
|
|---|
| 165 | # the original number of args was >2.
|
|---|
| 166 | # $# is still the original number of args.
|
|---|
| 167 | # Tack the file name onto the destination to force this behaviour.
|
|---|
| 168 |
|
|---|
| 169 | lastisslash()
|
|---|
| 170 | {
|
|---|
| 171 | case "$1" in
|
|---|
| 172 | */) return 0;;
|
|---|
| 173 | *) return 1;;
|
|---|
| 174 | esac
|
|---|
| 175 | }
|
|---|
| 176 |
|
|---|
| 177 | if [ ${#argv[@]} = 2 ] && { lastisslash "$2" || [ $# -gt 2 ]; }; then
|
|---|
| 178 | $debug && echo "Appending filename." 1>&2
|
|---|
| 179 | # Don't know which element of argv[] holds the source filename,
|
|---|
| 180 | # since may have started with more than 1 source file & had some unset.
|
|---|
| 181 | # So, compact args to make it easy to find the set one.
|
|---|
| 182 | argv=("${argv[@]}")
|
|---|
| 183 | argv[1]="${argv[1]}/${argv[0]##*/}"
|
|---|
| 184 | fi
|
|---|
| 185 |
|
|---|
| 186 | $debug && echo "Executing command: $cmd ${argv[@]}" 1>&2
|
|---|
| 187 | exec $cmd "${argv[@]}"
|
|---|