31158

Piped Variable Into FINDSTR w/ Regular Expressions and Escaped Double Quotes

Question:

I am trying to understand a batch file that was sent to me in order to work around a bug in a third party program while they resolve the issue. Basically they are running a findstr regular expression command in order to determine whether or not the string matches. If it does, then the special characters that should not be stripped out are being added back in manually before it is passed off to the original commandline program.

As best I can tell though, what has been provided does not work or I do not understand it. I am pasting the relevant section of code below.

@echo off setlocal set username=%1 shift echo %username% | findstr /r "^\"[0-9][0-9]*\"" >nul if not errorlevel 1 (set username=";%username:~0,9%=%username:~10,4%?") echo %username%

The three pieces I really have questions about are as follows:

<ol><li>I believe the unescaped interpretation of the regular express above is ^"[0-9][0-9]*" which I think means that the string must <em>begin</em> with a numeric character and then must consist of <em>zero or more</em> additional <em>numeric-only</em> characters in order for a match to be found. Well, FINDSTR seems to be doing something weird with the escaped quotes and I cannot get it to match anything I have tried. If I remove the \" around [0-9][0-9]* then I can get it to work, but it does not properly reject non-numeric characters such as an input string of 123456789O1234 (there is a letter O instead of a zero in that sample string).</li> <li>What is the point of the >nul</li> <li>Wouldn't it be better to check for an errorlevel equal to 0 instead of "not errorlevel 1" since it could <a href="https://stackoverflow.com/questions/8844868/what-are-the-undocumented-features-and-limitations-of-the-windows-findstr-comman" rel="nofollow">possibly return an error level of 2</a>?</li> </ol>

Anyway, the following code works, but it is not as precise as I would like. I am just looking to understand why the quotes in the regex string are not working. Perhaps this is a limitation of FINDSTR, but I have not came across anything definitive yet.

@echo off setlocal set username=%1 shift echo %username% | findstr /r "^[0-9][0-9]*" >nul if not errorlevel 1 (set username=";%username:~0,9%=%username:~10,4%?") echo %username%

I can workaround the problem by repeating the class 14 times since that is the number of characters in my situation (<a href="https://stackoverflow.com/questions/2635740/why-does-findstr-not-handle-case-properly-in-some-circumstances/8767815#8767815" rel="nofollow">more than 15 classes will cause it to crash</a> - scroll to the bottom). I am still curious as to how this could be achieved more simply, and of course the remaining 2 questions.

<h2>EDIT / WORKING SOLUTION</h2> @echo off setlocal enableDelayedExpansion set username=%~1 shift echo !username!|findstr /r /c:"^[0-9][0-9]*$" >nul if not errorlevel 1 (set username=";!username:~0,9!=!username:~10,4!?") echo !username!

NOTES:

<ul><li>When I first ran it after modifying my existing code to more cloesly resemble dbenham's, <a href="http://ss64.com/nt/delayedexpansion.html" rel="nofollow">enableDelayedExpansion</a> gave an error as did the quotes around setting the username (see below). I can't replicate what I did wrong, but it is all working now (this is in case someone else comes across the same issue).</li> <li>I had tried the $ for the EOL marker (which is the key to forcing it match numeric content only), but I think that the other problems were getting in the way which made me think it was not the solution. Also, to ensure the $ works don't miss this part of dbenham's answer "...you must also make sure there are no spaces between your echoed value and the pipe symbol."</li> <li>In short it pretty much seems that trying to put double quotes inside a regex for findstr is wrong syntax/does not work/etc... unless you are actually looking to match " in the string/files you are parsing through. See dbenham's answer for clarity here. As he noted, you can use %~1 to strip the quotes from the argument instead of adding it to your regex (and programmatically add them back in if needed).</li> </ul>

Error Message

C:>sample.bat 123456789 'enableDelayedExpansion' is not recognized as an internal or external command, operable program or batch file. '"' is not recognized as an internal or external command, operable program or batch file. !username!

Reference Links:

<ul><li><a href="https://stackoverflow.com/questions/8844868/what-are-the-undocumented-features-and-limitations-of-the-windows-findstr-comman" rel="nofollow">Undocumented features and limitations of the Windows FINDSTR command</a></li> <li><a href="https://stackoverflow.com/questions/2635740/" rel="nofollow">Case sesntive anomalies with findstr (not handling case properly in some circumstances)</a></li> <li><a href="http://ss64.com/nt/findstr.html" rel="nofollow">http://ss64.com/nt/findstr.html</a></li> <li><a href="http://www.robvanderwoude.com/findstr.php" rel="nofollow">http://www.robvanderwoude.com/findstr.php</a></li> <li><a href="http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/findstr.mspx" rel="nofollow">http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/findstr.mspx</a></li> </ul>

Answer1:

Answering your questions in reverse order:

3) if not errorlevel 1 is probably the same as if %errorlevel%==0 because IF ERRORLEVEL 1 means if ERRORLEVEL is greater than or equal to 1. So putting a NOT in front means if ERRORLEVEL is less than 1. I believe FINDSTR never returns a negative ERRORLEVEL, so the syntax should be OK.

2) The >nul redirects the stdout output of FINDSTR to the nul device, meaning it disables the output. Normally any matching line would be printed. You are only interested in the return code - you don't want to see the output.

1) The original regex will match any input string that starts with a quote, followed by at least one digit, followed by another quote. It ignores any characters that may appear after the 2nd quote.

So the following strings (quotes included) will match:

<ul><li>"0"</li> <li>"01234"</li> <li>"0"a</li> <li>"01234"a</li> </ul>

The following strings will not match:

<ul><li>0</li> <li>01234</li> <li>""</li> <li>"0a"</li> </ul>

The original code has problems if the number of digits in the matching string reaches a certain length because the ending quote gets stripped causing the closing ) to be quoted and so the rest of the script fails.

I don't understand your requirements so I don't know how to fix the code.

It sounds like you don't want to match strings that have non digits. That means you need to include the end of line marker $ at the end of the regex. But you must also make sure there are no spaces between your echoed value and the pipe symbol.

I believe you probably don't want quotes in your value, (or else you should programatically add them at the very end). You can use %~1 to strip any enclosing quotes from the supplied argument.

If you are looking to check if argument 1 consists of nothing but numeric digits, then you can use:

setlocal enableDelayedExpansion set "username=%~1" echo !username!|findstr /r "^[0-9][0-9]*$" >nul

I used delayed expansion because you have no control over what characters are in %1, and if it contains special characters like & or | it will cause problems if you use normal expansion. The syntax I have given is not bullet proof, but it handles most "normal" situations.

It is not necessary in your case, but I prefer to use the /c option, just in case your search string contains spaces. So the above could be written as

echo !username!|findstr /r /c:"^[0-9][0-9]*$" >nul

It seems odd to me that both the original and your modified code simply pass through the username if it does not match your regex. Maybe that is your intent, maybe not.

Recommend

  • Where is the source for: “Function application has higher precedence than infix operators” [Haskell]
  • How to compile picoProlog from source code?
  • How to stop tomcat 7 with maven in eclipse
  • matplotlib issues when nan first in list
  • Overriding array literal in JavaScript
  • How do I remove duplicates from an AutoHotkey array?
  • Best style for iterating over a small number of items in Python?
  • How does MemberWiseClone create a new object with the cloned properties?
  • How do I revert sys.stdout.close()?
  • closing WCF proxy
  • Do I have to rewrite an html header everytime I want to use it?
  • Grouping by blank nodes
  • How to override List.Add method?
  • Selectively hide background elements when overlayed with transparent div
  • ZipList with Scalaz
  • Enumerating Controls on a Form
  • Implement JwtBearer Authentication in NSwag SwaggerUi
  • What is the correct way to synchronize a shared, static object in Java?
  • order post according to custom array position
  • How to disable all widgets inside Panel or inside Composite?
  • Does it make sense to call System.gc() and Thread.sleep() when working on Bitmaps?
  • azure media services - The request body is too large and exceeds the maximum permissible limit
  • Atlas images wrong size on iPad iOS 9
  • Avoid links criss cross / overlap in d3.js using force layout
  • Is there a javascript serializer for JSON.Net?
  • NetLogo BehaviorSpace - Measure runs using reporters
  • What is the “return” in scheme?
  • Join two tables and save into third-sql
  • Where to put my custom functions in Wordpress?
  • How to model a transition system with SPIN
  • ActionScript 2 vs ActionScript 3 performance
  • ORA-29908: missing primary invocation for ancillary operator
  • Buffer size for converting unsigned long to string
  • Java static initializers and reflection
  • unknown Exception android
  • Binding checkboxes to object values in AngularJs
  • Observable and ngFor in Angular 2
  • Unable to use reactive element in my shiny app
  • Converting MP3 duration time
  • java string with new operator and a literal