【问题标题】:Is there a way to make mv create the directory to be moved to if it doesn't exist?有没有办法让 mv 创建要移动到的目录,如果它不存在?
【发布时间】:2010-10-07 13:22:55
【问题描述】:

所以,如果我在我的主目录中并且我想将 foo.c 移动到 ~/bar/baz/foo.c ,但这些目录不存在,有没有办法让这些目录自动创建,这样您只需键入

mv foo.c ~/bar/baz/ 

一切都会好起来的?似乎您可以将 mv 别名为一个简单的 bash 脚本,该脚本将检查这些目录是否存在,如果不存在则调用 mkdir 然后调用 mv,但我想我会检查是否有人有更好的主意。

【问题讨论】:

标签: linux macos unix mkdir mv


【解决方案1】:

这个单线(在 bash 中)怎么样:

mkdir --parents ./some/path/; mv yourfile.txt $_

分解:

mkdir --parents ./some/path
# if it doesn't work; try
mkdir -p ./some/path

创建目录(包括所有中间目录),之后:

mv yourfile.txt $_

将文件移动到该目录($_ 扩展为传递给上一个 shell 命令的最后一个参数,即:新创建的目录)。

我不确定这在其他 shell 中能发挥多大作用,但它可能会给你一些关于寻找什么的想法。

以下是使用此技术的示例:

$ > ls
$ > touch yourfile.txt
$ > ls
yourfile.txt
$ > mkdir --parents ./some/path/; mv yourfile.txt $_
$ > ls -F
some/
$ > ls some/path/
yourfile.txt

【讨论】:

  • 同意,但这是一个不太通用的解决方案。我的解决方案可以作为 mv 替代品(因此是 bash 中的别名)(假设它工作正常——仍然未经测试!)。
  • 为什么到处都沉迷于多余的./? args 不是 PATH 中没有 . 目录的命令...
  • 对于新手,--parents 可以缩写为 -p
  • 奇怪的是,--parents 在 macOS 10.13.2 上不可用。您需要使用-p
  • 在移动文件时不起作用,例如 ./some/path/foo。在这种情况下,命令应该是:mkdir -p ./some/path/foo; mv yourfile.txt $_:h
【解决方案2】:
mkdir -p `dirname /destination/moved_file_name.txt`  
mv /full/path/the/file.txt  /destination/moved_file_name.txt

【讨论】:

  • 我是唯一一个意识到这段代码没有意义的人?您递归地创建现有文件的绝对路径(这意味着该路径已经存在),然后将文件移动到一个位置(在您的示例中不存在)。
  • @user544262772 GNU coreutils' dirname 不需要它的参数存在,它是纯粹的字符串操作。不能代表其他发行版。这段代码没有任何问题。
  • 我认为这是最容易自动化的答案。 for f in *.txt; do mkdir -p `dirname /destination/${f//regex/repl}`; mv "$f" "/destination/${f//regex/repl}; done
【解决方案3】:

另存为名为 mv.sh 的脚本

#!/bin/bash
# mv.sh
dir="$2" # Include a / at the end to indicate directory (not filename)
tmp="$2"; tmp="${tmp: -1}"
[ "$tmp" != "/" ] && dir="$(dirname "$2")"
[ -a "$dir" ] ||
mkdir -p "$dir" &&
mv "$@"

或者在你的 ~/.bashrc 文件的末尾放置一个函数来替换每个新终端上的默认 mv。使用函数可以让 bash 保留它的内存,而不必每次都读取脚本文件。

function mvp ()
{
    dir="$2" # Include a / at the end to indicate directory (not filename)
    tmp="$2"; tmp="${tmp: -1}"
    [ "$tmp" != "/" ] && dir="$(dirname "$2")"
    [ -a "$dir" ] ||
    mkdir -p "$dir" &&
    mv "$@"
}

示例用法:

mv.sh file ~/Download/some/new/path/ # <-End with slash

这些基于 Chris Lutz 的提交。

