Stop DIR command searching after first hit is found - batch-file

In CMD.EXE I can search for something using DIR - take the below example:
DIR C:\*EXCEL.EXE /A:-D /B /S
Although it will take a while to get through the whole structure of the C:\ drive, the first match is relatively quick.
Q: Is there a way to automatically stop the command from continuing the search once a hit has been found?
I'm asking because I actually want to shell a cmd prompt and read the StdOut to another script, but there will be a very long pause if I wait for the entire search to finish.

you can't stop dir, but you can use anoter method:
for /r "c:\" %%i in (excel.exe) do (echo -- %%i & goto :eof)

This by far has been the fastest way to do this that I have found.
Run this to get the location...(Way faster than dir)
where /r %systemdrive% inventor.exe
If you actually want to use the output run this...
where /r %systemdrive% inventor.exe > %userprofile%\loctest.txt
Then run this to set what ever variable you want to use.
NOTE: (Remember only use 1 % if you are running this manually instead of in a .bat or .cmd)
for /f "tokens=*" %%G in (%userprofile%\loctest.txt) do (set testvar=%%G)
You can then run an echo %testvar% to see the value...
Here is what is looks like from the command line

I came to this chat searching a way to cd to a directory with a given partial name; based on the Stephan's suggestion above, using a batch file this seems to work nicely for me:
in cww.bat::
for /D /r "." %%i in (*%1*) do (cd %%~pfxi & goto :eof)

Related

CMD trying to set variable in a loop

I'm working with windows' cmd and trying to set a variable in a loop. Here's the code I have:
for /d %%a in ("F:\backup*") do set folder=%%a
ECHO %folder%
PAUSE
I want to look for a folder with name starting with "backup" on drive F and save that folder's name to %folder% variable. So for example if the folder would be called "backup 2017-01-18" I'd like that saved to a var.
Instead it doesn't seem to set anything as the ECHO just prints that "ECHO is on". The for loop is correct and the folder is there as well (I'm already using that piece of code for other batch with robocopy).
I could theoretically put all my code inside the FOR loop and use %%a instead of the %folder% var but that seems like a hacky solution.
All the solutions I found so far pointed to using EnableDelayedExpansion. I modified the code to use it like that:
Setlocal EnableDelayedExpansion
for /d %%a in ("F:\backup*") do set folder=%%a
ECHO !folder!
PAUSE
But now ECHO prints "!folder!" as if it would not detect the variable. If I revert to ECHO %folder% I once again learn that "ECHO is on".
EDIT:
I found the issue here. I was also running another batch file on the backup folder. It turns out that ROBOCOPY (which I used in that batch) is setting the enclosing folder to hidden, system and readonly by default (even if copied files are not hidden or system o_0). When I removed HSR attributes on the directory the code posted here started working fine (the initial version).
If your loop for /d %%a in ("F:\backup*") do does not detect any directories whose names begin with backup, they either do not exist or there are the attributes hidden and/or system set.
To detect also such hidden or system directories, replace the for /D loop by this:
rem Change to parent directory "F:\" temporarily in order for the `~f` modifier to resolve the full path properly:
pushd "F:\" || exit /B 1
for /F "eol=| delims=" %%D in ('dir /B /A:D /O:N "backup*"') do set "folder=%%~fD"
popd

How to get the FIND command output count value into a variable in batch scripting?

I am using a command:
find /c "abc" "C:\Users\abc\Desktop\project\string.txt"
Output:
---------- C:\Users\abc\Desktop\project\string.txt: 4
I want to assign this value 4 to a variable so that I can use it for an if statement.
I would use:
For /F %%A In ('Find /C "abc"^<"C:\Users\abc\Desktop\project\string.txt"') Do (
Set "mlc=%%A")
Your %mlc% varaiable would hold the matched line count.
I'm not sure if this is the best method to do this, but it works:
find /c "abc" "C:\Users\abc\Desktop\project\string.txt" > tmpFile
set /p myvar= < tmpFile
del tmpFile
with your snytax the output is ---------- C:\Users\abc\Desktop\project\string.txt: 4
There is another syntax: type file.txt|find /c "abc", which gives you a beautiful output of just:
15
To get it into a variable, use a for /f loop:
for /f %%a in ('type file.txt^|find /c "abc"') do set count=%%a
echo %count%
(for use directly on commandline (not in a batchfile) use %a instead of %%a)
I am not a batch script / Windows command line pro, but I got this to work with the following, without a temporary file:
for /f "delims=" %%a in ('dir "%~dpSomeFolder\*.suffix" /b ^|find /c "suffix"') do set "fileCount=%%a"
Explanation:
I found it very confusing, why assigning a variable with a batch script, is so weird, considering it's "Windows", the most used operating system. Anyways this answer here is helpful. Even if it is a duplicate, I like the formatting more:
Assign command output to variable in batch file
%~dp0: basically translates to "path of this script". You can find info about this online.
SomeFolder\*.suffix: In my case this I was looking to count the number of files ending with a certain suffix. I had problems using the dir command with \s as this listed all the matches in subfolders I did not expect him too look. As if this was referenced to the path from which I executed this script from. Therefore, the path name with the asterisk "\*.suffix" solved that issue for me.
^|: When using the pipe sign "|" in "for command", specified inside the single quotation marks, you need to use a circumflex "^|", instead of just the "|", which you would normally use when just typing in the command in cmd (f.e. like dir "%~dp0Folder*.suffix" /b | find /c "suffix"
%%a: You have to use the "double percentage", as this is just a locale variable when writing this in a batch script.
FYI: you can have a look at the command help/ manual ("man" as I am used to Linux), with the command /? (f.e. dir /? or find /?)
I thought I would also mention how I then used this variable, as this might save some time for you ;) (batch code coloring somehow did not work here...).
IF %fileCount% NEQ 1 (ECHO Number of SUFFIX files does not equal 1! Found %fileCount% SUFFIX files inside the SomeFolder. Aborting script! & PAUSE & EXIT)

