如何在bash / zsh / ksh中复制期间创建目录?

时间:2021-09-14 20:47:36

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. 要更改警告消息:哪个文件控制“没有这样的文件或目录”命令?

  3. To scrape the Path in the initial command AND mkidr Path without the file: How would scrape the Path in the initial command?
  4. 在初始命令和没有文件的mkidr路径中刮取路径:如何在初始命令中刮取路径?

  5. 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.
  6. 使用您选择的语言(如AWK)从末尾开始:当/是字段分隔符时,如何删除路径中的最后一个匹配?我不知道你怎么能用AWK从头开始刮字母。

2 个解决方案

#1


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

这是我写的一个函数,可以在zsh,bash或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):

要使用它,请将此脚本放在/ usr / local / bin(或路径中的其他位置)中名为cpmd的文件中。要激活它,请在shell提示符下键入以下命令(或将其添加到启动脚本中 - 对于bash,它将是〜/ .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".

目录“不存在”或“newdir”尚未存在。创建两个目录,然后将名为“carparts”的文件复制到“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".

创建目录“anothernew”,然后使用新文件名“耗材”复制“耗材”。

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

如果目标中的所有目录都已存在,则cpmd的作用类似于常规cp命令。

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://*.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
}

#2


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.

该错误消息来自cp命令,而不是zsh。如果要改进输出,则必须编写截断和检查路径的逻辑,并检查它是否存在。

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

有一些命令可以帮助解决这个问题,看一下basename(1)和dirname(1)。

#1


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

这是我写的一个函数,可以在zsh,bash或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):

要使用它,请将此脚本放在/ usr / local / bin(或路径中的其他位置)中名为cpmd的文件中。要激活它,请在shell提示符下键入以下命令(或将其添加到启动脚本中 - 对于bash,它将是〜/ .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".

目录“不存在”或“newdir”尚未存在。创建两个目录,然后将名为“carparts”的文件复制到“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".

创建目录“anothernew”,然后使用新文件名“耗材”复制“耗材”。

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

如果目标中的所有目录都已存在,则cpmd的作用类似于常规cp命令。

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://*.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
}

#2


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.

该错误消息来自cp命令,而不是zsh。如果要改进输出,则必须编写截断和检查路径的逻辑,并检查它是否存在。

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

有一些命令可以帮助解决这个问题,看一下basename(1)和dirname(1)。