Board logo

标题: [问题求助] PowerShell动态移动符合条件的文件夹 [打印本页]

作者: 5i365    时间: 2022-2-9 20:13     标题: PowerShell动态移动符合条件的文件夹

桌面上有一个Task文件夹,  其下有4个一级子文件夹,  名字分别是 1  2  3  4
Task下的文件夹结构图请看最后面:

结构具体描述:
       
1文件夹下: 有 OK  A1  A2 文件夹:
A1文件夹下有文件 YES

2文件夹下: 有 OK  B1  B2 文件夹:
B1文件夹下有文件 YES

3文件夹下: 有 OK  NO  C1  C2  文件夹

4文件夹下: 有 OK  NO  D1  D2  文件夹

-------------------------------------------------------------------------------
逻辑描述①
Task下的一级子文件夹中, 如果只包含OK文件夹, 不包含NO文件夹, 例如 文件夹 1 和 2 ,  则检查除OK外的其它文件夹:
如果有YES文件: 例如 1下的A1,   2下的B1, 符合条件,   则文件夹 A1 和 B1 就是要移动的对象,

逻辑描述②
Task下的一级子文件夹中, 如果既包含OK文件夹, 又包含NO文件夹, 则这个一级子文件夹是目标文件夹, 例如 文件夹 3 和 4 , 其下的文件夹数量小于5时,  就满足了条件 【脚本中文件夹数量阙值变量定义为 $QZ=5】
-----------------------------------------------------------------------------------------
Ps脚本执行后:
就会将文件夹 A1 移动到 文件夹 3 下,  然后将文件夹下的YES文件命名为其原来的父父文件夹名 即1,  此时 3 下的文件夹数量 =5 不小于5了,不满足条件了, 也就不能再移过来文件夹了,

此时文件夹 4 还满足条件,  所以B1就移动到了 4 下,  然后将文件夹下的YES文件命名为其原来的父父文件夹名 即2



实际情况中,  一级文件夹会有多个, 一级文件夹下的子文件夹也会有多个,  但是规律就是上面这样,  每次执行脚本时,  符合条件的文件夹会动态的移动,  感觉实在太复杂了,  不知从哪里搞起,  请求高手支招,  非常感谢!!!

示例文件夹: https://send.cm/d/8gO0
--------------------------------------------------------------------------------------------------------------------------

C:\Users\Administrator\Desktop>tree task /f
卷 Win 的文件夹 PATH 列表
卷序列号为 448B-18AC
C:\USERS\ADMINISTRATOR\DESKTOP\TASK
├─1
│  ├─A1
│  │      A1.txt
│  │      YES
│  │
│  ├─A2
│  │      A2.txt
│  │
│  └─OK
├─2
│  ├─B1
│  │      B1.txt
│  │      YES
│  │
│  ├─B2
│  │      B2.txt
│  │
│  └─OK
├─3
│  ├─C1
│  │      C1.txt
│  │
│  ├─C2
│  │      C2.txt
│  │
│  ├─NO
│  └─OK
└─4
    ├─D1
    │      D1.txt
    │
    ├─D2
    │      D2.txt
    │
    ├─NO
    └─OK
作者: for_flr    时间: 2022-2-10 16:43

  1. $num=5
  2. [System.Collections.ArrayList]$src=@()
  3. $dir=dir -filter *.|?{(dir $_ -name) -contains "OK" -and (dir $_ -name) -notcontains "NO"}
  4. $src+=$dir|%{dir $_ -exclude OK|?{(dir $_ -n) -contains "YES"}}
  5. $des+=dir -filter *.|?{(dir $_ -name) -contains "OK" -and (dir $_ -name) -contains "NO"}
  6. $des|%{for($i=(dir $_).count;$i -lt $num;$i++){move $src[0] $_;$src.removeat(0)}}
复制代码

作者: 5i365    时间: 2022-2-11 06:47

本帖最后由 5i365 于 2022-2-11 07:48 编辑