Windows batch file: get last folder name from path

I'm trying to rename .jpg files which is in one of many subdirectories of e:\study\pubpmc\test\extracted.
I want to rename files to LastFolderName_ImageName.jpg.
(For example if Figure1.jpg is in e:\study\pubpmc\test\extracted\folder1
I want it to be renamed like this: folder1_Figure1.jpg)
So I need to take out the last folder name from the file's path.
Since it's my first time with batch scripting, I'm having a hard time.
I googled and made code similar to it
but it doesn't seem to work out.
Can you help me with it and tell me where I've done wrong?
Thank you! :)
#echo off
cd /D "e:\study\pubpmc\test\extracted"
for /r %%f in (*.jpg) do (
set mydir=%%~dpf
set mydir=%mydir:\=;%
for /f "tokens=* delims=;" %%i in (%mydir%) do call :LAST_FOLDER %%i
goto :EOF
:LAST_FOLDER
if "%1"=="" (
#echo %LAST%
goto :EOF
)
set LAST=%1
SHIFT
goto :LAST_FOLDER
)
JosefZ explains the obvious problems with your code, but he failed to point out a subtle problem, though his code fixed it:
FOR /R (as well as the simple FOR) begin iterating immediately, before it has finished scanning the disk drive. It is possible for the loop to reiterate the already named file! This would cause it to be renamed twice, giving the wrong result. The solution is to use FOR /F with command 'DIR /B', because FOR /F always processes the command to completion before iterating.
JosefZ also provides code that works for most situations. But there is a much simpler solution that works always:
#echo off
for /f "delims=" %%A in (
'dir /b /s /a-d "e:\study\pubpmc\test\extracted\*.jpg"'
) do for %%B in ("%%A\..") do ren "%%A" "%%~nxB_%%~nxA"
The "%%A\.." treats the file name as a folder and walks up to the parent folder. So %%~nxB gives the name of the parent folder.
The command could be run as a long one liner, directly from the command line (no batch):
for /f "delims=" %A in ('dir /b /s /a-d "e:\study\pubpmc\test\extracted\*.jpg"') do #for %B in ("%A\..") do #ren "%A" "%~nxB_%~nxA"
Avoid using :label and :: label-like comment inside (command block in parentheses). Using any of them within parentheses - including FOR and IF commands - will break their context.
Using variables inside (command block in parentheses). Read EnableDelayedExpansion: Delayed Expansion will cause variables to be expanded at execution time rather than at parse time [and CLI parses all the (command block in parentheses) at once]
Next script should work for you. Note rename statement is merely echoed for debugging purposes.
#ECHO OFF >NUL
SETLOCAL enableextensions disabledelayedexpansion
set "fromFolder=e:\study\pubpmc\test\extracted"
rem my debug setting set "fromFolder=D:\path"
for /F "tokens=*" %%f in ('dir /B /S /A:D "%fromFolder%\*.*"') do (
set "mydir=%%~ff"
set "last=%%~nxf"
call :renameJPG
)
#ENDLOCAL
goto :eof
:renameJPG
rem echo "%mydir%" "%last%"
for /f "tokens=*" %%i in ('dir /B /A:-D "%mydir%\*.jpg" 2^>nul') do (
echo ren "%mydir%\%%~nxi" "%last%_%%~nxi"
)
goto :eof
Resources:
SETLOCAL, disableDelayedExpansion, ENDLOCAL etc.
An A-Z Index of the Windows CMD command line
Windows CMD Shell Command Line Syntax
I already wrote a function for that. You give it any path and it returns you only it's filename or pathname. Works for any path: Url, Windows path, Linux path, etc...
Copy this function at the end of your batch script: (Instructions below)
rem ===========================================================================
:Name_From_Path
SetLocal
set _TMP_FOLDERNAME=%1
for %%g in ("%_TMP_FOLDERNAME%") do set _TMP_FOLDERNAME=%%~nxg
EndLocal & set _Name_From_Path=%_TMP_FOLDERNAME%
goto :EOF
rem ===========================================================================
Usage:
CALL :Name_Of_Path e:\study\pubpmc\test\extracted\folder1
ECHO %_Name_From_Path%
Result: folder1
If your program or com file traverses these folders when renaming, then it should be able to get the present working directory ( path ), pwd. You may be able to chop everything but the LAST_FOLDER out of this by also creating a PREVIOUS_FOLDER and doing a string replacement.
Or you may be able to break the folder names at the '\' token from the pwd into an array and use a -1 array reference to get the last folder name.
In any circumstance you'll want to check for a present working directory command.
If your creating a large text list of all these and issuing a single call to the batch file.
Then you may be better off with something like:
(Symmantic code warning )
(copy) /folderbase/some_folder/oneormore1/image_from_oneormore1.jpg (to) /folderbase/some_folder/oneormore1/oneormore1_image_from_oneormore1.jpg
Instead of copy, window uses rename, linux uses mv.
The latter example would require simply creating a duplicate list and replacing the \ with a _ while parsing through the tokens.
The code you've given is difficult to make sense of, so its hard to discern if you can simple concatenate the current folder and image name (stringify) and then write or rename them where they are.

Windows FOR command not expanding system environment variables

I have been using this site for sometime and have found it quite valuable in finding solutions to scripting issues. Today I resolve to ask a question as I have not found my solution after exhausting 3 days here, on Superuser, and Googling. So much for all about me, now my point...
I have several installs of a program, each slightly different version or flavor. Each install has a text file defining variables for its use. One of these variables defines a folder for user specific settings, USERSET=%APPDATA%\foo1\foo2. Overtime these user settings can become corrupt. I want to be able to delete these folders with a batch file or script. In short, I want to search a directory for text files that contain a specific string which sets a variable that points to a folder, get the value of that variable and use it to remove a directory.
This is what I have so far:
for /f "tokens=2 delims==" %%G in ('dir /b /s c:\foo\*.txt ^| findstr /i /f:/ userset') do rd /s /q %%G
The output looks like this:
H:\>rd /s /q %appdata%\foo1\foo2\USERSET
The system cannot find the file specified.
Apparently %appdata% does not expand?
Of course if I just type rd /s /q %appdata%\foo1\foo2\USERSET at the command prompt, it works.
I'm open to other languages, I am mostly familiar with Windows command line.
This is because environment variable expansion is done at parse time of a statement. While your for loop is running and %%G actually gets a value it's too late; variables have already been expanded.
You can work around this with a subroutine, I guess:
pushd C:\foo
for /f ... %%G in (...) do call :process "%%G"
popd
goto :eof
:process
call set "X=%~1"
rd /s /q "%X%"
goto :eof
I'd simply insert a CALL before the RD
....CALL RD ...
although I'd also try
....CALL ECHO(RD ...
first to verify - just for safety's sake.

Batch file that moves files when string is not in filename or when a file has finish writing

A program is writing flatfiles on a certain directory, the problem is that another program is moving the file before it finishes the writing the flatfile. i have no control on both programs i can only set the path/directory for both so i decided to create a batch file that moves the files when "~" is not in the filename. since temporary files have "~" in their filenames. can you tell me what's wrong with my program?
echo off
%%a%% = Dir /b|Find /V "~" (C:\source)
move %%a%% (C:/dest)
pause
Any suggestion wouldbe helpful
Thanks!
I tried noodles code
for /f %%A in ('dir /b^|findstr /i /v /c:"~"') do move %%A (C:\dest).
error is
the filename,directory name or volume label syntax is incorrect
im pretty sure the C:\dest exist
for /f %%A in ('dir /b^|findstr /i /v /c:"~"') do echo %%A
See for /?, dir /?, and findstr /?.
Seems to be an XY problem to me.
Sadly, your valiant attempt at a solution is more error than not.
Returning to the actual problem - we have a file being created which needs to be moved after it has been created.
The critical question would be - how do we know that creation is complete?
It is possible but not probable that the file is indeed created as a temporary file, then renamed when complete. It could be that the being-created filename contains ~ but I'd regard the assumption as highly unreliable. ~ occurs frequently in filenames, but very often in short filenames (the 'short' name for a filename that does not follow the 8.3 pattern).
The normal method for creating a file is to open it, write to it, then close it when complete. Might sound obvious, but it's also possible to re-open it and append more data then re-close it, but that would be a relatively rare approach.
In all probability, the open-write-close scenario is used by the creation application. The trick we can use in batch here is that it will appear to have a zero-length until it is closed, hence
for /f %%a in (*.*) do if %%~za neq 0 ECHO(move "%%a" "c:\dest\"
should work (note that \ is a directory-separator; / is a switch-indicator. Note also that the move command is merely echoed - remove the echo( to activate the move after verification)
If we have the open-write-close-reopen-write_again-close scenario, it becomes more complicated. What we would do then is take a directory list at intervals (say 5 sec or so) and compare it to the previous list. If the filelength of a file remains stable and >0 then that file would seem ready to transfer, if not then wait for the next dir snapshot.
Back to OP for verification/comment...
Launch this in the folder and see what the echo command writes to the console.
#ehco off
for /f "delims=" %%A in ('dir /b /a-d^|findstr /v "~" ') do echo move "%%A" "d:\folder"

Resources