187 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			187 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
| #! /bin/bash
 | |
| #
 | |
| # original from:
 | |
| # @(#) ncp.ksh,nmv.ksh 1.1 94/07/23
 | |
| # 92/01/18 john h. dubois iii (john@armory.com)
 | |
| # 92/01/31 added check for no args left after shifts
 | |
| # 92/02/17 added help
 | |
| # 92/02/25 remove path component from filename before tacking it onto dest.
 | |
| # 92/03/15 exec mv or cp
 | |
| # 93/07/13 Added -i
 | |
| # 93/09/29 Made abort if file exists optional.
 | |
| # 93/11/19 Exit before invoking mv if no files to move
 | |
| # 94/01/03 Added o option
 | |
| # 94/04/13 Added x option.
 | |
| #          Fixed appending of source filename, broken by earlier change.
 | |
| # 94/07/23 Append only the filename part of the source path.
 | |
| #
 | |
| # conversion to bash v2 syntax done by Chet Ramey
 | |
| 
 | |
| false()
 | |
| {
 | |
| 	return 1
 | |
| }
 | |
| 
 | |
| true()
 | |
| {
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| phelp()
 | |
| {
 | |
| echo "$name: do a $cmd with extra checking and options.
 | |
| $Usage
 | |
| $name is used as a front end for $cmd to get the [icfo] options, and so
 | |
| that a trailing / will force the last component of the path to be
 | |
| interpreted as a directory, so that   $name foo bar/   will fail if bar is
 | |
| not an existing directory, instead of changing the name of foo to bar. 
 | |
| Effectively,  $name foo bar/   is short for  $name foo bar/foo
 | |
| Options: 
 | |
| -h prints this help.
 | |
| -c checks first for the existence of each file, and fails if it exists.
 | |
| -i is like -c except that if the file exists and stdin and stdout are a
 | |
|    tty, a query is printed and a reply is read; a file is overwritten only
 | |
|    if the reply begins with 'y'.
 | |
| -f unsets -c and -i (in case $cmd is aliased to $name).
 | |
| -o (overwrite only) checks that the named file(s) exist and fails for any
 | |
|    that do not.  It is the complement of the -c option.
 | |
| Whichever of [cifo] comes later on the command line determines the behaviour.
 | |
| Any of these options must come before any standard $cmd options."
 | |
| }
 | |
| 
 | |
| # interactive: Attempt to overwrite file should result in interactive
 | |
| # query rather than automatic failure.
 | |
| # noover: Do not overwrite files (if interactive is true, query, else fail)
 | |
| # overwrite: Only overwriting is allowed, not creation of new files.
 | |
| # debug: Print debugging info.
 | |
| typeset interactive=false noover=false overwrite=false debug=false
 | |
| name=${0##*/}
 | |
| 
 | |
| case "$name" in
 | |
| ncp|nmv) cmd=/bin/${name#?} ;;
 | |
| *) echo "$name: Must be invoked as ncp or nmv." 1>&2 ; exit 2;;
 | |
| esac
 | |
| 
 | |
| Usage="Usage: $name [-cfhio] $cmd-cmd-line"
 | |
| 
 | |
| while getopts :cfhiox opt; do
 | |
|     case $opt in
 | |
|     h) phelp; exit 0;;
 | |
|     x) debug=true ;;
 | |
|     c) noover=true ;;
 | |
|     i) noover=true ; interactive=true ;;
 | |
|     f) noover=false ; interactive=false ;;
 | |
|     o) overwrite=true ; noover=false ; interactive=false;;
 | |
|     +?) echo "$name: options should not be preceded by a '+'." 1>&2; exit 2;;
 | |
|     ?)  echo "$name: $OPTARG: bad option.  Use -h for help." 1>&2 ; exit 2;;
 | |
|     esac
 | |
| done
 | |
|  
 | |
| # remove args that were options
 | |
| shift $((OPTIND - 1))
 | |
| 
 | |