回复 2# for_flr


     感谢帮忙, 我把下面的代码存成BAT文件然后和要处理的Task文件夹放在一个文件夹下, 执行后转移文件夹成功,  但是没有将YES文件命名为其原来的父父文件夹名
【PS:上面的示意图加了/F 参数显示文件了, 逻辑描述文字也更清楚了】
  1. #@&cls&powershell "type '%~0'|out-string|iex"&pause&exit
  2. cd Task
  3. $num = 5
  4. [System.Collections.ArrayList]$src = @()
  5. $dir = dir -filter *. | where{
  6. (dir $_ -name) -contains "OK" -and (dir $_ -name) -notcontains "NO"
  7. }
  8. $src += $dir | foreach{
  9. dir $_ -exclude OK |
  10. where{ (dir $_ -n) -contains "YES" }
  11. }
  12. $des += dir -filter *. | where{
  13. (dir $_ -name) -contains "OK" -and (dir $_ -name) -contains "NO"
  14. }
  15. $des | foreach{
  16. for ($i = (dir $_).count; $i -lt $num; $i++)
  17. {
  18. move $src[0] $_
  19. $src.removeat(0)
  20. }
  21. }
复制代码

作者: 5i365    时间: 2022-2-11 06:56

本帖最后由 5i365 于 2022-2-11 07:48 编辑

回复 2# for_flr


    另外, 如果我把  2  下的 B1 删除, 然后执行脚本后, A1 能转移成功, 但是报下面的错误
【PS:上面的示意图加了/F 参数显示文件了, 逻辑描述文字也更清楚了】
-----------------------------------------------------------------------

Move-Item : Cannot bind argument to parameter 'Path' because it is null.
At line:25 char:8
+         move $src[0] $_
+              ~~~~~~~
    + CategoryInfo          : InvalidData: ( [Move-Item], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.MoveItemCom
   mand

Exception calling "RemoveAt" with "1" argument(s): "Index was out of range. Must be non-negative and less than the size
of the collection.
Parameter name: index"
At line:26 char:3
+         $src.removeat(0)
+         ~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentOutOfRangeException

请按任意键继续. . .
作者: for_flr    时间: 2022-2-11 10:50

  1. #@&cls&powershell "type '%~0'|out-string|iex"&pause&exit
  2. cd Task
  3. $num = 5
  4. [System.Collections.ArrayList]$src = @()
  5. $dir = dir -filter *. | where{
  6. (dir $_ -name) -contains "OK" -and (dir $_ -name) -notcontains "NO"
  7. }
  8. $src += $dir | foreach{
  9. dir $_ -exclude OK |
  10. where{ (dir $_ -n) -contains "YES" }
  11. }
  12. $des += dir -filter *. | where{
  13. (dir $_ -name) -contains "OK" -and (dir $_ -name) -contains "NO"
  14. }
  15. $src|foreach{
  16. if(test-path $_\yes){
  17. ren $_\yes $_.parent.name
  18. }
  19. }
  20. $des | foreach{
  21. for ($i = (dir $_).count; $i -lt $num; $i++){
  22. if($src.count -gt 0){
  23. move $src[0] $_
  24. $src.removeat(0)
  25. }
  26. }
  27. }
复制代码
sorry,没注意到重命名的要求,错误提示是因为可移动对象数量和目标空位数量不对等。现在加了个if判断。(原代码是准备放到task目录下。)
作者: 5i365    时间: 2022-2-11 11:22

回复 5# for_flr

技术碉堡了, 真没想到这么复杂的逻辑, 大侠居然搞定了, 目前还没有实际使用, 一直在手动操作, 想多测试之后再用,

现在我想再在加个逻辑, 请教大侠, 下面的描述中, 大字号是新加的逻辑, 正常字号还是原来的描述, 其它的逻辑也没有变化

1文件夹下: 有 OK  A1  A2  A3文件夹,
A1文件夹下有文件 YES   没有DD
A3文件夹下有文件 DD   没有YES

2文件夹下: 有 OK  B1  B2  B3文件夹,
B1文件夹下有文件 YES 没有DD
B3文件夹下有文件 DD 没有YES

描述①
Task下的一级子文件夹中, 如果只包含OK文件夹, 不包含NO文件夹, 例如 文件夹 1 和 2 ,  则检查除OK外的其它文件夹:
A. 如果有YES文件, 没有DD文件: 例如 1下的A1, 2下的B1, 符合条件,   则文件夹 A1 和 B1 就是要移动的对象
B. 如果没有YES文件, 有DD文件: 例如 1下的A3, 2下的B3, 符合条件,   则将文件夹 A3 和 B3 移动到 其父目录的 OK 文件夹下, 然后再删除DD文件
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

示例文件链接:  https://send.cm/d/8iyx

新文件结构示意图如下:

C:\Users\Administrator\Desktop>tree task2 /f
卷 Win 的文件夹 PATH 列表
卷序列号为 448B-18AC
C:\USERS\ADMINISTRATOR\DESKTOP\TASK2
├─1
│  ├─A1
│  │      A1.txt
│  │      YES
│  │
│  ├─A2
│  │      A2.txt
│  │
│  ├─A3
│  │      A3.txt
│  │      DD
│  │
│  └─OK
├─2
│  ├─B1
│  │      B1.txt
│  │      YES
│  │
│  ├─B2
│  │      B2.txt
│  │
│  ├─B3
│  │      B3.txt
│  │      DD
│  │
│  └─OK
├─3
│  ├─C1
│  │      C1.txt
│  │
│  ├─C2
│  │      C2.txt
│  │
│  ├─NO
│  └─OK
└─4
    ├─D1
    │      D1.txt
    │
    ├─D2
    │      D2.txt
    │
    ├─NO
    └─OK
作者: 5i365    时间: 2022-2-11 11:26

想知道, 这么复杂的逻辑, 用BAT能实现吗?
作者: qixiaobin0715    时间: 2022-2-11 12:13

我觉得bat应当也能实现,关键时要理清头绪。
作者: for_flr    时间: 2022-2-11 13:02

  1. #@&cls&powershell "type '%~0'|out-string|iex"&pause&exit
  2. cd Task
  3. $num = 5
  4. [System.Collections.ArrayList]$src = @()
  5. $dir = dir -filter *. | where{
  6. (dir $_ -name) -contains "OK" -and (dir $_ -name) -notcontains "NO"
  7. }
  8. $src += $dir | foreach{
  9. dir $_ -exclude OK |
  10. where{ (dir $_ -n) -contains "YES" -and (dir $_ -n) -notcontains "DD" }
  11. }
  12. $dir|foreach{
  13. cd $_
  14. dir  -exclude OK |foreach{
  15.   if((dir $_ -n) -contains "DD" -and (dir $_ -n) -notcontains "YES"){
  16. del $_\DD
  17. move $_ OK
  18.   }
  19. }
  20. cd ..
  21. }
  22. $des += dir -filter *. | where{
  23. (dir $_ -name) -contains "OK" -and (dir $_ -name) -contains "NO"
  24. }
  25. $src|foreach{
  26. if(test-path $_\yes){
  27. ren $_\yes $_.parent.name
  28. }
  29. }
  30. $des | foreach{
  31. for ($i = (dir $_).count; $i -lt $num; $i++){
  32. if($src.count -gt 0){
  33. move $src[0] $_
  34. $src.removeat(0)
  35. }
  36. }
  37. }
复制代码
就是多几个if条件,不算复杂。
批处理的话
用if exist 轻松实现。
作者: 5i365    时间: 2022-2-11 14:21

本帖最后由 5i365 于 2022-2-11 14:32 编辑

回复 9# for_flr


大侠,太牛X了, 目前有两个疑问:
1. 代码中用了 -notcontains "NO" 那如果文件夹的名字是  NO2 或  1NO 还稳吗?

2.  我在 3 和 4 文件夹下的一些子文件夹中, 也要加 DD 文件, 例如:
文件夹3 中增加了 C3文件夹 里面 也没有YES文件, 但有 DD文件
文件夹4 中增加了 D3文件夹 里面 也没有YES文件, 但有 DD文件
如何实现刚刚上面的功能? 即:  C3移到 3下的OK文件夹, 然后删除DD, 同样 D3 移到 4下的OK文件夹, 然后删除DD, 其它的功能不能受影响, 最开始的逻辑有一点改变, 就是移动到OK文件夹的条件, 不再考虑 其父目录下, 是不是有 OK 和 NO 文件夹
这些逻辑如果人来理解并手动操作很简单, 但是用文字和代码描述的时候, 真是太吃力了
------------------------------------------------------------------------------------
示例文件下载链接: https://send.cm/d/8jAI
现在的文件结构如下:

C:\Users\Administrator\Desktop\T>tree Task3 /f
卷 Win 的文件夹 PATH 列表
卷序列号为 448B-18AC
C:\USERS\ADMINISTRATOR\DESKTOP\T\TASK3
├─1
│  ├─A1
│  │      A1.txt
│  │      YES
│  │
│  ├─A2
│  │      A2.txt
│  │
│  ├─A3
│  │      A3.txt
│  │      DD
│  │
│  └─OK
├─2
│  ├─B1
│  │      B1.txt
│  │      YES
│  │
│  ├─B2
│  │      B2.txt
│  │
│  ├─B3
│  │      B3.txt
│  │      DD
│  │
│  └─OK
├─3
│  ├─C1
│  │      C1.txt
│  │
│  ├─C2
│  │      C2.txt
│  │
│  ├─C3
│  │      C3.txt
│  │      DD
│  │
│  ├─NO
│  └─OK
└─4
    ├─D1
    │      D1.txt
    │
    ├─D2
    │      D2.txt
    │
    ├─D3
    │      D3.txt
    │      DD
    │
    ├─NO
    └─OK

C:\Users\Administrator\Desktop\T>
作者: 5i365    时间: 2022-2-11 14:27

本帖最后由 5i365 于 2022-2-11 14:31 编辑

回复 9# for_flr


简尔言之两句话, 现在就是两条线:
一条线, 根据 DD 文件 向其所在文件夹的父目录下的OK文件夹中移动文件夹
另一条线, 根据 YES 文件 向包含 OK和NO 文件夹的目录下移动文件夹

作者: 5i365    时间: 2022-2-11 17:08

回复 9# for_flr


    关于第一个问题, 我试了
1. 代码中用了 -notcontains "NO" 那如果文件夹的名字是  NO2 或  1NO 还稳吗?

很稳! 下面的代码, 只匹配 NO
dir -dir -name | where{ $_ -contains "NO" }
作者: for_flr    时间: 2022-2-11 18:04

本帖最后由 for_flr 于 2022-2-14 09:33 编辑

回复 12# 5i365


    9楼第12行,$dir改做dir就可以了。
PowerShell语法跟白话文挺像的,你可以试着自己修改的。
回复 19# 5i365
改的好,当时写的先改名后移动,犯了错;
dir -dir可以,为了获取子文件夹;
[System.Collections.ArrayList]$src = @()是为了将数组转为可以删除数组元素的arraylist。
作者: 5i365    时间: 2022-2-11 20:14

回复 13# for_flr


大侠真是太拽了, 还真是, 把$去掉就可以了!

分析单个功能的代码, 在下能大致看懂, 但是真要搞多个逻辑的事情, 就一点头绪没有, 不知道用什么, 只会改点单一功能的PS代码

再请教大侠问题, 下面的代码:  dir -filter *. 应该可以换成 dir -dir 吧 百度搜索您的代码, 没找到结果, 所以就问一下

$dir = dir -filter *. | where{
        (dir $_ -name) -contains "OK" -and (dir $_ -name) -notcontains "NO"
}
作者: 5i365    时间: 2022-2-11 20:48

本帖最后由 5i365 于 2022-2-11 20:50 编辑

回复 13# for_flr


    大侠发现个问题,
文件夹 3 和 4 里面的子文件夹数量都是5时, 如果我在A2里面, 我放了一个YES文件, 执行PS后,

此时因为 目标文件夹 3和4的数量,均不满足移动A2的条件, 所以A2不做任何操作, 但是我发现 A2里面的 YES 文件, 改名为了 1  应该是移过去之后, 再改名
作者: 5i365    时间: 2022-2-13 09:19

本帖最后由 5i365 于 2022-2-13 09:31 编辑

回复 13# for_flr


    我把根据文件DD移动到OK文件夹的功能去掉了, 只留下了最开始,那个复杂的, 根据文件夹数量移动文件夹的功能,

问题就是, 在不满足移动条件下, 仍会把源文件夹中的yes改名, 应该是先移过去, 然后再改名, 但文件名又是原来的文件夹的父目录名

感觉问题应该出在红色字的地方, 尝试加在后面蓝字的地方也不行, 不知道怎样改粉色地方的代码


#@&cls&powershell "type '%~0'|out-string|iex"&pause&exit
cd Task3

$num = 5

[System.Collections.ArrayList]$src = @()

$dir = dir -filter *. | where{
        (dir $_ -name) -contains "OK" -and (dir $_ -name) -notcontains "NO"
}

$src += $dir | foreach{
        dir $_ -exclude OK |
        where{ (dir $_ -n) -contains "YES" }
}

$des += dir -filter *. | where{
        (dir $_ -name) -contains "OK" -and (dir $_ -name) -contains "NO"
}

$src | foreach{
        if (test-path $_\YES)
        {
                ren $_\YES $_.parent.name
        }
}


$des | foreach{
        for ($i = (dir $_).count; $i -lt $num; $i++)
        {
                if ($src.count -gt 0)
                {
                        move $src[0] $_
                        #ren $_\YES $_.parent.name
                        $src.removeat(0)
                }
        }
}
作者: 5i365    时间: 2022-2-13 11:02

本帖最后由 5i365 于 2022-2-13 11:09 编辑

回复 2# for_flr


    这个第5行前面是不是应该新建个数组? 这个不太理解, 我看前面 源 是先建了一个数组 [System.Collections.ArrayList]$src = @()
另外, 为什么 要$des+=直接$des=不行吗?
$des = @()
$des+=dir -filter *.|?{(dir $_ -name) -contains "OK" -and (dir $_ -name) -contains "NO"}
作者: 5i365    时间: 2022-2-13 20:20

回复 5# for_flr


    终于搞定了, 删除上面红字的代码, 然后将最后的代码改为如下, 就可以了!
  1. $des | foreach{
  2. for ($i = (dir $_).count; $i -lt $num; $i++)
  3. {
  4. if ($src.count -gt 0)
  5. {
  6. move $src[0] $_
  7. $s = $src[0].name
  8. $s2 = $src[0].parent.name
  9. ren $_\$s\YES -newname $s2
  10. $src.removeat(0)
  11. }
  12. }
  13. }
复制代码

作者: 5i365    时间: 2022-2-13 20:24

本帖最后由 5i365 于 2022-2-13 20:32 编辑

最终代码:
  1. cd Task3; $num = 6; [Collections.ArrayList]$src = @()
  2. $dir = gci -dir | where{
  3. (dir $_ -name) -contains "OK" -and (dir $_ -name) -notcontains "NO"
  4. }
  5. $src += $dir | foreach{
  6. dir $_ -exclude OK |
  7. where{ (dir $_ -name) -contains "YES" }
  8. }
  9. $des = gci -dir | where{
  10. (dir $_ -name) -contains "OK" -and (dir $_ -name) -contains "NO"
  11. }
  12. $des | foreach{
  13. for ($i = (dir $_ -dir).count; $i -lt $num; $i++)
  14. {
  15. if ($src.count -gt 0)
  16. {
  17. move $src[0] $_
  18. $s = $src[0].name
  19. $s2 = $src[0].parent.name
  20. ren $_\$s\YES -newname $s2
  21. $src.removeat(0)
  22. }
  23. }
  24. }
复制代码





欢迎光临 批处理之家 (http://www.bathome.net/) Powered by Discuz! 7.2