In the source folder there are 3 files:
a.csv
b.csv
a.csv_backup
I would expect *.csv to copy a.csv and b.csv only, but it copies a.csv_backup as well.
Code:
Dim oFso As New Scripting.FileSystemObject
oFso.CopyFile "c:\temp\*.csv" "d:\temp\"
You're running into the fact that each file has a "short name" (the old DOS 8.3 standard) for compatibility with really old software (some of which is still kicking around). Your file a.csv_backup is also known by another name (probably something like a~1.csv though it could be about anything) which only uses the first three letters of the extension. You can run dir /x to see the short names alongside each long name.
Further reading:
On Superuser: Windows wildcards with files having more than 3 characters extensions
From Raymond Chen (Developer who has worked on Windows for a very long time): Why does FindFirstFile find short names? (about the API used internally by many applications to look for files by pattern)
Wikipedia: 8.3 filename
In terms of a solution, you either need your backup extension to not share the first three characters of what you're searching for (so use something like .backup_csv instead), or you need to disable short names on your system (which can break old applications).
Related
In my directories, I have file names like *.xxx and also *.xxxzz
When I do dir /s/b "*.xxx" I get *.xxxzz files in my list. I do NOT get these results in a "Take Command" console, but I do in a cmd console.
How do I get cmd to give me only *.xxx files?
With the DIR command, when you specify a mask containing an extension of exactly three characters, you will get matches of files that contain extensions with three or more characters, so long as the first three characters match the extension you originally specified.
I have no idea why it works this way, but at least the behavior is consistent nearly everywhere in the Windows API where you can specify a file search pattern. I can only assume it has something to do with support for long file extensions (i.e., file names that don't comply with the old DOS 8.3 rule).
But, you can get around the behavior in two ways:
A mask that specifies a file extension with one, two, or more than three characters will return only files with extensions of exactly the specified length.
So, for example, dir /s/b "*.xx" will give you only files with the extension .xx, and dir /s/b "*.xxxzz" will give you only files with the extension .xxxzz.
You can use the question mark wildcard character, instead of the asterisk. Asterisks mean "replaced by zero or more characters", while question marks mean an exact substitution of the question mark with a single character.
I suspect you're running into a problem because of the way Windows (older versions, at least) generated a short 8.3 filename to improve compatibility with old programs. You can probably confirm this by doing dir /x *.xxx in the directory where your *.xxxzz files exist.
I'm not sure if there's a way around it from the limited Windows command line tools. There should probably have been a switch on the dir command to force consideration only of long filenames, but I don't see one.
You may be able to solve your problem by disabling short filenames on that volume, if you're sure you don't need them for any ancient software you're running.
I haven't tried that myself, so maybe the short names already generated will continue to exist after you follow those instructions. If so, you might be able to fix it by copying the tree you're working with to a new location.
The fact is that unless the system has been set up to not generate 8.3 names, every file or directory with a long filename will also have an 8.3 alias. At least one - with some of the warped constructs in use in later editions, there many be many aliases.
Academically, since it's a matter of opinion (and hence outside of SO's bailiwick) it could be argued that your alternative command processor is not producing the correct results since it apparently ignores the short filename. Which is "correct" is debatable - what suits in one application may not in another. The easiest and most logical way of course is to have an option - but the chances of the major player in the debate incorporating such a facility at this stage amount to well,Buckley's
Here's a routine that may suit. It's not bullet-proof as it will have problems with some "poison characters" (those with special meaning for the standard command-processor cmd.exe(A windows application that emulates and enhances many of the facilities available in DOS, and normally, though technically-incorrectly, called "DOS" by the politically-incorrect for the sake of brevity.))
#ECHO Off
SETLOCAL
SET "mask=%~1"
IF "%mask:~-4,1%"=="." ECHO(%mask:~-3%|FINDSTR /L "* ." >NUL&IF ERRORLEVEL 1 (
FOR /f %%a IN ('dir /s/b "%mask%"') DO IF /i "%%~xa"=="%%~sxa" ECHO(%%a
GOTO :EOF
)
dir /s/b "%mask%"
GOTO :EOF
I have pattern to search. Say "*.txt".
Now I have some files I do not want to list there. I believe they do not match this pattern.
But on windows, they do.
I know tilde character is used to make short form of legacy 8.3 filename. That is LongFilename.json might be LONGFI~1.JSO. But I did not know they are handled somehow on windows in file search patterns. They are. I cannot find any documentation about what they mean and how to match files my way.
My problem is NOT with short forms. Or I think it is not directly related to it.
I have file "A.txt". Now I wanted temporary file and used "A.txt~". It is unix backup files that is not usually visible. But on windows, they should not have special meaning by itself. Only for my application.
Now I want list of "*.txt" files. Command
dir *.txt
returns to my surprise also all .txt~ files in the same directory. And I do not want them. I use FindFileFirst from Win32 API. I did not find anything about tilde character in documentation. FindFileFirst(".txt", handle) returns also files "A.txt~". Can I use some flag to exclude them? I know I can make special condition, like I have for "." and "..". How does ~ operator work? A.txt~1 is also matched. Is everything after tilde ignored? Is that feature or bug?
I am testing that on Windows 7 Professional, 64 edition, if that changes anything.
FindFirstFile also includes short names for legacy reasons so the pattern *.txt will include anything with an 8.3 representation ending in *.txt which includes *.txtANYTHING , not just the ~ character (see dir /xfor what's being matched against).
You will need to filter in your FindNext enumeration.
If you are searching for .txt files for example, you can use "kind:text" option in windows to exclude txt~ and similar files since they are not a recognized type anymore.
That's something that works on regular windows search. I'm not 100% sure about the API, but it should also be there.
Is it possible to use Windows API function FindFirstFile to search for multiple file types, e.g *.txt and *.doc at the same time?
I tried to separate patterns with '\0' but it does not work - it searches only the first pattern (I guess, that's because it thinks that '\0' is the end of string).
Of course, I can call FindFirstFile with *.* pattern and then check my patterns or call it for every pattern, but I don't like this idea - I will use it only if there no other solutions.
This is not supported. Run it twice with different wildcards. Or use *.* and filter the result. This is definitely the better choice, wildcards are ambiguous anyway due to support for legacy MS-DOS 8.3 filenames. A wildcard like *.doc will find both .doc and .docx files for example. A filename like longfilename.docx also creates an entry named LONGFI~1.DOC
The MSDN docs mention nothing about FindFirstFile allowing multiple search patterns, hence it doesn't exist.
In this case your best bet is to scan using an open selection (like C:\\some directory\* or *) and then filter based on WIN32_FIND_DATA's cFileName member, using strrchr (or the appropriate Unicode variant) to find the extension. It should run pretty fast for the small set of characters that make up a file extension.
If you know the that all the extensions are say 3 characters, you should be able to mask it off as *.??? to speed things up.
Does anyone know a pure Win32 solution for renaming a file and only changing its capitalization, that does not involve intermediate renaming to a different name or special privileges (e.g. backup, restore).
Since the Win32 subsystem generally regards two file names differing only in capitalization as the same, I haven't been able to find any solution to the problem.
A test program I made with the MoveFile API seems to work. So does the rename command in cmd.exe. What have you tried, and what error are you getting?
This isn't relevant, but further testing shows that renaming a long filename in this way works but will change the short filename (alternating between ~1 and ~2 for example), incidentally.
Just use the normal MoveFile API. That call probably just turns into ZwSetInformationFile(..., FileRenameInformation,...) The docs for FILE_RENAME_INFORMATION states that you need DELETE access and the file can't be locked etc, but those restrictions will probably apply to other solutions also.
I do not believe there is a way to expose two files with identical names that differ only in spelling to the Win32 subsystem. Even if some how you were able to create these files, the most likely result would be that only one file would be accessible - defeating the purpose of staying soley in Win32.
If you want to go into the Native layer, you can create a file with NtCreateFile and InitializeObjectAttributes w/o OBJ_CASE_INSENSITIVE or you can pad the end with extra spaces (if you pad with extra spaces, the file will not be accessible from Win32 dos paths). See here: http://www.osronline.com/ddkx/kmarch/k109_66uq.htm . I'm pretty sure you were already aware but I included it incase you did not know.
So long as your file is not immediately needed by another program, you can use my solution.
When you rename the file, capitalize, and delete the last letter. Then rename again and return the letter.
:)
We are using a proprietary application for inventory management and have discovered this application is unable to interpret spaces in file paths. For example:
C:\Google Drive\Invoices
Does not work, whereas
C:\Google\Invoices
does work.
Is there a special way to represent a space in Windows much like a URL string can use %20? For example C:\Google%20\Drive\Invoices.
Use 8.3 short name.
Try dir /x c:\
Google Drive should have a short name, probably like GOOGLE~1
Then you can use C:\GOOGLE~1\Invoices
You can use short names if supported. Type dir /x and it's in the middle column.
However it only works if short names aren't turned off. If short names aren't available the only way is making a junction point or a symbolic link1.
Run cmd as admin and type either of the following
mklink /J C:\ggdrive "C:\Google Drive"
mklink /D C:\ggdrive "C:\Google Drive"
This will create a link from ggdrive to the real Google Drive folder and now you can access Google Drive as ggdrive
However it's highly probable that you've used the path incorrectly. In some places you need to quote paths with spaces like this "C:\Google Drive\Invoices". But if an application in the last 15-20 years doesn't support long file names then it is rubbish anyway. Use a better program or report to the developer to fix it.
1 The differences between them is like this
What is the difference between NTFS Junction Points and Symbolic Links?
“directory junction” vs “directory symbolic link”?