| if [ $# -lt 2 ]; then
 | |
|     echo -e "$Usage\nUse -h for help."
 | |
|     exit
 | |
| fi
 | |
| 
 | |
| Check()
 | |
| {
 | |
|     if [ ! -f "$1" ] && $overwrite; then
 | |
| 	echo "$name: $1: File does not exist." 1>&2
 | |
| 	return 1
 | |
|     elif [ -f "$1" ] && $noover; then
 | |
| 	if [ $interactive = false ] || [ ! -t 0 ] || [ ! -t 1 ]; then
 | |
| 	    echo "$name: $1: File exists." 1>&2
 | |
| 	    return 1
 | |
| 	else
 | |
| 	    while :; do
 | |
| 		echo -n \
 | |
| "$name: $1: File exists.  Overwrite? (y)es/(n)o/(a)bort/(Y)es for all: " 1>&2
 | |
| 		read reply
 | |
| 		case "$reply" in
 | |
| 		y*)
 | |
| 		    echo "$name: Overwriting $1."
 | |
| 		    return 0
 | |
| 		    ;;
 | |
| 		Y*)
 | |
| 		    echo "$name: Overwriting $1."
 | |
| 		    interactive=false
 | |
| 		    noover=false
 | |
| 		    return 0
 | |
| 		    ;;
 | |
| 		[nN]*)
 | |
| 		    echo "$name: Skipping $2."
 | |
| 		    return 1
 | |
| 		    ;;
 | |
| 		[aA]*)
 | |
| 		    echo "$name: Aborting."
 | |
| 		    exit 1
 | |
| 		    ;;
 | |
| 		*)
 | |
| 		    echo "$name: Invalid response." 1>&2
 | |
| 		    ;;
 | |
| 		esac
 | |
| 	    done
 | |
| 	fi
 | |
|     else
 | |
| 	return 0
 | |
|     fi
 | |
| }
 | |
| 
 | |
| # i is the index of the filename being examined
 | |
| # lastarg is the index of the last filename before the dest directory name
 | |
| typeset -i i=0 lastarg=$(($#-1))
 | |
| 
 | |
| # Sets argv[0..$#-1]
 | |
| argv=("$@")
 | |
| $debug && echo argv = "${argv[@]}" 1>&2
 | |
| dest=${argv[lastarg]}
 | |
| 
 | |
| if $debug; then
 | |
|     echo \
 | |
| "interactive=$interactive noover=$noover overwrite=$overwrite debug=$debug
 | |
| lastarg=$lastarg dest=$dest name=$name cmd=$cmd
 | |
| files=$*" 1>&2
 | |
| fi
 | |
| 
 | |
| if $noover || $overwrite; then
 | |
|     $debug && echo "checking for existance of directories..." 1>&2
 | |
|     # If the destination is not intended to be a directory...
 | |
|     if [ $# -eq 2 ] && [ ! -d "$dest" ]; then
 | |
| 	Check "$dest" "$1" || exit 0		# No files to copy
 | |
|     else
 | |
| 	while [ $i -lt $lastarg ]; do
 | |
| 	    Check "$dest/${argv[i]##*/}" "${argv[i]}" || unset argv[i]
 | |
| 	    let i+=1
 | |
| 	done
 | |
|     fi
 | |
| fi
 | |
| 
 | |
| [ ${#argv[@]} -lt 2 ] && exit 0
 | |
| 
 | |
| # If only 2 args are given, mv/cp will not insist that the destination
 | |
| # be a directory, which we want if the destination ends in "/" or if
 | |
| # the original number of args was >2.
 | |
| # $# is still the original number of args.
 | |
| # Tack the file name onto the destination to force this behaviour.
 | |
| 
 | |
| lastisslash()
 | |
| {
 | |
| 	case "$1" in
 | |
| 	*/)	return 0;;
 | |
| 	*)	return 1;;
 | |
| 	esac
 | |
| }
 | |
| 
 | |
| if [ ${#argv[@]} = 2 ] && { lastisslash "$2" || [ $# -gt 2 ]; }; then
 | |
|     $debug && echo "Appending filename." 1>&2
 | |
|     # Don't know which element of argv[] holds the source filename, 
 | |
|     # since may have started with more than 1 source file & had some unset.
 | |
|     # So, compact args to make it easy to find the set one.
 | |
|     argv=("${argv[@]}")
 | |
|     argv[1]="${argv[1]}/${argv[0]##*/}"
 | |
| fi
 | |
| 
 | |
| $debug && echo "Executing command: $cmd ${argv[@]}" 1>&2
 | |
| exec $cmd "${argv[@]}"
 | 
