6

I have a lot of windows files where the filename contains a %. See below:

c:\temp>
dir /b
114902_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1489879.pdf
114904_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1489880.pdf
114906_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1491058.pdf
114916_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1491059.pdf
114918_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1491452.pdf
114920_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1491453.pdf
114923_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1491454.pdf
115513_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039292647.pdf
115515_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039295860.pdf
115517_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039296218.pdf
115523_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039308592.pdf
115525_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039308593.pdf
120342_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039328905.pdf
120345_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039337204.pdf
120348_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039337221.pdf
120351_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039337461.pdf
120639_T%3A%5CAccounting%5CPayables%5CAPA%5CAFA MAY 15%5CINV#63168731.pdf
120640_T%3A%5CAccounting%5CPayables%5CAPA%5CAFA MAY 15%5CINV#63168988.pdf
120642_T%3A%5CAccounting%5CPayables%5CAPA%5CAFA MAY 15%5CINV#63168993.pdf
120644_T%3A%5CAccounting%5CPayables%5CAPA%5CAFA MAY 15%5CINV#63169027.pdf

These % are actually hex code for a : (%3A) and \ (%5C). I can't explain how they got this way when they were copied from another system, but this what I have to work with. For clarity, this is what is looks like with the hex code.

114902_T:\Accounting\Payables\APA\Boshart Apr 25\BII_inv_1489879.pdf

What I want to do is to rename the file from this:

114902_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1489879.pdf

to this:

BII_inv_1489879.pdf

I tried following other rename exmaples to use the % as a delimiter, but what further complicates things is the last delimiter (the pdf filename) isn't always the 7th token.

Any help would be greatly appricitated.

4
  • 3
    Hi and welcome to SuperUser. Simply use the "escape" form with ^% instead of % in each of those places. Alternatively, if you happen to be familiar with Bash, you may want to (ab)use the Bash shell and basic set of tools that comes with Git for Windows. Personally I'm not a fan of Git (in particular because it makes assumptions about how unixoid a system ought to be), but in this case you can make it work to your advantage. Apr 16, 2020 at 21:08
  • 4
    Alternatively, on Windows 10, this can be done with PowerShell too. If you want to keep the folderstructure in tact, Powershell can actually help there too.
    – LPChip
    Apr 16, 2020 at 21:13
  • 1
    You can rename 114902_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1489879.pdf to BII_inv_1489879.pdf by typing ren "114902_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1489879.pdf" "BII_inv_1489879.pdf". (The quotes around BII_inv_1489879.pdf are optional.) If you want a more general solution, please describe what you want, and provide a wider range of examples. … (Cont’d) Apr 18, 2020 at 15:00
  • 1
    (Cont’d) …  In particular, will your filenames always have exactly two spaces, and will the cut point always be the sixth character after the second space? (One of the posted answers assumes that.) Will the last %nn always be a %5C? If not, do you want the filename to be cut after the last %nn or after the last %5C? Will there ever be % characters in the filename that are not part of a %nn sequence? Are there any other characters in the filenames that are likely to be problematic for scripting? Etc. Apr 18, 2020 at 15:00

5 Answers 5

15

If you're on Windows 10, you should be learning & using Powershell for administration & automation. It's very powerful and a lot of fun.

With your sample names and desired result, I opted to split on '%5C' and use an index of -1, which gets the last element in an array regarless of length.

Verbose:

$path = 'c:\temp'
Get-ChildItem $path | Rename-Item -NewName { ($_ -split '%5C')[-1] }

At the console:

gci c:\temp | ren -NewName { ($_ -split '%5C')[-1] }

Get-ChildItem

Rename-Item

about split

String manipuation demo:

