将文件移动到当前位置的父目录

3

我有许多文件夹,这些文件夹里面还有一个子文件夹,并且子文件夹里面还有文件。我想把第二层的文件移到第一层,而且不知道它们的名称。

以下是一个简单的例子:

  • Before running a script:

    /temp/1stlevel/test.txt
    /temp/1stlevel/2ndlevel/test.rtf
    
  • After running a script:

    /temp/1stlevel/test.txt
    /temp/1stlevel/test.rtf
    

我已经很接近了,但是还缺少一些东西,我相信这很简单/愚蠢。这是我运行的内容:

find . -mindepth 3 -type f -exec sh -c 'mv -i "$1" "${1%/*}"' sh {} \;

这是我的理解:

这就是我得到的结果:

mv: './1stlevel/2ndlevel/test.rtf' and './1stlevel/2ndlevel/test.rtf' are the same file

有什么建议吗?

更新:George,这是很棒的东西,谢谢!我学到了很多并做了笔记。使用mv命令而不是更复杂的命令是很聪明的。这不是我第一次被指责用最困难的方式去做事情了!

然而,虽然它在一个文件夹集合中运行得很好,但如果我有更多的文件夹,它就不能按预期工作。这就是我的意思:

之前:

new
└── temp
    ├── Folder1
    │   ├── SubFolder1
    │   │   └── SubTest1.txt
    │   └── Test1.txt
    ├── Folder2
    │   ├── SubFolder2
    │   │   └── SubTest2.txt
    │   └── Test2.txt
    └── Folder3
        ├── SubFolder3
        │   └── SubTest3.txt
        └── Test3.txt

之后:

new
└── temp
    └── Folder3
        ├── Folder1
        │   ├── SubFolder1
        │   └── Test1.txt
        ├── Folder2
        │   ├── SubFolder2
        │   └── Test2.txt
        ├── SubFolder3
        ├── SubTest1.txt
        ├── SubTest2.txt
        ├── SubTest3.txt
        └── Test3.txt

希望达成的目标:

new
└── temp
    ├── Folder1
    │   ├── SubFolder1
    │   ├── SubTest1.txt
    │   └── Test1.txt
    ├── Folder2
    │   ├── SubFolder2
    │   ├── SubTest2.txt
    │   └── Test2.txt
    └── Folder3
        ├── SubFolder3
        ├── SubTest3.txt
        └── Test3.txt

如果想要更加高级一些:

new
└── temp
    ├── Folder1
    │   ├── SubTest1.txt
    │   └── Test1.txt
    ├── Folder2
    │   ├── SubTest2.txt
    │   └── Test2.txt
    └── Folder3
        ├── SubTest3.txt
        └── Test3.txt
  • 虽然后面在我的脚本中会删除空文件夹,但我不需要变得花哨。

顺便说一句,在Notepad++中这花了我很长时间才画出来。你用的是什么工具?


2
确实,你用这个问题的标题吸引了我的注意力 :) - Guy Avraham
2
Stack Overflow是一个关于编程和开发问题的网站。这个问题似乎不属于编程或开发范畴,因此被认为是离题的。请参阅帮助中心中的可以在这里提问的主题。也许Super User或者Unix & Linux Stack Exchange会更适合您提问。 - jww
3
@jww。这是一个非常明确的问题,提问者已经做出了足够的努力。你声称这个问题不适合的唯一可能原因是OP选择了Bash作为他们的脚本语言。如果这个问题是关于Python的,你会投票将其关闭吗? - Mad Physicist
2
@MadPhysicist - 这个问题的门槛比仅仅在问题上添加“...在脚本中”要高一些。假设 最小完整可验证示例 仍然有效,则只是一个关于如何运行 findmv 命令的问题。 - jww
3
@jww。我不同意。这很明显是一个关于如何以非常特定、清晰描述的方式移动文件的问题。显然,如果你使用bash,mv最终会涉及其中,而find恰好是OP尝试的一部分。但正如发布的答案所示,可以使用bash编程语言的一个特性来完成操作,而无需使用find。这绝对是一个编程问题,即使它根本不是在脚本中发生。 - Mad Physicist
显示剩余4条评论
4个回答

4
你的 find . -mindepth 3 -type f -exec sh -c 'mv -i "$1" "${1%/*}"' sh {} \; 尝试非常接近正确。
在调试复杂命令时,一种有用的技术是插入 echo 语句以查看正在发生的情况。因此,如果我们这样说:
$ find . -mindepth 3 -type f -exec sh -c 'echo mv -i "$1" "${1%/*}"' sh {} \;
我们会得到
mv -i ./Folder1/SubFolder1/SubTest1.txt ./Folder1/SubFolder1
mv -i ./Folder2/SubFolder2/SubTest2.txt ./Folder2/SubFolder2
mv -i ./Folder3/SubFolder3/SubTest3.txt ./Folder3/SubFolder3

这是很有道理的 — 它会找到所有深度为 3(和更深)的文件, 去掉路径名的最后一级,然后将文件移动到那里。 但是

mv (path_to_file) (path_to_directory)
意味着将文件移动到目录中。
因此,命令mv -i ./Folder1/SubFolder1/SubTest1.txt ./Folder1/SubFolder1 表示将Folder1/SubFolder1/SubTest1.txt移动到Folder1/SubFolder1中 — 但它已经在那里了。 因此,您会收到错误消息,说明您正在将文件移动到已存在的位置。

从您的说明中可以清楚地看出, 您想将SubTest1.txt移动到Folder1中。 一个快速的解决方法是

