有什么方法可以让mv在不存在的情况下创建要移动的目录?

375 人关注

那么,如果我在我的主目录中,我想把foo.c移到~/bar/baz/foo.c,但这些目录并不存在,是否有办法让这些目录自动创建,这样你只需要输入

mv foo.c ~/bar/baz/ 

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

linux
macos
unix
mkdir
mv
Paul Wicks
Paul Wicks
发布于 2009-02-14
20 个回答
KarstenF
KarstenF
发布于 2016-06-30
已采纳
0 人赞同

这个单行本如何(在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命令所传递的最后一个参数,即:新创建的目录)。

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

下面是一个使用这种技术的例子。

$ > touch yourfile.txt yourfile.txt $ > mkdir --parents ./some/path/; mv yourfile.txt $_ $ > ls -F some/ $ > ls some/path/ yourfile.txt
同意,但这是一个不太通用的解决方案。 我的解决方案可以作为mv的变体(因此是bash中的别名)(假设它能正常工作--仍未测试!)。
Jens
为什么对到处都是多余的 ./ 念念不忘?args不是PATH中没有 . 目录的命令...
对于新手来说,--父母可以简称为-p
奇怪的是, --parents 在macOS 10.13.2上是不可用的。你需要使用 -p
如果移动一个文件,例如./some/path/foo,则不起作用。在这种情况下,命令应该是。 mkdir -p ./some/path/foo; mv yourfile.txt $_:h
Billmc
Billmc
发布于 2016-06-30
0 人赞同
mkdir -p `dirname /destination/moved_file_name.txt`  
mv /full/path/the/file.txt  /destination/moved_file_name.txt
    
我是唯一一个意识到这段代码没有意义的人吗?你递归地创建了现有文件的绝对路径(这意味着这个路径已经存在),然后将文件移动到一个位置(在你的例子中不存在)。
@user544262772 GNU coreutils的 dirname 并不要求它的参数存在,它是纯字符串操作。我不知道其他发行版的情况。这段代码没有任何问题。
Kyle
这是最容易自动化的答案,我想。【替换代码0
Sepero
Sepero
发布于 2016-06-30
0 人赞同

保存为一个名为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

这些基于克里斯-卢茨的提交。

这是对问题最准确的回答,IMHO。用户希望有一个增强的 mv 命令。对于脚本来说特别有用,即你不一定要运行检查,在需要使用 mv 的时候随时运行 mkdir -p 。但由于我想让 mv 的默认错误行为,我把函数名改为 mvp 。--这样我就知道什么时候可以创建目录了。
Ulises Layera
似乎是最好的解决方案的想法,但实施起来却经常崩溃。
@UlisesLayera 我修改了算法,使其更加强大。现在它应该不会崩溃了
bazz
谁能解释一下 [ ! -a "$dir" ] 的含义?我曾做过实验,右半边是真,右半边是假,都评估为真,? ?? 为了其他的原因,tmp="${tmp:-1}"似乎是为了抓取文件的最后一个字符,以确保它不是一个路径(/)。
@bazz 它应该是做 "如果$dir不存在,则继续"。不幸的是,你是对的,在使用"!-a "时有一个错误,我不知道为什么。我对代码进行了一些编辑,现在应该可以工作了。
strager
strager
发布于 2016-06-30
0 人赞同

You can use 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命令的一个版本中使用过它(如果最后一个参数不是一个目录,则添加当前目录)。即使是现代的shells也只支持1...9美元;一个Perl脚本可能更好。
@Leffler, 不是的 -- 我知道bash支持9个以上的参数(使用${123}是一种方法)。 我不懂Perl,所以欢迎你自己做个回答。 =]
Pat
Pat
发布于 2016-06-30
0 人赞同

听起来答案是否定的:)。我真的不想为了做这件事而创建一个别名或func,往往是因为这是一次性的,而且我已经在输入 mv 命令了,但我发现有一些东西可以很好的解决这个问题。

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

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

Dean Serenevy
Dean Serenevy
发布于 2016-06-30
0 人赞同

充分利用《中国共产党纪律处分条例》中的招数。 "获取传递给shell脚本的最后一个参数" 我们可以做一个简单的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/
    
sepehr
sepehr
发布于 2016-06-30
0 人赞同

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的别名应该是安全的。

Chris Lutz
Chris Lutz
发布于 2016-06-30
0 人赞同

下面的shell脚本,也许?

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

