Ask for file version of a list of DLLs - windows

In order to ask for the version of a file, the following commandline request can be launched:
wmic datafile where name="C:\\Windows\\System32\\msiexec.exe" get Version, Name /Value
The result looks like:
Name=C:\Windows\System32\msiexec.exe
Version=5.0.19041.1
I would like to launch this request for all specific DLL files in all subdirectories somewhere on my computer.
So I started as follows:
forfiles /S /M specific.dll /C "cmd /c echo #path"
I received the entire list of specific DLL files, mentioned as entire path names, which was exactly what I wanted. Now I would like to combine both approaches and you can guess, this is going wrong:
forfiles /S /M specific.dll /C "cmd /c wmic datafile where name="#path" get Version, Name"
I get following results:
Node - PORT-DDM (this is the name of my computer)
ERROR:
Description = Invalid query
You might think "Obviously, you are using double quotes within double quotes", but replacing the internal double quotes (") by double double quotes (like name=""#path"") does not solve the issue, and using single quotes neither. (Please don't tell me the issue is due to the single backslashes in #path)
Does anybody know how I can solve this, preferably without using a simple for-loop: I'm trying to understand how to replace the UNIX find ... -exec by Windows forfiles ... /C "cmd /c ..." ....
Edit: Maybe the question can be more general: apparently there is not one question on the entire site, containing the tags [forfiles] and [wmic]: is it possible that both commands don't go well together and why?

Related

Open folder only knowing the name partially

I have a bunch of folders named like "XXXXX John Doe". I know the X part, not the name afterwards, however, the X part is always unique. Manually i could just TAB, however i don't know how to do it via cmd only.
I used to be able to just do this:
"! \n explorer.exe " + rootdirectory + foldername
Now, since the folder structure naming scheme changed, im struggling finding a way to open folders, regardless of it having a (name) suffix or not.
This question is for Microsoft's cmd and it's gonna be called by a VisualFoxPro program.
You could ADIR() in VFP to get the actual name of the file and then use that in the call to Explorer. Presumably you know the name of the folder that contains the folder you're looking for, so something like this:
LOCAL aFolder[1], nFolders, nFolder
CD <the parent folder>
nFolders = ADIR(aFolder, '','D') && gives you all folders in the parent folder
IF nFolders = 1
nFolder = 1
ELSE
nFolder = ASCAN(aFolder, 'XXXXX', -1, -1, 1, 13)
ENDIF
* Now do your call to Explorer, passing aFolder[m.nFolder] as the folder name
Probably the shortest command is using ForFiles but do consider safety improvements below
forfiles /m XXXXX* /C "cmd /c explorer /e,#path"
OR slightly longer but more likely to only run with cd & folder thus not launch a similar filename* by mistake.
forfiles /m XXXXX* /C "cmd /c cd /d #path&explorer ."
This works on the basis that you are searching from the root folder and not somewhere a random explorer bat or otherwise gotcha is lurking.
first as #mofi suggests ensure explorer is well defined so replace that as %SystemRoot%\explorer.exe
/m is based on the premise it is a foldername rather than any filename that is found thus must be run in the sub folders root and you can enforce that by adding /p "rootdirectory" before /m
As constructed it works best if folders are exactly as specified i.e. a fixed unique ID followed by a space and then variable naming.
finally a shorter method but beware the need for escapes and also be aware that without the cd restriction it can launch any random files such as xxxxxx.txt thus I consider the "cd" method above as safest for calling a folder.
forfiles /p "rootfolder" /m XXXXX* /C "cmd /c start \"\" #path"

Not able to delete files using "forfiles" command, but able to extract them

I am trying to delete files older than a certain time period.
I have problems deleting a txt file of certain format.
File: KB4054981_20180130_060034933-Microsoft .NET Framework 4.6.1-MSP0.txt.
When I try to echo all txt files, the above file gets printed. However, I try to delete it, it comes up with "it could not find the file".
Any help is appreciated!
As well as the advice I've already provided in my initial comment, you should note that directories will also pass through with the default pattern, you're needlessly specifying. As directories aren't deleted using the Del command, you should therefore filter those out:
ForFiles /P "C:\Test" /S /C "Cmd /C If #IsDir==FALSE Del #Path"

Why does this batch file close immediately when called from a shortcut, despite it working correctly when called from command line?

Edit 3 (SOLUTION):
As michael_heath described in his answer, the issue came down to two things: how exactly windows builds commands when executing something through a shortcut, and the very specific (and frankly ridiculous) consequences of the \C switch on cmd.exe. For future reference, if any other poor soul stumbles into this StackOverflow question, the problem was fixed by changing the "Target" property in the shortcut to a slightly edited version of Michael's answer, specifically C:\Windows\System32\cmd.exe /C #"C:\{path-to-script}\link.bat". Here's a screenshot too, if necessary, although you unfortunately can't see the whole Target line.
Huge thanks again to Michael.
I am attempting to make a personal batch utility to create a symlink on the desktop, much in the same way "Send to... > Desktop" works with shortcuts. I use symbolic links frequently to allow things like my bash configuration files (.bashrc and .bash_profile, etc) to be version controlled elsewhere for portability, and for several other things on my computer.
For ease of use, my idea was to create a simple batch file to do this for me, and place the symlink on the desktop. Then, I would put a shortcut to this file in the Send To folder so it appears in Send To in the context menu (I am aware that mklink requires admin privileges, so the shortcut is set to run as administrator also).
The following is the file I have written:
#echo off
set f=%~1
set switch=
if exist "%f%\*" set switch=/D
for /F "delims=" %%i in ("%f%") do set name=%%~nxi
mklink %switch% "%USERPROFILE%\Desktop\%name%" "%f%"
if not %ERRORLEVEL%==0 pause
Here is the general idea of what I'm trying to do:
Strip the quotes on the input, if any (%~1)
Check if the input is a directory
Get the base name and file extension of the input (%%~nxi)
Make the link
If an error occurred, pause so it can be seen rather than exiting (because the batch file is called from the shortcut)
It works perfectly fine until I give it an input that contains spaces in the name of the file or directory. I actually haven't tested what happens if there is a directory with a space in the path, but not in the base name of the actual file or directory, but I assume the same problem will be present.
I have made several changes to attempt to get it to work with files with spaces, including stripping the quotes on the input in that first line so that the quotes aren't doubled later, and that "delims=" thing on the for loop. Those two solutions I found here, actually.
But despite my best efforts, no matter what I do, the file closes immediately when given an input with a space. I have littered every line with pauses, run the script from the command line with a manually entered input so it would not exit, and run each individual command (where possible) from the command line.
Infuriatingly, when I run it from the command line or run the individual commands, it all works perfectly even with spaces in the input. I even created another batch file that does nothing but output the input it receives and ran that from Send To, and confirmed that the input is the same as I entered from the command line.
What on Earth is going wrong then when called from that shortcut in Send To?
Edit 1: The properties of the shortcut itself are as follows:
Target: C:\Users{username}\vc\git\util-scripts\bat\link.bat
Start in: C:\Users{username}\vc\git\util-scripts\bat
Here is a screenshot as well:
Because I just made my account here I can't embed the picture but here it is
Edit 2: This is the current code I am using, as suggested by Gerhard Barnard, however the problem still persists:
#echo off
set "fname=%~1"
set switch=
if exist "%fname%\.*" set "switch=/D"
for /F "delims=" %%i in ("%fname%") do (
mklink %switch% "%USERPROFILE%\Desktop\%%~nxi" "%fname%"
if errorlevel 1 pause
)
#echo off
setlocal
rem Set the path for the created symlink.
set "linkdir=%USERPROFILE%\Desktop"
for %%A in (%*) do call :link "%%~A"
rem Check results.
echo: & dir /A:L "%linkdir%" & pause
exit /b
:link
set "switch="
if exist "%~1\*" set "switch=/D"
for /F "delims=" %%A in ("%~1") do set "name=%%~nxA"
if not defined name echo Variable "name" not defined.& exit /b 1
mklink %switch% "%linkdir%\%name%" "%~1"
exit /b 0
Your batch-file code is working.
This code does multiple file or folders.
This code also helped to test all at once,
files, folders, symlinked files and symlinked
folders as targets.
If you only want 1 target to be processed,
then just change the %* to "%~1".
The main issue is the command string in the shortcut.
C:\Users\{username}\vc\git\util-scripts\bat\link.bat
The file type of .bat is going to build a command such as:
C:\Windows\System32\cmd.exe /C "C:\Users\{username}\vc\git\util-scripts\bat\link.bat" %*
%* is substituted with the passed arguments.
If you have the command with an argument with double quotes,
it may look like:
C:\Windows\System32\cmd.exe /C "C:\Users\{username}\vc\git\util-scripts\bat\link.bat" "C:\Users\a file.txt"
The command string after /C has 4 double quotes
and double quotes are at both ends.
The behavior changes due to the double quotes.
A quote from cmd /?:
If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:
1. If all of the following conditions are met, then quote characters
on the command line are preserved:
- no /S switch
- exactly two quote characters
- no special characters between the two quote characters,
where special is one of: &<>()#^|
- there are one or more whitespace characters between the
two quote characters
- the string between the two quote characters is the name
of an executable file.
2. Otherwise, old behavior is to see if the first character is
a quote character and if so, strip the leading character and
remove the last quote character on the command line, preserving
any text after the last quote character.
In section 1, "no /S switch" is true, then the next is
"exactly two quote characters" which is false.
This now applies section 2, which can make the
command string after /C with stripped double quotes:
C:\Users\{username}\vc\git\util-scripts\bat\link.bat" "C:\Users\a file.txt
The space is quoted though the rest is exposed,
which is an invalid command string in this case.
Note that C:\Users\a file.txt is an example
passed argument that was double quoted as
"C:\Users\a file.txt".
A change in the command string in the shortcut to:
C:\Windows\System32\cmd.exe /C echo: & "C:\Users\{username}\vc\git\util-scripts\bat\link.cmd"
The command is now C:\Windows\System32\cmd.exe which
gives some more control with the command string.
After the /C, echo: is used to avoid the command
string beginning with a double quote, which helps to
prevent double quotes being stripped at both ends because
the command string no longer starts with a double quote.
After the & is the command that is important and will
now work even if the arguments end with a double quote.
You can replace the echo: & with another initial command.
You can also use # before the command string, so the shortcut
command string would be:
C:\Windows\System32\cmd.exe /C #"C:\Users\{username}\vc\git\util-scripts\bat\link.cmd"
So whatever works the best for your use case.
Try without the /F option which is not needed here, we also do not need to set a variable as it will work perfectly fine with the expanded meta variable:
#echo off
set "fname=%~1"
set switch=
if exist "%fname%\.*" set "switch=/D"
for %%i in ("%fname%") do (
echo mklink %switch% "%USERPROFILE%\Desktop\%%~nxi" "%fname%"
if errorlevel 1 pause
)

Windows equivalent of Unix "find" command?

I'd like to be able to search files on a Windows machine using the command line instead of the GUI interface. For example, on Linux, I use:
find . -name "*.c" -exec grep -Hn "sqlcommand" {} \;
Is there something similar with Windows?
After long time working with Unix systems I had to make some scripts on Windows.
For serious scripting Powershell is the tool you should use.
You can search Internet with keywords like powershell find string in file,
or other combinations and you find a lot of information.
That's the problem, a simple oneliner like
get-childitem C:\yourdir -include *.c -recursive |Select-String -pattern sqlcommand
won't help you much. You need to find the PowerShell IDE, learn the different syntax and try to love / accept that new stuff.
Prepare for a study with PowerShell when you want to do these things more often, or try to get a Unix-like environment on your windows (cygwin, or better git for windows)
NEW AND IMPROVED ANSWER
I recently stumbled upon a built-in command that is rather similar to find in Unix:
ForFiles
Basic syntax is:
forfiles [/p <Path>] [/m <SearchMask>] [/s] [/c <Command>] [/d [{+|-}][{<Date>|<Days>}]]
There are several variables to use when constructing the command to execute per each file (via the /c switch):
#FILE File name.
#FNAME File name without extension.
#EXT File name extension.
#PATH Full path of the file.
#RELPATH Relative path of the file.
#ISDIR Evaluates to TRUE if a file type is a directory. Otherwise, this variable evaluates to FALSE.
#FSIZE File size, in bytes.
#FDATE Last modified date stamp on the file.
#FTIME Last modified time stamp on the file.
It looks like you would use the command like this:
FORFILES /m *.cs /c FINDSTR /I /N /C:"sqlcommand" #FILE
I'm not sure how long this command has been around, but the earliest reference I could find in the documentation is from 2008-09-02:
https://web.archive.org/web/20080902221744/http://technet.microsoft.com:80/en-us/library/cc753551.aspx
and that page states that it was last updated on "April 25, 2007". The documentation is filed under "Windows Server" so it likely started there and was added to the desktop OSes starting with Windows Vista, I believe. I did check Windows XP and didn't see it there, though it is on Windows 10.
ORIGINAL ANSWER
This requires a combination of two DOS commands:
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]
and
DIR /B /O:N /W *.c (this is the 'command' noted in the FOR command above)
Create a CMD script as follows:
#ECHO OFF
FOR /F %%B IN ('DIR /B /O:N /W *.cs') DO (
findstr /I /N /C:"sqlcommand" %%B
)
OR, just use the find command found in this set of Unix command ports:
http://unxutils.sourceforge.net/
or
http://sourceforge.net/projects/unxutils/
(both links should be the same project)