PS C:\...\keith>$oldnames
114902_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1489879.pdf
114904_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1489880.pdf
114906_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1491058.pdf
114916_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1491059.pdf
114918_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1491452.pdf
114920_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1491453.pdf
114923_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1491454.pdf
115513_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039292647.pdf
115515_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039295860.pdf
115517_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039296218.pdf
115523_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039308592.pdf
115525_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039308593.pdf
120342_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039328905.pdf
120345_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039337204.pdf
120348_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039337221.pdf
120351_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039337461.pdf
120639_T%3A%5CAccounting%5CPayables%5CAPA%5CAFA MAY 15%5CINV#63168731.pdf
120640_T%3A%5CAccounting%5CPayables%5CAPA%5CAFA MAY 15%5CINV#63168988.pdf
120642_T%3A%5CAccounting%5CPayables%5CAPA%5CAFA MAY 15%5CINV#63168993.pdf
120644_T%3A%5CAccounting%5CPayables%5CAPA%5CAFA MAY 15%5CINV#63169027.pdf
PS C:\...\keith>$oldnames | ForEach{ ($_ -split '%5C')[-1] }
BII_inv_1489879.pdf
BII_inv_1489880.pdf
BII_inv_1491058.pdf
BII_inv_1491059.pdf
BII_inv_1491452.pdf
BII_inv_1491453.pdf
BII_inv_1491454.pdf
0039292647.pdf
0039295860.pdf
0039296218.pdf
0039308592.pdf
0039308593.pdf
0039328905.pdf
0039337204.pdf
0039337221.pdf
0039337461.pdf
INV#63168731.pdf
INV#63168988.pdf
INV#63168993.pdf
INV#63169027.pdf
2
  • 6
    Hasn't powershell been around long before Win10? Apr 17, 2020 at 11:09
  • 3
    Yes, That is correct Apr 17, 2020 at 15:48
7

I would write a PowerShell script that iterates over the file names and rewrites them with the following:

$dir = Get-ChildItem -Path "PUTFULLPATHHERETODIRECTORY"
foreach($P in $dir) {
    Rename-Item -path $P -newname $P.FullName.Substring($P.FullName.lastindexof("%5C")+3)
}

First, we get the contents of the target folder. Then, for each item in the directory, rename it to the name starting at the last index of the "%5C" pattern and add 3 spaces to account for removing it.

Of course, put the full path where I put PUTFULLPATHHERETODIRECTORY.

0
4

Use *5c as a delimiter and obtain target name substrings by the set command, and this will get the last part of the file name without worrying if it is token 5, 6, 7, etc, because that always get the strings after the last delimiter...

Split string into substrings based on delimiter by Sponge Belly / DosTips.com

set "_trick=%_name:*5c=" & set "_name=%"

@echo off && setlocal enabledelayedexpansion

for %%i in (*.pdf)do call :_ren_: "%%~ni" && ren "%%~fi" "!_name!%%~xi"

:_ren_:
set "_name=%~1" <nul || endlocal && goto :EOF
set "_trick_=%_name:*5c=" & set "_name=%" && exit /b 
  • The same in Powershell
gci C:\temp\*.pdf|Ren -New {$_ -replace '.*%5C',''}

enter image description here

0
3

How can I batch rename windows files where the % is a delimiter?

Use the following batch file (test.cmd):

@echo off
setlocal enabledelayedexpansion
for /f "tokens=1-3 delims= " %%a in ('dir /b *.pdf') do (
  set _temp=%%c
  set _name=!_temp:~5!
  ren "%%a %%b %%c" !_name!
  )
endlocal

Example:

F:\test>dir /b *.pdf
114902_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1489879.pdf
114904_T%3A%5CAccounting%5CPayables%5CAPA%5CBoshart Apr 25%5CBII_inv_1489880.pdf
115513_T%3A%5CAccounting%5CPayables%5CAPA%5CABB MAY 15%5C0039292647.pdf

F:\test>test
F:\test>dir /b *.pdf
BII_inv_1489879.pdf
BII_inv_1489880.pdf
0039292647.pdf

F:\test>

Further Reading