这就是基本部分。你可能想加入一点检查参数的内容,你可能想让行为在目的地存在、源目录存在或不存在时发生变化(即不要覆盖不存在的东西)。

With your code, the move isn't performed if the directory does not exist!
而且你错过了mkdir的"-p "标志。
如果一个目录不存在,如果你只是要移动它,为什么要创建它?(-p标志固定)
我的意思是,当你运行脚本时,目录2美元不存在,它被创建,但文件没有被复制。
他给出了一个简单的构造,并补充了应该如何扩展的信息。为什么要把它投下去?
Blarn Blarnson
Blarn Blarnson
发布于 2016-06-30
0 人赞同

更加愚蠢,但工作方式。

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

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

是的,有点傻。:-)如果2美元是一个目录(如原来的问题),那么它就会惨遭失败,因为最后一个目录会被删除,所以'mv'会失败。如果2美元已经存在,那么它也会试图删除它。
谢谢! 这正是我需要完成的工作:mv ./old ./subdir/new,其中subdir尚不存在。
0 人赞同

这将把 foo.c 移动到新的目录 baz 中,其父目录为 bar

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

替换代码5】的-p选项将根据需要创建中间目录。
没有-p,路径前缀中的所有目录必须已经存在。

后缀``内的所有内容都会被执行,输出作为你的命令的一部分被在线返回。
由于mkdir没有返回任何东西,所以只有echo $_的输出会被添加到命令中。

$_引用先前执行的命令的最后一个参数。
在这种情况下,它将返回传递给mkdir命令的新目录的路径(~/bar/baz/)。 I unzipped an archive without giving a destination and wanted to move all the files except demo-app.zip from my current directory to a new directory called demo-app.
The following line does the trick:

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输出。

Ben Winding
Ben Winding
发布于 2016-06-30
0 人赞同

根据另一个答案中的评论,这里是我的外壳功能。

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

在.bashrc或类似的文件中包括这个,这样你就可以在任何地方使用它。

John Doe
John Doe
发布于 2016-06-30
0 人赞同

Code:

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

参数。"d1" "d2/sub"

mkdir: created directory 'd2'
renamed 'd1' -> 'd2/sub'
    
svaardt
svaardt
发布于 2016-06-30
0 人赞同
((cd src-path && tar --remove-files -cf - files-to-move) | ( cd dst-path && tar -xf -))
    
Orestis Kapar
Orestis Kapar
发布于 2016-06-30
0 人赞同

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

mv * newdir/  

这条线上的大多数答案都提出要mkdir,然后再mv,但这导致了。

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

我面临的问题略有不同,我想一揽子移动所有的东西,如果我在移动之前创建新的目录,那么它也会试图将新的目录移动到自己那里。所以,我通过使用父目录来解决这个问题。

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

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

这个答案更像是给任何偶然遇到这个问题的特定子案例的人的一个说明。在遇到这个确切的SO问题后,我不得不多次重新思考这个解决方案,所以我决定把它贴在这里,供以后参考。不过,也有可能它并不属于这里。
Andrey
Andrey
发布于 2016-06-30
0 人赞同

My one string solution:

test -d "/home/newdir/" || mkdir -p "/home/newdir/" && mv /home/test.txt /home/newdir/
    
RASG
RASG
发布于 2016-06-30
0 人赞同

我在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)
    
Jesse Nickles
Jesse Nickles
发布于 2016-06-30
0 人赞同

这方面有很多相互矛盾的解决方案,以下是对我们有用的方法。

## ss_mv ##
function ss_mv {
    mkdir -p $(dirname "$2") && mv -f "$@"

这假定了以下语法的命令。

ss_mv /var/www/myfile /var/www/newdir/myfile

这样,目录路径/var/www/newdir就从命令的第二部分被提取出来,然后该新目录就被创建了(关键是你要使用dirname标签,以避免myfile被添加到被创建的新目录中)。

然后我们继续前进,通过使用"$@"标签对整个字符串再次进行mv

0 人赞同

你甚至可以使用支架扩展。

mkdir -p directory{1..3}/subdirectory{1..3}/subsubdirectory{1..2}      
  • which creates 3 directories (directory1, directory2, directory3),
  • and in each one of them two subdirectories (subdirectory1, subdirectory2),
  • and in each of them two subsubdirectories (subsubdirectory1 and subsubdirectory2).
  • 你必须使用bash 3.0或更新的版本。

    有意思,但并没有回答OP的问题。
    cab404
    cab404
    发布于 2016-06-30
    0 人赞同
    $what=/path/to/file;