【讨论】:

  • 这回答了最准确的问题恕我直言。用户想要一个增强的mv 命令。对于脚本编写特别有用,即您不必在需要使用mv 的任何时候运行检查并运行mkdir -p。但是因为我想要mv 的默认错误行为,所以我将函数名称更改为mvp——这样我就知道什么时候可以创建目录了。
  • 似乎是最好的解决方案,但实现时经常崩溃。
  • @UlisesLayera 我修改了算法以使其更加健壮。它现在不应该崩溃
  • 有人能解释一下[ ! -a "$dir" ]的含义吗?我做过实验,右半边是真假,都评价为真..???为他人着想, tmp="${tmp: -1}" 似乎抓住了文件的最后一个字符以确保它不是路径(/)
  • @bazz 应该是“如果 $dir 不存在,则继续”。不幸的是,你是对的,使用“!-a”时有一个错误,我不知道为什么。我已经对代码进行了一些编辑,现在应该可以工作了。
【解决方案4】:

你可以使用mkdir:

mkdir -p ~/bar/baz/ && \
mv foo.c ~/bar/baz/

一个自动执行的简单脚本(未经测试):

#!/bin/sh

# Grab the last argument (argument number $#)    
eval LAST_ARG=\$$#

# Strip the filename (if it exists) from the destination, getting the directory
DIR_NAME=`echo $2 | sed -e 's_/[^/]*$__'`

# Move to the directory, making the directory if necessary
mkdir -p "$DIR_NAME" || exit
mv "$@"

【讨论】:

  • 当我运行“$ dirname ~?bar/baz/”时,我得到“/home/dmckee/bar”,这不是你想要的...
  • @dmckee,啊,你是对的。关于如何解决这个问题的任何想法?如果您输入 ~/bar/baz,则 (a) 想要复制到 ~/bar/ 并重命名为 baz,或者 (b) 复制到 ~/bar/baz/。什么是更好的工作工具?
  • @dmckee,我使用 regexp/sed 提出了解决方案。你喜欢吗? =]
  • 很好,除了 $2 不是最后一个参数,除非 $# = 2。我有一个程序,la,它打印它的最后一个参数。我曾经在 cp 命令的一个版本中使用它(如果最后一个参数不是目录,则添加当前目录)。即使是现代贝壳也只支持 1..9 美元; Perl 脚本可能会更好。
  • @Leffler,不正确——我知道 bash 支持超过 9 个参数(使用 ${123} 是一种方法)。我不知道 Perl,所以请随意自己回答。 =]
【解决方案5】:

听起来答案是否定的:)。我真的不想为此创建别名或函数,通常是因为它是一次性的,而且我已经在输入mv 命令的过程中,但我发现了一些很适合的东西:

mv *.sh  shell_files/also_with_subdir/ || mkdir -p $_

如果mv 失败(dir 不存在),它将创建目录(这是上一个命令的最后一个参数,所以$_ 有它)。所以只要运行这个命令,然后 up 重新运行它,这次mv应该会成功。

