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 (john@armory.com) 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 20false() 21{ 22 return 1 23} 24 25true() 26{ 27 return 0 28} 29 30phelp() 31{ 32echo "$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 35that a trailing / will force the last component of the path to be 36interpreted as a directory, so that $name foo bar/ will fail if bar is 37not an existing directory, instead of changing the name of foo to bar. 38Effectively, $name foo bar/ is short for $name foo bar/foo 39Options: 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. 48Whichever of [cifo] comes later on the command line determines the behaviour. 49Any 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. 57typeset interactive=false noover=false overwrite=false debug=false 58name=${0##*/} 59 60case "$name" in 61ncp|nmv) cmd=/bin/${name#?} ;; 62*) echo "$name: Must be invoked as ncp or nmv." 1>&2 ; exit 2;; 63esac 64 65Usage="Usage: $name [-cfhio] $cmd-cmd-line" 66 67while 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 78done 79 80# remove args that were options 81shift $((OPTIND - 1)) 82 83if [ $# -lt 2 ]; then 84 echo -e "$Usage\nUse -h for help." 85 exit 86fi 87 88Check() 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 134typeset -i i=0 lastarg=$(($#-1)) 135 136# Sets argv[0..$#-1] 137argv=("$@") 138$debug && echo argv = "${argv[@]}" 1>&2 139dest=${argv[lastarg]} 140 141if $debug; then 142 echo \ 143"interactive=$interactive noover=$noover overwrite=$overwrite debug=$debug 144lastarg=$lastarg dest=$dest name=$name cmd=$cmd 145files=$*" 1>&2 146fi 147 148if $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 159fi 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 169lastisslash() 170{ 171 case "$1" in 172 */) return 0;; 173 *) return 1;; 174 esac 175} 176 177if [ ${#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]##*/}" 184fi 185 186$debug && echo "Executing command: $cmd ${argv[@]}" 1>&2 187exec $cmd "${argv[@]}" 188