5
  • 3
    This answer works for the provided example. However, it's not generic - presumably the OP wants just the filename from the url-escape full path, while this answer depends on the specific location of spaces in the last dir (Boshart Apr 25), and therefore will not work if the last dir is formatted differently.
    – Jonathan
    Apr 17, 2020 at 10:27
  • @Jonathan We can only go by what data the asker provides. If they want a more general solution they should provide additional example data.
    – DavidPostill
    Apr 17, 2020 at 11:16
  • 1
    Why do for /f … in ('dir /b *.pdf') instead of for … in (*.pdf)? Apr 18, 2020 at 15:02
  • @Scott The other variants of for do not support the token options which are used to pick apart the result.
    – DavidPostill
    Apr 18, 2020 at 15:24
  • 1
    OK, so you’re justifying it based on your (IMO, mistaken) impression that the OP wants the filename broken into three pieces, based on spaces (rather than six to eight pieces based on %’s). You’ll note that I have asked the OP for clarification, but I believe that it’s fairly clear already — the question title mentions “files where the % is a delimiter”, and the body says “the last delimiter (the pdf filename) isn't always the 7th token.” Apr 18, 2020 at 15:34
2

Here is a Command Prompt (batch) solution, with a variant.  (I interpret the problem to be to remove everything through (including) the last %nn sequence from each .pdf filename, and rename the file to that.)

@echo off
setlocal enabledelayedexpansion
for %%F in (*.pdf) do (
    set file=%%F
    call :process
)
goto :eof

:process
    set temp=%file%
    :find_all
    REM Remove everything through first %.
    set right=!temp:*%%=!
    REM If there's no change, there were no %'s.
    if not "%right%" == "%temp%" (
        REM Remove the two characters after the %.
        set temp=%right:~2%
        goto :find_all
    )
    if "%file%" == "%right%" (
        REM Filename didn't contain %.
        echo File %file% is unchanged.
    ) else if "%right:~4%" == "" (
        REM Name was abcd%5C.pdf (or abcd%.pdf), so there's nothing left.
        echo Nothing left after truncating %file%
    ) else if exist "%right%" (
        REM The "ren" command will give an error message for this,
        REM but it doesn't say the filename.
        echo Cannot rename %file% because %right% already exists.
    ) else (
        echo ren "%file%" "%right%"
    )
    goto :eof

The overall approach is to take each filename and remove everything through (including) the first %nn sequence, and then repeat that until there are none left.  We have several tricks rolled up into one.

  • We use the %var:str1=str2% syntax to edit the filename.
  • We use the %var:*str1=str2% form, to remove / replace everything through (including) the first occurrence of str1.
  • We use setlocal enabledelayedexpansion, not because we need delayed expansion per se, but so we can do substitution using !var:str1=str2!, so we can use % in str1.
  • As usual, when we want to use % in a batch file, we must type %%.

So the statement set right=!temp:*%%=! sets right to the part of temp after (to the right of) the first %.  This will be a string probably starting with 3A or 5C (because those are the hex characters that appear after % in the example filenames).  Then we strip off those two characters and assign the result back to temp.

Run the above and inspect the output.  If it looks OK, change echo ren to ren so it will actually rename the files.


If the requirement is to remove everything through the last %5C rather than the last %nn, use this:

@echo off
setlocal enabledelayedexpansion
for %%F in (*.pdf) do (
    set file=%%F
    call :process
)
goto :eof

:process
    set temp=%file%
    :find_all
    REM Remove everything through first %5C.
    set right=!temp:*%%5C=!
    REM If there's no change, there were no %5C's.
    if not "%right%" == "%temp%" (
        set temp=%right%
        goto :find_all
    )
    if "%file%" == "%right%" (
        REM Filename didn't contain %5C.
        echo File %file% is unchanged.
    ) else if "%right:~4%" == "" (
        REM Filename was abcdef%5C.pdf, so there's nothing left.
        echo Nothing left after truncating %file%
    ) else if exist "%right%" (
        REM The "ren" command will give an error message for this,
        REM but it doesn't say the filename.
        echo Cannot rename %file% because %right% already exists.
    ) else (
        echo ren "%file%" "%right%"
    )
    goto :eof

Other notes:

  • These have been tested on Windows 7.
  • In principle, these could have been done with a simple for loop, iterating over the files.  But the for loop broke when I used goto, so I had to move the secondary loop into a subroutine.
  • I would normally pass information into a subroutine as an argument.  But that failed when the filenames contained %.
  • These routines do a moderate amount of error-checking.
  • These handle an arbitrary number of %nn (or %5C) sequences.
  • These handle an arbitrary number of spaces, including after the last %nn sequence (i.e., in the new filename).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .