Save output from FIND command to variable - batch-file

Like the title says, I'm trying to take the output from a FIND command and save it to a variable. Specifically, I'm using:
DIR /b /s "C:\" | FIND "someexe.exe"
to locate a specific .exe file, which seems to work fine, but then I want to save the results of FIND to use later in the same script.
I've tried various different tweaks of:
for /f "usebackq" %%i in (`DIR /b /s "C:\" | FIND "someexe.exe"`) do SET foobar=%%i
but when I try to run the script the command window immediately closes (presumably due to some error, I tried putting a PAUSE command in the next line to no avail).
I assume it's some stupid minor thing that I'm doing wrong but if someone could show me what it is I'd appreciate it. Just for further reference, I don't care how many copies of "someexe.exe" exist, I just need the path for one of them.

You should be getting this error: | was unexpected at this time.. Your immediate problem is unquoted special characters like | must be escaped using ^ when they appear in a FOR /F ('command').
for /f "usebackq" %%i in (`DIR /b /s "C:\" ^| FIND "someexe.exe"`) do SET foobar=%%i
It sounds like you are running your batch file by double clicking from either your desktop or Windows Explorer. That works, but then the window immediately closes after the batch terminates. In your case it terminates before reaching PAUSE because of the syntax error.
I always run my batch files from a command window: From the Start menu you want to run cmd.exe. That will open up a command console. Then CD to the directory where your batch file resides and then run the batch file by typing its name (no extension needed). Now the window stays open after the script terminates. You can examine your variables using SET, run another script, whatever.
There is no need to use FIND in your case - DIR can find the file directly. Also, the path of your file may include spaces, in which case it will be parsed into tokens. You need to set "DELIMS=" or "TOKENS=*" so that you get the complete path.
I never understand why people use USEBACKQ when they are executing a command. I only find it useful if I am trying to use FOR /F with a file and I need to enclose the file in quotes because of spaces and/or special characters.
Also, you may run across errors due to inaccessible directories. Redirecting stderr to nul cleans up the output. Here again, the > must be escaped.
for /f "delims=" %%F in ('dir /b /s "c:\someexe.exe" 2^>nul') do set foobar=%%F

Related

Dir /b /s different in cmd.exe and batch file

I want to know why there is a delay between dir /b/s C:\*.* in cmd.exe and batch file.
I tried the blow batch file, But it takes about one hour to show me the result, but dis /b/s in cmd.exe show the result fast.
for /f "tokens=*" %%a in ('dir/b/s c:\*.*') do (
echo "%%a"
copy "%%a" C:\windows\ )
Please help me to show the result in batch file fast like cmd.exe.
There are two elements that lead to this behaviour
for /f will always retrieve all the data that it needs to process before starting to process it. Than means that for /f will "sit" (not execute the code in the do clause) while dir works, waiting for all the data.
When for /f reads a disk file it will "simply" acomodate a buffer large enough to load it into memory, load the file and start processing it. But when the data source is a command execution, not knowing the final size of the data, a buffer is defined and resized as needed while retrieveing the command's output.
The need to retrieve all the data and the process of resizing the buffer is what generates the delay.
Why? As a example:
If I use dir /s /b c:\windows I get a list of 119343 files, 13MB of data.
As the memory buffer defined by for /f starts at 4KB and is increased in 4KB each time it is full it will need 3327 resize operations.
Each time a resize is needed, a new 4KB larger buffer is allocated and the data inside the old buffer is copied into the new larger buffer. For 13MB we need 3327 resize operations which means aprox. 21GB in memory copy operations (data to copy increases each time the buffer is resized). Maybe it does not seem a lot and memory is fast, but sometimes (ex. here) things are not so simple.
If you add the time needed to retrieve the data from disk to the time needed to handle the memory allocation/memory copy, before starting to process the data, you have a visible delay.
If you need remove the delay, don't use a for /f. A better option (while keeping a similar approach) could be
for /r "c:\" %%a in (*) do (
echo "%%~fa"
)
That is, use the recursive version of the for command, from the indicated starting folder.
First, dir/b/s c:\*.* is of invalid syntax. It works because of Windows command processor automatically corrects the command line. Correct would be:
dir /b /s C:\*.*
Or 100% valid shorter:
dir /b /s C:\*
Or 100% valid shortest:
dir /b /s C:\
The general syntax on Windows command line is:
command/executableSPACEargument1SPACE"argument 2"SPACE/option
No space between command dir and its first argument /b is not 100% correct syntax. It is in general not good in no scripting and programming language on writing code depending on automatic correction of the syntax by the application interpreting the code. I see this daily on visiting lots of websites with an old browser where the old browser fails to display a webpage right just because of incorrect code in one of the files of the webpage which are displayed fine by latest browsers because of their excessive auto-detection and auto-correction of syntax errors caused by the people writing the files of the webpage.
The execution of the DIR command in the batch file is slower because of FOR with option /F and a set in '...' starts in background using %CompSpec% /C a new command process for execution of the DIR command line. Everything finally output to handle STDOUT is captured by FOR and processed after started cmd.exe terminated itself.
FOR with option /F ignores all empty lines on processing captured output of additional command process. This behavior cannot be changed with options.
FOR with option /F splits up each line into substrings (tokens) using normal space and horizontal tab character as string delimiters. The string splitting behavior can be controlled by using option delims= whereby using this option with specifying no characters turns off the string splitting behavior.
FOR with option /F assigns to specified loop variable by default only the first space/tab separated substring. This behavior can be controlled with option tokens=. The usage of "tokens=*" results in removing all leading spaces/tabs and assign rest of captured line to the specified loop variable.
FOR with option /F ignores also all lines starting with a semicolon by default. This behavior can be controlled by option eol= (end of line).
So what happens on execution of this command line:
for /f "tokens=*" %%a in ('dir/b/s c:\*.*') do ( echo "%%a")
FOR starts in background a command process which executes the command DIR.
DIR searches for non-hidden files and directories on drive C: and all its non-hidden subdirectories and outputs their names with full path to handle STDOUT of the background command process captured by FOR.
In background started cmd.exe terminates itself after DIR finished.
FOR processes the captured lines.
There are no empty lines because dir /B outputs no empty lines.
There are no lines starting with spaces/tabs because dir /B /S results in output of file and directory names with full path starting with drive C: in this case. A file or directory name without full path could begin with one or more spaces.
There are no lines starting with ; also because of dir /B /S. A file or directory name can have a semicolon as first character, but not on being output with full path.
FOR runs the command ECHO for each string assigned to loop variable a.
Better would be the command line:
for /F "eol=| delims=" %%I in ('dir /B /S C:\ 2^>nul') do echo "%%I"
This command line would work also on DIR option /S not used resulting in output of just file/folder name without path even for files/folders starting with a semicolon or a space character. The end of line option is defined with vertical bar because of no file/folder name can contain | according to the Microsoft documentation Naming Files, Paths, and Namespaces.
It is advisable to run a command in a command prompt window with /? as first and only argument to get displayed the help/documentation for this command before using it. Try it out with for /? and dir /? in a command prompt window and run also cmd /? because of this executable processes a batch file.
There is the executable %SystemRoot%\System32\robocopy.exe for copying an entire directory tree or deprecated older executable %SystemRoot%\System32\xcopy.exe. See Windows Commands and SS64.com - A-Z index of the Windows CMD command line for documentation of these two external commands in addition to running them in a command prompt window with /? for a brief help.

Batch script fails when run as admin from explorer, but not when run as admin from terminal?

I have a batch script that needs to run as admin. I will be distributing to users so it would be best if they can run it from Windows Explorer.
Unfortunately, it doesn't work when run from explorer (right click -> run as admin). It does work when called from a pre-existing admin terminal.
Initially I thought the problem was with the active directory, but I added a "cd /d %~dp0" as the first command. I confirmed through echo that this places them both in the same directory, but it still fails when running from explorer.
The failure occurs when reading an external file in the same directory as the .bat. It pulls empty strings when run from explorer. Here is sample code:
rem Make sure active directory is correct (verified that this works)
cd /d %~dp0
rem Load parameters from params.txt
for /f "delims== tokens=1,2" %%G in ("params.txt") do set %%G=%%H
rem Print params (it's a loop so you can read it when running from expl.)
for /l %%a in (1 1 100000) do echo %DST%
Then you just need to make sure params.txt is in same directory as .bat and includes the line "DST=some\directory\name"
Anybody know why this doesn't work?
As has been pointed about by #nephi12 in his answer if your file name does not have spaces you can remove the quotes, otherwise it thinks the IN clause is a string you want to parse. If you need to quote your file names then you need to use the USEBACKQ option as pointed out by the comments. Once you use that option your code works just fine.
But I would like to make a point with your code. If the contents of your params.txt file is:
"DST=some\directory\name"
Then your FOR command can just be this:
for /f "usebackq tokens=1 delims=" %%G in ("params.txt") do set %%G
I am not understanding why you are echoing the %dst% variable 100,000 times?
For one thing, take away the "s from around params.txt as double-quotes means string parsing, while unquoted is a list of files.
Second, try prepending params.txt with %~pd0\ to ensure the correct path, rather than changing directory.

How to specify /D in FOR command?

The Windows command line interpreter features a FOR command, which is able to parse the output of a given command and execute the loop for each line of the output, e.g.:
FOR /F %%i IN ('DIR .') DO echo %i # Outputs each file name
The command (DIR .) is executed in a child command line via cmd /C <command> <command-arguments>, however, the /D parameter is not specified ... this leads to weird behavior if the user has a AutoRun command with output (e.g. echo, or cls).
Is there a way to force FOR to execute the command via cmd /C /D <command> <command-arguments>?
You have run across one of the many design flaws of cmd.exe, and this one has bothered me for quite some time. I'm pretty sure there is no way to suppress AutoRun when FOR /F executes a command.
What makes this especially irritating is that pipes also use CMD /C (one for each side of the pipe), but the designers of the pipe were smart enough to incorporate both the /D and /S options. It is really a shame the designers of FOR /F couldn't have done the same.
I believe your only recourse One option is to be defensive within your AutoRun command definition. I suggest putting all the AutoRun commands within a batch script that has something like the following at the top:
#echo off
if defined AutoRunComplete exit /b
set AutoRunComplete=1
REM Put your AutoRun commands below...
But if you cannot control the AutoRun commands, then I think you are out of luck. Aacini's idea of using a temporary file to get around the problem is an effective and simple solution.
A very simple solution for your problem is use a file in the for /F command instead of a command. This way, we just emulate the internal operation of for /F over a command, but executing each step explicitly: 1. Execute the command and store its output in a temporary text file. 2. Process all lines in the temporary file. 3. Delete the file.
DIR . > TempFile.txt
FOR /F %%i IN (TempFile.txt) DO echo %%i
DEL TempFile.txt
When you have many FOR /F blocks for parsing program output, then it could be useful to add a cmd.exe wrapper.
This wrapper can be installed with
set "comspec=C:\somewhere\cmdWrapper.exe"
FOR /F %%i IN ('DIR .') DO echo %%i
The wrapper itself has to start the original cmd.exe with /D /C.
But the behaviour of the comspec variable itself is a bit strange.

Why is /F typically used with FOR loops even when dealing with non-text files?

I was looking for a batch script to identify the newest file in a directory. The examples I found all use FOR /F. When I read the help documentation on FOR, it states that /F opens, reads and processes each file.
Why is /F used in this case? I've used it with large binary files and the script does not seem to slow down so I do not think each file is actually being opened, etc.
I tried using FOR without /F to do the same job and didn't have any luck. Is there a reason for that?
For instance:
FOR %%I IN ('dir "*.AVI" /B /O:D') DO set newestAvi=%%I
does not seem to work. For some reason, newestAvi is equal to "/O:D'" at the end.
If I use
FOR /F "delims=|" %%I IN ('dir "*.AVI" /B /O:D') DO set newestAvi=%%I
then things work.
Thanks,
Dave
I think the relevant bit of the help file is
Finally, you can use the FOR /F command to parse the output of a
command. You do this by making the filenameset between the
parenthesis a back quoted string. It will be treated as a command
line, which is passed to a child CMD.EXE and the output is captured
into memory and parsed as if it was a file. So the following
example:
So, with the /F your command takes the output from
dir "*.AVI" /B /O:D
and parses each line into the command
newestAvi=%%I
which becomes
newestAvi=FileName.AVI
for each file in the current directory. The last value assigned is the one that is left at the end of the for commands execution.

Trying to process files using a Batch. No output, Nothing happens

I'm trying to pass files one by one(I have to dot that since executable only accepts one file at a time). So, in my batch I have follwoing:
FOR /F %file IN ('dir /b /s *.css') DO CALL myExecutable.exe %file
I should see out files in same directory but nothing happens, no errors are displayed either.
Am I missing something here?
You have several mistakes in your example:
FOR parameter name is a single letter only
CALL is used to call another batch file or a subroutine in the existing batch file, not executables
the FOR parameter should be referenced with two %, when in batch file
you need to use a non-space delimiter, if the directory you run this command in or any subdirectory, or if any of the files has a space in the name
With these in mind, here's the right command you should be using:
for /f "usebackq delims=|" %%f in (`dir /b /s *.css`) do myexecutable.exe "%%f"
Here's my answer to a similar SO question, where I give more details on using FOR to process all files in a directory.

Resources