BATCH: How to capture the extension of the file, or how to do a pattern test like regexp - windows

I'm writing a batch file for windows command prompt, I want to loop through files in a directory recursively using FOR /R .
However, inside the loop I want to check if the file of extension .txt or possibly other extensions in order to skip.
How can I do that? in FORFILES there is #ext what to use with for?
Also, is there something similar to regexp in command prompt?

%~xV will expand variable V to an extension only. If you think you need regexp, then ditch the batch altogether, and use e.g. Python, otherwise you're only going to be writing unmaintainable mess.
Also, for the love of Cthulhu, console window is not even remotely related to MS-DOS.

Complementing Cat++ answer, for the second question, FINDSTR command accepts regular expressions as search strings.
Although it is not a full featured regexp command, for search and replace, it is pretty useful in many simple situations.
Try HELP FINDSTR
Or for an introduction and a list of the supported regex expressions by FINDSTR see this doc http://technet.microsoft.com/en-us/library/bb490907.aspx .

Related

Run command in cmd from Ruby

I know this is probably a really simple question, but I am having a really hard time finding a solution. I am working on windows in cmd. I am simply trying to use this command:
find /i \"chef_server_url\" C:/chef/client.rb
I know the command works. It simply returns a url in a file that I wanted to check.
I want to execute this in code and return the results in my code. But I am having a hard time formatting the string especially with the escape characters
I am using %x[] to do so. So ideally I would just say
%x[find /i \"chef_server_url\" C:/chef/client.rb]
But the c:/chef/client.rb will not format correctly because it thinks /c and /c are not supposed to be that way
Are you trying to access the Chef Server URL from inside Chef? You can use Chef::Config[:chef_server_url] but the deeper question is what you trying to do?
The short answer to your question is that there is more than one way to run a system command in ruby:
The Kernel#system method takes a plain string, so as long as you can get your command accurately represented in a string, it should work. Something like system 'find /i "chef_server_url" C:/chef/client.rb' should work.
Backticks will do similar, which should look something like this: find /i "chef_server_url" c:/chef/client.rb (wrap in `backticks`, I'm not sure how to format on StackExchange).
I'd first recommend getting a string representation of your command, puts it out, copy paste to a command window. If it works, then send it through system.
However, like others here, I suspect there is a better solution to the underlying problem you're trying to solve.
I'm not anywhere near a windows computer for testing, so you'll have to experiment with these.

Dos dir mask, want "*.xxx" and not "*.xxxzz"

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

Windows Command Line: Search and replace value

I'm trying to find and replace the value of something in a text file and am having trouble figuring out how I would do that.
For example, I would like to go into a file that has android:versionName="1.53" and replace it with android:versionName="1.54".
I know how to do this if I just want to replace 1.53 with 1.54 for example, using a program called FART (find and replace text) but I want to be able to replace whatever is in between the speech marks with 1.54, i.e. without having to specify 1.53 as this could change each time.
Another similar example would be replacing #define DLS_VERSION (1540) with #define DLS_VERSION (1600).
I was just wondering if there's a standard way or utility to do this please?
Thanks,
Chris.
Any regular expression (regex) search and replace utility should work just fine. There are many free options for Windows, though none native to batch.
One good option is REPL.BAT - a hybrid JScript/batch utility that works on any Windows machine from XP forward, and does not require download or installation of any executable.
Assuming REPL.BAT is in current directory, or better yet, somewhere within your PATH:
type "yourFile.txt"|repl "(android:versionName=\q).*?(\q)" "$11.54$2" x >"yourFile.txt.new"
move /y "yourFile.txt.new" "yourFile.txt" >nul
type "yourFile.txt"|repl "(#define DLS_VERSION \().*?(\))" "$11600$2" >"yourFile.txt.new"
move /y "yourFile.txt.new" "yourFile.txt" >nul
Full documentation is embedded within the REPL.BAT script. A simple web search can find many sites that explain regular expressions.

What means tilde in windows file pattern

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.

Space before command circumvents DOSKEY

I wanted keep my users from running "dir" in the command line, so I used DOSKEY to alias "Dir" to "CLS". The testers found out that putting a space before "DIR" will circumvent the alias.
I've tried to put a space before "DIR" when setting up the DOSKEY, but the command prompt ignores the white space.
Anyone found a way of making DOSKEY acknowledge spaces?
Thanks.
Deny your users the List Directory contents permission on all relevant locations. That's probably the easier way. That way they can run dir but it won't be of any use.
I'm not even trying to figure out why you want such a thing, though.
Regarding doskey: As you noticed, macro substituion is done literally and only at the beginning of the command line. So what do you want to do? Create macros for dir to cls with 1, 2, 3, ..., 8188 spaces before it?
Blacklisting almost never works, and it certainly isn't going to in this case. You can, for example, list the files in a directory simply by pressing TAB repeatedly.
Instead, use whitelisting. Write a console application that takes user input, checks that the input is a command that the user is allowed to run, and if so, passes that command to the shell - or, better still, implement the "approved" commands yourself, so that (a) there can't be any trickery with special characters, and (b) you can remove cmd.exe from the approved applications list - you are using software restriction policy, right?
Even if you could figure out how to make your DOSKEY macro idea work (I don't think you can), it would be pointless. Your users could easily circumvent the restriction by creating the following batch file:
#dir %*
DOSKEY macros do not work within batch files, so there is nothing to stop the batch file from executing. And your users could name the batch file anything they want, so you would have a devil of a time policing.

Resources