【讨论】:

    【解决方案6】:

    最简单的方法是:

    mkdir [directory name] && mv [filename] $_
    

    假设我下载了位于我的下载目录 (~/download) 中的 pdf 文件,并且我想将它们全部移动到一个不存在的目录中 (比如 my_PDF)。

    我将输入以下命令(确保我当前的工作目录是~/download):

    mkdir my_PDF && mv *.pdf $_
    

    如果你想像这样创建子目录,可以在mkdir中添加-p选项:(假设我想创建一个名为python的子目录):

    mkdir -p my_PDF/python && mv *.pdf $_
    

    【讨论】:

      【解决方案7】:

      利用"Getting the last argument passed to a shell script" 中的技巧,我们可以制作一个简单的shell 函数,无论您要移动多少文件,该函数都应该工作:

      # Bash only
      mvdir() { mkdir -p "${@: -1}" && mv "$@"; }
      
      # Other shells may need to search for the last argument
      mvdir() { for last; do true; done; mkdir -p "$last" && mv "$@"; }
      

      使用如下命令:

      mvdir foo.c foo.h ~/some/new/folder/
      

      【讨论】:

        【解决方案8】:

        rsync 命令只有在目标路径中的最后一个目录不存在时才可以解决问题,例如对于~/bar/baz/的目标路径,如果bar存在但baz不存在,则可以使用以下命令:

        rsync -av --remove-source-files foo.c ~/bar/baz/

        -a, --archive               archive mode; equals -rlptgoD (no -H,-A,-X)
        -v, --verbose               increase verbosity
        --remove-source-files   sender removes synchronized files (non-dir)
        

        在这种情况下baz 目录如果不存在将被创建。但是如果barbaz 都不存在,rsync 将会失败:

        sending incremental file list
        rsync: mkdir "/root/bar/baz" failed: No such file or directory (2)
        rsync error: error in file IO (code 11) at main.c(657) [Receiver=3.1.2]
        

        所以基本上使用rsync -av --remove-source-files 作为mv 的别名应该是安全的。

        【讨论】:

          【解决方案9】:

          也许是下面的 shell 脚本?

          #!/bin/sh
          if [[ -e $1 ]]
          then
            if [[ ! -d $2 ]]
            then
              mkdir --parents $2
            fi
          fi
          mv $1 $2
          

          这是基本部分。您可能需要添加一点来检查参数,并且您可能希望在目标存在、源目录存在或不存在时改变行为(即不要覆盖不存在的东西) .

          【讨论】:

          • 使用您的代码,如果目录不存在,则不会执行移动!
          • 你错过了 mkdir 的“-p”标志。
          • 如果一个目录不存在,如果你只是要移动它,为什么还要创建它呢? (-p 标志固定)
          • 我的意思是,当您运行脚本并且目录 $2 不存在时,会创建它但不会复制文件。
          • 他给出了一个简单的结构,并添加了关于如何扩展它的信息。为什么要投反对票?!
          【解决方案10】:

          更傻,但工作方式:

          mkdir -p $2
          rmdir $2
          mv $1 $2
          

          使用 mkdir -p 创建包含共享目标文件名的临时目录的目录,然后使用简单的 rmdir 删除该文件名目录,然后将文件移动到新目标。 我认为使用 dirname 的答案可能是最好的。

          【讨论】:

          • 是的,有点傻。:-) 如果 $2 是一个目录(如在原始问题中),那么它会惨遭失败,因为最后一个目录将被删除,所以 'mv' 将失败。如果 $2 已经存在,那么它也会尝试删除它。
          • 谢谢!这正是我需要完成的: mv ./old ./subdir/new where subdir 还不存在
          【解决方案11】:

          这会将foo.c 移动到带有父目录bar 的新目录baz

          mv foo.c `mkdir -p ~/bar/baz/ && echo $_`
          

          mkdir-p 选项将根据需要创建中间目录。
          如果没有-p,路径前缀中的所有目录都必须已经存在。

          反引号`` 中的所有内容都将被执行,并且输出会作为命令的一部分内联返回。
          由于mkdir 不返回任何内容,因此只会将echo $_ 的输出添加到命令中。

          $_ 引用先前执行命令的最后一个参数。
          在这种情况下,它将返回传递给 mkdir 命令的新目录 (~/bar/baz/) 的路径。


          我在没有给出目的地的情况下解压缩了一个存档,并且想将除demo-app.zip 之外的所有文件从我的当前目录移动到一个名为demo-app 的新目录。
          以下行可以解决问题:
          mv `ls -A | grep -v demo-app.zip` `mkdir -p demo-app && echo $_`
          

          ls -A 返回所有文件名,包括隐藏文件(除了隐式的...)。

          管道符号| 用于将ls 命令的输出通过管道传输到grep命令行、纯文本搜索实用程序)。
          -v 标志指示 grep 查找并返回除 demo-app.zip 之外的所有文件名。
          该文件列表作为移动命令mv 的源参数添加到我们的命令行中。目标参数是传递给mkdir 的新目录的路径,使用$_ 引用并使用echo 输出。

          【讨论】:

            【解决方案12】:

            根据另一个答案中的评论,这是我的 shell 函数。

            # mvp = move + create parents
            function mvp () {
                source="$1"
                target="$2"
                target_dir="$(dirname "$target")"
                mkdir --parents $target_dir; mv $source $target
            }
            

            将此包含在 .bashrc 或类似文件中,以便您可以在任何地方使用它。

            【讨论】:

              【解决方案13】:
              ((cd src-path && tar --remove-files -cf - files-to-move) | ( cd dst-path && tar -xf -))
              

              【讨论】:

                【解决方案14】:

                我在将文件批量移动到新的子目录时经常偶然发现这个问题。理想情况下,我想这样做:

                mv * newdir/  
                

                此线程中的大多数答案都建议mkdir,然后是mv,但这会导致:

                mkdir newdir && mv * newdir 
                mv: cannot move 'newdir/' to a subdirectory of itself
                

                我面临的问题略有不同,因为我想全面移动所有内容,并且,如果我在移动之前创建新目录,那么它也会尝试将新目录移动到自身。所以,我通过使用父目录来解决这个问题:

                mkdir ../newdir && mv * ../newdir && mv ../newdir .
                

                注意事项:在根文件夹 (/) 中不起作用。

                【讨论】:

                • 这个答案更像是对任何偶然发现问题的特定子案例的人的注意。在到达这个确切的 SO 问题后,我不得不多次重新考虑这个解决方案,所以我决定在这里发布它以供将来参考。不过,它可能不属于这里。
                【解决方案15】:

                代码:

                if [[ -e $1 && ! -e $2 ]]; then
                   mkdir --parents --verbose -- "$(dirname -- "$2")"
                fi
                mv --verbose -- "$1" "$2"
                

                例子:

                参数:“d1”“d2/sub”

                mkdir: created directory 'd2'
                renamed 'd1' -> 'd2/sub'
                

                【讨论】:

                  【解决方案16】:

                  我的一串解决方案:

                  test -d "/home/newdir/" || mkdir -p "/home/newdir/" && mv /home/test.txt /home/newdir/
                  

                  【讨论】:

                    【解决方案17】:

                    我用 linux 上的install 命令完成了这个:

                    root@logstash:# myfile=bash_history.log.2021-02-04.gz ; install -v -p -D $myfile /tmp/a/b/$myfile
                    
                    bash_history.log.2021-02-04.gz -> /tmp/a/b/bash_history.log.2021-02-04.gz
                    

                    唯一的缺点是文件权限被更改:

                    root@logstash:# ls -lh /tmp/a/b/
                    
                    -rwxr-xr-x 1 root root 914 Fev  4 09:11 bash_history.log.2021-02-04.gz
                    

                    如果你不介意重置权限,你可以使用:

                    -g, --group=GROUP   set group ownership, instead of process' current group
                    -m, --mode=MODE     set permission mode (as in chmod), instead of rwxr-xr-x
                    -o, --owner=OWNER   set ownership (super-user only)
                    

                    【讨论】:

                      【解决方案18】:

                      您甚至可以使用大括号扩展:

                      mkdir -p directory{1..3}/subdirectory{1..3}/subsubdirectory{1..2}      
                      
                      • 创建 3 个目录(directory1、directory2、directory3),
                        • 并且在其中的每一个中都有两个子目录(子目录 1、子目录 2),
                          • 每个目录下都有两个子子目录(子子目录 1 和子子目录 2)。

                      您必须使用 bash 3.0 或更新版本。

                      【讨论】:

                      • 有趣,但没有回答 OP 的问题。
                      【解决方案19】:
                      $what=/path/to/file;
                      $dest=/dest/path;
                      
                      mkdir -p "$(dirname "$dest")";
                      mv "$what" "$dest"
                      

                      【讨论】:

                      • 要使其成为独立的 sh 脚本,只需将 $dest 和 $src 替换为 $1 和 $2
                      猜你喜欢
                      • 1970-01-01
                      • 2022-01-19
                      • 2014-06-24
                      • 2015-09-23
                      • 2020-12-07
                      • 2016-04-28
                      • 1970-01-01
                      • 2019-08-06
                      • 2021-12-12
                      相关资源
                      最近更新 更多