views:

328

answers:

3

I get the following messages often, for instance when coping dev files to a master branch

cp: /Users/Masi/gitHub/shells/zsh/dvorak: No such file or directory
cp: /Users/Masi/gitHub/shells/zsh/dvorak2: No such file or directory

I would like to be asked about the creation of the given folders such that my initial command will be run if I answer yes to the question(s).

My attempt in pseudo-code when I am trying to copy a file to a directory which does not exists

if no such a directory exists, then asks users about to create it:
   if yes, then mkdir directory AND run the initial command again
   else do noting

Problems

  1. To change the warning message: Which file does control the "No such file or directory" -command?
  2. To scrape the Path in the initial command AND mkidr Path without the file: How would scrape the Path in the initial command?
  3. To scrape from the end with your chosen language such as AWK: How would you remove the last match in the Path when / is the field separator? I am not sure how you can scrape letters starting from the end with AWK.
+1  A: 

That error message is coming from the cp command, not zsh. If you want to improve the output, you're going to have to write the logic for truncating and examining the path along with checking to see if it exists or not.

There are commands to assist with this, have a look at basename(1) and dirname(1).

caskey
+1  A: 

Here is a function I wrote which will work in zsh, bash or ksh.

Note: It has debugging enabled (it echoes the commands it would run rather than executing them). If you comment out that line, it will actually run them.

Caution: It hasn't been thoroughly tested.

To use it, put this script in a file called cpmd in /usr/local/bin (or elsewhere in your path). To activate it, from the shell prompt type the following command (or add it to your startup script - for bash it would be ~/.bashrc):

source cpmd

Then you can copy a file using a command like this:

cpmd carparts /home/dave/Documents/nonexistent/newdir/

Neither directory "nonexistent" or "newdir" exist yet. Both directories are created then the file named "carparts" is copied to "newdir".

If you don't include a slash ("/") at the end, the last part is treated as a file name and any non-existent directories before that are created:

cpmd supplies /home/dave/Documents/anothernew/consumables

The directory "anothernew" is created then "supplies" is copied with the new filename "consumables".

If all the directories in the destination already exist, cpmd acts like the regular cp command.

function cpmd {
    # copies files and makes intermediate dest. directories if they don't exist
    # for bash, ksh or zsh
    # by Dennis Williamson - 2009-06-14
    # http://stackoverflow.com/questions/993266/unable-to-make-nosuchdirectory-message-useful-in-zsh

    # WARNING: no validation is performed on $1 and $2

    # all cp commands below are hardcoded with -i (interactive) to prevent overwriting

   if [[ -n $KSH_VERSION ]]
   then
       alias local=typeset
       local func="$0"
       local lastchar="${2: -1}"
       readcmd () { read "$2?$1"; }
    elif [[ -n $ZSH_VERSION ]]
    then
        local func="$0"
        # the following two lines are split up instead of doing "${2[-1]}"
        # to keep ksh from complaining when the function is loaded
        local dest="$2"
        local lastchar="${dest[-1]}"
        readcmd () { read "$2?$1"; }
    elif [[ -n $BASH_VERSION ]]
    then
    local func="$FUNCNAME"
        local lastchar="${2:(-1)}"
        readcmd () { read -p "$1" $2; }
    else
        echo "cpmd has only been tested in bash, ksh and zsh." >&2
        return 1
    fi

    local DEBUG='echo' # COMMENT THIS OUT to make this function actually work

    if [[ ${#@} != 2 ]]
    then
        echo "$func: invalid number of parameters
Usage:
  $func source destination

  where 'destination' can include nonexistent directories (which will
  be created). You must end 'destination' with a / in order for it to
  specify only directories. Without the final slash, the 'source' will
  be copied with a new name (the last portion of 'destination'). If you
  are copying multiple files and 'destination' is not a directory, the
  copy will fail." >&2
        return 1
    fi

    local dir=$(dirname "$2")
    local response
    local nl=$'\n'

    # destination ($2) is presumed to be in one of the following formats:
    # .../existdir              test 1  (-d "$2")
    # .../existdir/existfile    test 2  (-f "$2")
    # .../existdir/newfile      test 3  (-d "$dir" && $lastchar != '/')
    # .../existdir/newdir/      (else)
    # .../newdir/newdir/        (else)
    # .../newdir/newfile        (else)

    if [[ -d "$2" || -f "$2" || (-d "$dir" && $lastchar != '/') ]]
    then
        $DEBUG cp -i "$1" "$2"
    else
        if [[ $lastchar == '/' ]]
        then
            dir="$2"
        fi
        local prompt="$func: The destination directory...${nl}  ${dir}${nl}...does not exist. Create? (y/n): "
        while [[ -z $response ]]
        do
            readcmd "$prompt" response
            case $response in
                y|Y) response="Y" ;;
                n|N) ;;
                *) response=
                   prompt="$func: Invalid response.${nl}  Create destination directory? (y/n): ";;
            esac
        done
        if [[ $response == "Y" ]]
        then
            $DEBUG mkdir -p "$dir" && $DEBUG cp -i "$1" "$2"
        else
            echo "$func: Cancelled." >&2
        fi
    fi
}
Dennis Williamson
@Dennis: I cannot run your code in OS/X. I put it to /usr/local/cpmd/ and to my PATH. It does not get activated when I copy a file to a directory which does not exist.
Masi
@Masi: I've updated my answer with more complete directions. Did you put the file in /usr/local/cpmd/ and add that directory to your PATH? I would recommend /usr/local/bin instead. Did you get any messages or error messages? If it printed out commands but didn't run them, that's because you haven't commented out the debug line. If you got a message like "command not found" it's because you didn't source the file. See the additional instructions for more info.
Dennis Williamson
@Dennis: Thank you for your answer!
Masi
A: 

thanks for putting this together! I'm thinking rsync may be an easier solution (ymmv)