How do I deal with quote characters when using cmd.exe

I'm trying to do this:
cmd.exe /C "C:\Program Files\Somewhere\SomeProgram.exe" > "C:\temp\Folder Containing Spaces\SomeProgram.out"
However, I have problems which are down to the way cmd.exe works. If you read the help for it, it handles " characters in a special way. See the help at the end of question. So, this doesn't execute correctly... I'm guessing cmd.exe strips some quotes which makes the statement ill-formed.
I can do this successfully:
// quotes not required around folder with no spaces
cmd.exe /C "C:\Program Files\Somewhere\SomeProgram.exe" > C:\temp\FolderWithNoSpaces\SomeProgram.out
But, I really need the first one to work. Is there away around the strange quote processing that cmd.exe uses? I want it to preserve all of the quotes, but there doesn't appear to be an option to make it do that.
Help taken from output of: cmd /?
If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:
1. If all of the following conditions are met, then quote characters
on the command line are preserved:
- no /S switch
- exactly two quote characters
- no special characters between the two quote characters,
where special is one of: &<>()#^|
- there are one or more whitespace characters between the
the two quote characters
- the string between the two quote characters is the name
of an executable file.
2. Otherwise, old behavior is to see if the first character is
a quote character and if so, strip the leading character and
remove the last quote character on the command line, preserving
any text after the last quote character.
Ah. doh. Think I've answered my own question.
If you use /S, and wrap the whole thing in quotes, it just removes those outer quotes.
cmd.exe /S /C " do what you like here, quotes within the outermost quotes will be preserved "
I think you'll find that your example works absolutely fine as it is.
cmd.exe /C "C:\Program Files\Somewhere\SomeProgram.exe" > "C:\temp\Folder Containing Spaces\SomeProgram.out"
I have reproduced your example here
http://pastebin.com/raw.php?i=YtwQXTGN
C:\>cmd /c "c:\Program Files\my folder\my long program.exe" > "c:\temp\spaces are here\a.a"
C:\>type "c:\temp\spaces are here\a.a"
my long program.exe has run
C:\>
further example demonstrating it works with "my long program.exe", removing cmd /c, it operates fine too.
C:\>"c:\Program Files\my folder\my long program.exe" > "c:\temp\spaces are here\
a.a"
C:\>type "c:\temp\spaces are here\a.a"
my long program.exe has run
C:\>
Another example, but with replace. replace with no parameters says "source path required" "no files replaced"
C:\>replace > a.a
Source path required
C:\>type a.a
No files replaced
Exactly the same effect when they're in folders with spaces.
C:\>cmd /c "c:\Program Files\my folder\replace.exe" > "c:\temp\spaces are here\r.r"
Source path required
C:\>type "c:\temp\spaces are here\r.r"
No files replaced
C:\>
further demonstration with replace
without cmd /c works fine too.
C:\>"c:\Program Files\my folder\replace.exe" > "c:\temp\spaces are here\r.r"
Source path required
C:\>type "c:\temp\spaces are here\r.r"
No files replaced
C:\>
The reason why your example works fine
cmd.exe /C "C:\Program Files\Somewhere\SomeProgram.exe" > "C:\temp\Folder Containing Spaces\SomeProgram.out"
and how/why it works the way it does, is because the > is interpreted as special by the host.exe So this part cmd.exe /C "C:\Program Files\Somewhere\SomeProgram.exe" - I think - is evaluated first. i.e. cmd /c does not see the > and after.
cmd /? shows 2 cases
Case 1 and Case 2. Your example fits Case 1
If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:
1. If all of the following conditions are met, then quote characters
on the command line are preserved:
- no /S switch
- exactly two quote characters
- no special characters between the two quote characters,
where special is one of: &<>()#^|
- there are one or more whitespace characters between the
two quote characters
- the string between the two quote characters is the name
of an executable file.
2. Otherwise, old behavior is to see if the first character is
a quote character and if so, strip the leading character and
remove the last quote character on the command line, preserving
any text after the last quote character.
You can test for sure that your example fits case 1, because if you add /s (without adding any more quotes or making any change at all to your example other than adding /s), then you get a different result, because it makes your example hit case 2. So that proves that your example is definitely a case 1. And it clearly meets all the criteria of case 1.
If your example were a case 2, and you added /s, then it'd make no difference.
Your answer is interesting because it shows an alternative way of getting your result, but in case 2. By adding additional outter quotes and adding /s.
But actually, when you add those additional outter quotes, then you've just made it a case 2, and adding a /s on top of that won't make a difference.
C:\>cmd /c "c:\Program Files\my folder\replace.exe"
Source path required
No files replaced
C:\>cmd /s /c "c:\Program Files\my folder\replace.exe"
'c:\Program' is not recognized as an internal or external command,
operable program or batch file.
C:\>cmd /c ""c:\Program Files\my folder\replace.exe""
Source path required
No files replaced
C:\>cmd /s /c ""c:\Program Files\my folder\replace.exe""
Source path required
No files replaced
C:\>
The example in your question worked fine
cmd.exe /C "C:\Program Files\Somewhere\SomeProgram.exe" > "C:\temp\Folder Containing Spaces\SomeProgram.out"
Your alternative (with the /S and outer quotes) you give as an answer to make the example work, works fine too
cmd.exe /S /C ""C:\Program Files\Somewhere\SomeProgram.exe" > "C:\temp\Folder Containing Spaces\SomeProgram.out""
Though your answer which is an alternative, can actually be simplified by removing the /S because it's already a case 2, so adding /s won't make any difference. So this would improve the solution given in your answer
cmd.exe /C ""C:\Program Files\Somewhere\SomeProgram.exe" > "C:\temp\Folder Containing Spaces\SomeProgram.out""
Your example which you described as a problem in your question, and your solution, produce the same good result. But one big difference I suppose, (and I am not sure how to test for it), but one difference in the way your example works, and the way the solution in your answer works, is I think in the case of your example, the hosting/invoking cmd.exe does the redirect to the file. Whereas in your solution's example, the invoked cmd.exe is passed the > by the host cmd.exe, and so the invoked cmd.exe does the redirect. Also of course, your example is a case 1, while your solution is an amendment you made (very well) to make it work in case 2.
I hope I haven't erred here, I may have. But your question and answer did help me wrap my head around how cmd and in particular cmd /c is working!
Perhaps your example was an oversimplification of your actual one, and your actual one did fail and needed your amendment. If your example case, had been a tiny bit more complex, by for example, having a parameter to the program that took quotes, then it'd fail Case 1, and you would indeed need outter quotes (/S would not change the result, so no /S would be necessary, as it'd already be a case 2 once you add those needed outer quotes). But the example you gave in your question actually seems to me to work fine.
Added - A related Q and A What is `cmd /s` for?

Resources