3

I got a folder with filenames ending with space (after the extension), like this:

"File.txt "

Windows, of course, doesn't like me rename/erase/move the file. I noticed that I can rename it manually each file using Winrar or other 3rd party apps.

Is there anyway to batch rename all files?

phuclv
  • 30,396
  • 15
  • 136
  • 260

2 Answers2

3

The (excellent) shareware program TotalCommander can deal with this.

To test I created a bunch of files with a space at the end of the extension using WSL in a Windows directory: for N in {1..20}; do touch "text$N.txt "; done,

This resulted in:

drwxrwxrwx 1 user user 4096 Oct 18 20:52  ./
drwxrwxrwx 1 user user 4096 Oct 18 20:47  ../
-rwxrwxrwx 1 user user    0 Oct 18 20:52 'text1.txt '*
-rwxrwxrwx 1 user user    0 Oct 18 20:52 'text10.txt '*
-rwxrwxrwx 1 user user    0 Oct 18 20:52 'text11.txt '*
-rwxrwxrwx 1 user user    0 Oct 18 20:52 'text12.txt '*
-rwxrwxrwx 1 user user    0 Oct 18 20:52 'text13.txt '*
-rwxrwxrwx 1 user user    0 Oct 18 20:52 'text14.txt '*
etc...

I checked in Windows Explorer, it cannot rename any of these files.

To resolve:

  • Open TotalCommander (there's a portable version if you want)
  • navigate to the folder in question
  • select all files you want to rename
  • open the rename tool via Files -> Multi-Rename Tool...
  • In that tool select the RegEx checkbox, search for $ ([space][dollar sign]) and replace it with <Clear> (= nothing): enter image description here
  • after pressing Start! all spaces at the end of a filename are removed, and the Windows Explorer can work with the files again.
2

PowerShell, cmd and most other Win32 applications have no issue interacting with the file as long as you know how to bypass the Windows name normalization with the \\?\ prefix:

PS D:\space> 1..20 |% { New-Item -Path "\\?\D:\space\test$_.txt " }

PS D:\space> Get-ChildItem |% { '"' + $_.Name + '"' } "test1.txt " "test10.txt " "test11.txt " "test12.txt " "test13.txt " ... "test8.txt " "test9.txt "

PS D:\space> Get-ChildItem |% { Rename-Item ("\?$PWD" + $.Name) ($.Name.SubString(0, $_.Name.Length - 1)) }

PS D:\space> Get-ChildItem |% { '"' + $_.Name + '"' } "test1.txt" "test10.txt" "test11.txt" "test12.txt" "test13.txt" ... "test8.txt" "test9.txt"

In cmd you can also do the same

D:\space>for /l %i in (1, 1, 10) do @echo %i > "\\?\D:\space\%i.txt "
D:\space>forfiles

"1.txt " "10.txt " "2.txt " "3.txt " "4.txt " "5.txt " "6.txt " "7.txt " "8.txt " "9.txt " D:\space>for /l %i in (1, 1, 10) do @ren "\?\D:\space%i.txt " "%i.txt"

D:\space>forfiles

"1.txt" "10.txt" "2.txt" "3.txt" "4.txt" "5.txt" "6.txt" "7.txt" "8.txt" "9.txt"

In short, NTFS is fully POSIX-compliant and can contain names with any characters except / and NUL. But the Win32 namespace is much more limited, and Win32 applications using normal Win32 APIs can't access files with names that are invalid in the Win32 namespace, which include names ending in spaces or periods

Do not end a file or directory name with a space or a period. Although the underlying file system may support such names, the Windows shell and user interface does not. However, it is acceptable to specify a period as the first character of a name. For example, ".temp".

Naming Conventions

The \\?\ prefix sends the name directly to the driver, which allows us to operate on files with invalid names, or files with very long paths. Many tools like 7z, winrar, robocopy, TotalCommander... know how to prepend \\?\ so they can deal with those files, but for normal tools you need to prepend it yourself

In fact PowerShell Core also use \\?\ automatically in various places, unlike PowerShell 5.1 and older which you have to do that yourself. But there are still some bugs here and there so sometimes you'll have to remove \\?\ and let PowerShell Core do that itself

There are lots of related and duplicate questions:

phuclv
  • 30,396
  • 15
  • 136
  • 260