【问题标题】:File and Folder Manipulation in PowershellPowershell 中的文件和文件夹操作
【发布时间】:2013-10-02 22:10:17
【问题描述】:

我有一个包含数千个文件名的文件夹。

所有文件名都与用户的用户 ID 相关。某些用户在此文件夹中有多个文件,因此它会在名称的末尾附加一个数字。 IE。 susy.txt、susy1.txt、susy2.txt、carl.txt、carl1.txt等……

我想要做的是为每个拥有多个文件的用户创建一个特定的文件夹,并将所有关联的文件移动到该文件夹​​中。所以我注意到有多个 susy 文件。所以我想创建一个名为 Susy 的文件夹,并将 susy.txt、susy1.txt 和 susy2.txt 放入其中……所有文件以此类推。

甚至可以将其作为批处理文件执行,如果可以的话,有人可以指出我这样做的正确方向吗?我对编写批处理脚本有一点了解,希望借此机会进一步了解。

谢谢

【问题讨论】:

    标签: file powershell file-management


    【解决方案1】:

    这是一种更简单/更紧凑的方法:

    Get-ChildItem | ?{$_.Name -match '(\D+)\d*\.txt'} | %{
      md $matches[1] -ea SilentlyContinue 
      Move-Item $_ $matches[1]
    }
    

    纯粹主义者可能会说,在尝试创建目录之前,Test-Path 是“更好的做法”,但我通常更喜欢更紧凑的代码;如果没有其他决定取决于目录是否已存在,则每次尝试创建它并忽略错误会更快。


    一些解释性说明,以回应 OP 的评论:

    • ?{ } 是 Where-Object 的简写,它根据条件进行过滤,即只有满足指定条件的对象才会沿管道传递。因此,在这种情况下,它会获取当前目录的列表并仅选择 Name 属性与正则表达式匹配的对象。
    • 使用-match 运算符后,匹配项存储在自动数组变量$matches 中。 $matches[0] 包含字符串的整个匹配部分,从 1 开始的索引包含匹配组,从左括号的序号位置开始编号,即 $matches[1] 是以遇到的第一个左括号开头的匹配组,$matches[2] 是从第二个左括号开始的匹配组,依此类推。

      在这种情况下只有一个匹配组,$matches[1]包含了(\D+)匹配的字符串部分,即之前的连续非数字字符序列第一个数字或点。

    【讨论】:

    • 好的,这很有效,而且正是我想要的。谢谢!我显然不是 powershell 专业人士,但我在很大程度上理解这一点......我只是不知道 ?{} 是什么意思和 $matches[1]。主要是[1],我不明白。再次感谢您!
    • 再次感谢您!很有道理!
    【解决方案2】:

    这是我刚刚捏造出来的快速而肮脏的解决方案。它使用正则表达式将用户名从文件名中拆分出来,即表达式会将 carl 从 carl1.txt carl3.txt carl22.txt 中拆分出来。然后,它会根据这些名称创建唯一的文件夹。所以它只尝试创建一次carl,而不是 57 次。之后,它将所有carl 和johnny 和susy 等移动到新文件夹中。

    如果您是新手,如果这看起来有点吓人,请不要害怕。尤其是正则表达式。如果它们让您感到困惑,几乎可以肯定没有它们也可以做到这一点,但是正则表达式是其中一种工具,一旦您学习了它,您就会意识到它在解决问题方面的能力。

    [regex]$pattern_name = '^\w.+?(?=[\d\.])'
    
    # create folders for all the unique users
    get-Childitem -File -path 'c:\scripts' | foreach-Object {$pattern_name.match($_).value } | sort -unique | foreach-Object { mkdir $_ }
    
    # move files into those folders
    get-Childitem -File -path 'c:\scripts' | foreach-Object {
        $user = $pattern_name.match($_).value 
        Move-Item -destination ".\$user" -Path $_.FullName
        }
    

    【讨论】:

    • +1 - 请注意,即使用户只有一个文件,这也会为用户创建一个目录,这不是 OP 要求的。
    • 同意。虽然这可能是他们想要的:P
    • 那个正则表达式的几个问题......不是主要问题,但我想知道你为什么这样写。 (1) 为什么. 在正则表达式中\w 之后?虽然名称可能只有一个字符长,但 OP 没有指定至少两个字符。另外,为什么你只需要特别是第一个字符在 \w 类中,然后匹配任何字符直到第一个数字或句点?
    • 这只是一种基于 OP 初始语句的快速而肮脏的模式,文件基于人名和末尾的数字。当然应该改变模式以适应他们的确切需求。至于匹配第一个 \w 之后的任何字符,这只是我的一个错误。但是,根据所讨论的文件名,如果任何人的名字中都有符号,它甚至可能会更好。底线是应该修改正则表达式以适应他们的确切情况。
    • 非常感谢,这正是我所需要的!调整正则表达式,得到了我需要的结果。我确实有一个问题,我可能听起来有点笨拙。我知道 $pattern_name.match($_) 返回一个布尔值,它将我的文件名与正则表达式进行比较......但是添加 .value 有什么作用?除此之外,我完全理解一切。谢谢!
    【解决方案3】:

    一种方法。这是做什么的:

    1. 按用户名对目录中的文件进行分组。

    2. 如果组有多个文件,它会为用户创建一个目录,然后将用户的文件移动到该目录中。

    (请注意,我在所有命令中添加了 -WhatIf 选项 修改数据 - 您必须删除它们才能实际修改 数据。)

    # Make backup for safety
    cp -recurse .\userFiles .\userFilesBackup
    
    cd .\userFiles
    
    gci | `
    
    select-object @{n='User';e={$_.BaseName -replace "^([a-z]+)[\d]*$", '$1'}}, Name | `
    
    group-object User | `
    
    ? { $_.Count -gt 1 } | `
    
    % { `
    
    $userDirectory = $_.Name
    mkdir -WhatIf $userDirectory
    
    foreach ($f in $_.Group)
    { mv -WhatIf $f.Name $userDirectory }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-07
      • 2014-05-08
      • 2019-07-02
      • 1970-01-01
      • 1970-01-01
      • 2015-12-11
      相关资源
      最近更新 更多