0

I have a folder with hundreds of files that are supposed to be to this standard:

Lastname, Firstname - 1010 ddmmyy.pdf

At least half of them are missing that comma, and I need it added to them all.

Using something like this replaces all spaces with a comma, which is not what I need.

Get-ChildItem -Name *.pdf | Rename-Item -NewName { $_ -replace ' ',',' }

I'm stuck! Is there a way to identify inserting a single comma at the beginning of the first blank space?

Compo
  • 36,585
  • 5
  • 27
  • 39

4 Answers4

1

You can use below to do that:

Get-ChildItem -Path 'D:\SomeWhere' -Filter '*.pdf' -File | Rename-Item -NewName { $_.Name -replace '^([^,\s]+)\s+(.+)', '$1, $2' }

Regex details:

^               Assert position at the beginning of the string
(               Match the regular expression below and capture its match into backreference number 1
   [^,\s]       Match a single character NOT present in the list below
                The character “,”
                A whitespace character (spaces, tabs, line breaks, etc.)
      +         Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)              
\s              Match a single character that is a “whitespace character” (spaces, tabs, line breaks, etc.)
   +            Between one and unlimited times, as many times as possible, giving back as needed (greedy)
(               Match the regular expression below and capture its match into backreference number 2
   .            Match any single character that is not a line break character
      +         Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)
Theo
  • 57,719
  • 8
  • 24
  • 41
0

You could try filtering for all the .pdf that have a space in their name then adding a condition to check if the file already contains a , in it's Name, if it does skip and if it doesn't, replace the first space white space followed by everything else with a , plus all the replaced string, i.e.:

Get-ChildItem -Filter '* *.pdf' | ForEach-Object {
    if(-not $_.BaseName.Contains(',')) {
        $_ | Rename-Item -NewName { $_.Name -replace '\s.+', ',$0' }
    }
}
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • @NotVeryGoodAtThis it should have been `-Filter` instead of `-Name`. Should work now – Santiago Squarzon Jul 09 '22 at 15:34
  • It does, thank you! I went with Theo's method above in this case, as in the event that there is another comma somewhere in the file name, his would add the comma at the first space regardless, while this one here would just skip the file. – NotVeryGoodAtThis Jul 11 '22 at 13:21
0

Another regex solution based on the -replace operator, a streamlined version of Theo's helpful answer:

Get-ChildItem -Filter *.pdf | 
  Rename-Item -NewName { $_.Name -replace '^[^, ]+(?= )', '$&,' }

As an aside:

  • There is an incidental problem with your attempt in that you're mistakenly using the -Name parameter, which, as a switch parameter (flag), is a stand-alone parameter not related to your *.pdf argument, which is therefore a positional argument that implicitly binds to the -Path parameter.

  • Thus, since -Path also understands wildcards, simply removing -Name (which makes Get-ChildItem output relative path strings instead of [System.IO.FileInfo] instances) would have sufficed, but for performance reasons it is preferable to perform wildcard-based filtering with the -Filter parameter, as shown above. Slight caveat: While this typically works as intended, -Filter does not use PowerShell's wildcard expressions: it uses the wildcard matching of the file-system APIs, which have fewer features and legacy quirks - see this answer.

The basic idea is that if the word before the first space doesn't contain a comma, insert one right after it:

  • Regex:

    • ^[^, ]+ matches one or more (+) characters (inside [...]) that are not (^) a , or a (space), at the start of the string (the first ^).

    • (?= ) is a look-ahead assertion that requires that a (space) follow what was matched before, without getting captured as part of the match.

  • Replacement operand:

    • $& refers to the substring that matched the whole regex, and following it with a , is what inserts the desired comma.

Note:

  • It is safe to unconditionally apply the -replace operation: if the file name doesn't match, the regex, it is returned as-is, and Rename-Item quietly ignores attempts to rename a file to the same name it already has.

  • Unfortunately, Rename-Item does not do that for directories and reports an error instead - see GitHub issue #14903.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Similar to Santiago's, the only thing needing changed in this was to change -Name to -Filter, but this worked. Thank you! – NotVeryGoodAtThis Jul 11 '22 at 13:13
  • My pleasure, @NotVeryGoodAtThis (my solution is actually a refinement of Theo's answer). I missed the `-Name` problem - answer updated. Note that `-Name` is a _switch_ (flag) results in outputting _relative paths_ instead of `System.IO.FileInfo` instances. `*.pdf`, if not preceded by a parameter name, _positionally_ binds to the `-Path` parameter. As such, simply _removing_ `-Name` would work too, but filtering based on `-Filter` is faster (caveat: it [does _not_ use PowerShell's wildcard language and has legacy quirks](https://stackoverflow.com/a/60171419/45375)). – mklement0 Jul 11 '22 at 13:39
0

What about this (to be executed in a Windows Command Prompt, due to the tag , at least in revision 2 of the question):

for /F "tokens=1* eol=  delims= " %I in ('dir /B /A:-D-H-S "* *.pdf" ^| findstr /R /I /C:"^[^, ][^, ]* [^ ]"') do @ren "%I %J" "%I, %J"

How it works:

  • dir "* *.pdf" returns files whose names contain at least a SPACE;
  • a pipe (|) redirects the list of file names into the next command,
  • which is findstr; the search expression narrows the list to items beginning with a string neither containing SPACE nor ,, followed by a SPACE, followed by anything but a SPACE;
  • the resulting list is captured by a for /F loop, that iterates over it, splitting the names into the part before (in %I) and the one after (in %J) the first SPACE;
  • ren finally renames the files, inserting the desired , before the first SPACE;
aschipfl
  • 33,626
  • 12
  • 54
  • 99