$ find . -mindepth 3 -type f -exec sh -c 'mv -i "$1" "${1%/*}/.."' sh {} \;

使用..SubFolder1向上进入Folder1

mv -i ./Folder1/SubFolder1/SubTest1.txt ./Folder1/SubFolder1/..
mv -i ./Folder2/SubFolder2/SubTest2.txt ./Folder2/SubFolder2/..
mv -i ./Folder3/SubFolder3/SubTest3.txt ./Folder3/SubFolder3/..

我认为这是不好的风格,尽管我无法确定原因。

我更喜欢

$ find . -mindepth 3 -type f -exec sh -c 'mv -i "$1" "${1%/*/*}"' sh {} \;

这里使用%/*/*来从文件路径名中删除两个组成部分,以获得您真正想要的。

mv -i ./Folder1/SubFolder1/SubTest1.txt ./Folder1
mv -i ./Folder2/SubFolder2/SubTest2.txt ./Folder2
mv -i ./Folder3/SubFolder3/SubTest3.txt ./Folder3

您可以使用以下步骤:
$ find . -mindepth 2 -type d –delete

删除空的SubFolderN文件夹。如果由于某些故障导致任何一个非空,find将不会处理它并发出警告消息。


这是我会做的,但我会在目标文件夹的结尾处放一个'/',"${1%//}"/,以明确显示目标是一个文件夹。不过这并非必需。 - ChuckCottrill
谢谢,G-Man!我会经常使用你的echo语句技巧。这将帮助我在需要寻求帮助之前自己解决更多问题,这正是我喜欢的方式。 - Peter

3
让我用这个例子来说明:
树形结构:
new
└── temp
    └── 1stlevel
        ├── 2ndlevel
        │   └── text.rtf
        └── test.txt

使用以下方式移动:

find . -mindepth 4 -type f -exec mv {} ./*/* \; 

移动后的结果如下:
new
└── temp
    └── 1stlevel
        ├── 2ndlevel
        ├── test.txt
        └── text.rtf

运行脚本时,所在的文件夹很重要。我是从temp文件夹的上一级文件夹运行的。如果你想从temp文件夹中运行它,则命令应为:

find 1stlevel/ -mindepth 2 -type f -exec mv {} ./* \;

或者:

find ./ -mindepth 3 -type f -exec mv {} ./* \;

请仔细查看find ./ -mindepth 3部分,记住-mindepth 1 表示处理除起始点之外的所有文件。因此,如果您从temp开始,并且要找temp/1st/2nd/中的某个文件,则需要使用以-mindepth 3从temp开始的方法访问它。请参见:man find
现在,针对目标我使用了./*/*,解释是“从当前(比temp高一级的位置,我的是new)目录向下到temp,然后是1stlevel”,因此:
  • ./: =>新文件夹
  • ./*: =>新/temp文件夹
  • ./*/*: =>新/temp/1stlevel
但是所有这些都只适用于find命令,但是另一个技巧是仅在new文件夹中使用mv命令:
mv ./*/*/*/* ./*/*

这个例子中,程序是从一个新的文件夹运行的(换句话说,是从temp文件夹的上一级文件夹运行的)。如果需要在不同层级下运行,请进行相应调整。

要从temp文件夹运行:

mv ./*/*/* ./*

如果你担心时间问题,因为你提到有很多文件,那么使用mv选项比find选项更快。以下是仅针对三个文件的时间结果:

find:

real    0m0.004s
user    0m0.000s
sys     0m0.000s

mv:

real    0m0.001s
user    0m0.000s
sys     0m0.000s

更新:

由于楼主需要一个能够访问多个文件夹的脚本,我提供了以下内容:

#!/usr/bin/env bash

for i in ./*/*/*;
do
        if [[ -d "$i" ]];
        then
                # Move the files to the new location
                mv "$i"/* "${i%/*}/"
                # Remove the empty directories
                rm -rf "$i"

        fi
done

如何:从新文件夹运行:./move.sh,请记得使用chmod +x move.sh命令使脚本可执行。

目标目录结构:

new
├── move.sh
└── temp
    ├── folder1
    │   ├── subfolder1
    │   │   └── subtext1.txt
    │   └── test1.txt
    ├── folder2
    │   ├── subfolder2
    │   │   └── subtext2.txt
    │   └── test1.txt
    └── folder3
        ├── subfolder3
        │   └── subtext3.txt
        └── test1.txt

获取精美的结果:

new
├── move.sh
└── temp
    ├── folder1
    │   ├── subtext1.txt
    │   └── text1.txt
    ├── folder2
    │   ├── subtext2.txt
    │   └── text2.txt
    └── folder3
        ├── subtext3.txt
        └── text3.txt

好的,第五次是成功的!对于一直增加/删除评论我道歉。这个系统不太友好!哈哈。我已经编辑了我的帖子并回复了你。谢谢! - Peter
1
抱歉耽搁了,工作占用了个人时间。乔治,这真的太完美了!完全按照我希望的方式运行。非常感谢你回来更新答案! - Peter

0

让你的脚本导航到每个需要移动文件的目录,然后你可以使用find在目录中找到每个文件,然后将它们移动到上一级目录:

 $ find . -type f -exec mv {} ../. \;

(1)这将移动每个文件,包括Test1.txtTest2.txtTest3.txt,这些文件本不应该被移动,都会被移动到new目录中(即temp的上级目录)。 (2)为什么你说../.而不是.. - G-Man Says 'Reinstate Monica'

0
mv YOUR-FILE-NAME ../

如果您具有写入权限,它应该以这种方式工作


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接