Using the caret to split lines,
dir ^
*.bat ^
/w
works as expected, but
dir ^
"*.bat" ^
won't let me enter the "/w". I guess the caret does not work after a double quote. Is this a bug? Or if this is a feature, what is it's use and how can I get around it?
I found the answer myself:
dir ^
^"*.bat^" ^
/w
works as I want. In the second line there must be spaces before the first caret and after the last. (using Vista SP2)
An alternative solution that has worked for me (Windows Server 2012) is to include a tab on the subsequent line. For example:
dir ^
"*.bat" ^
/w
In my experience a tab character can replace a space when delimiting arguments to a command or script. I also feel that this has a cleaner look.
However, I haven't spent time testing it under every possible batch-file syntax scenario and it's possible there are some where it won't apply or work properly.
Related
I am trying to use the rem command to place a remark in a command line that contains several commands. Here are some examples to illustrate what I mean:
echo Hello & rem.Comment & echo world!
(echo Hello & rem.Comment) & echo world!
This works perfectly fine, both echo commands in each line are executed as I expect. The . seems to modify the behaviour of the rem command so that it does not treat the remaining line as comment:
Hello
world!
If I placed a SPACE (or any other delimiter TAB, ,, ;, =) instead of the ., the remaining line and therefore the second echo would be ignored (for the second example a More? prompt appears, because the ) is part of the remark and cmd expects a closing ) because of the ():
Hello
I found out that beside ., the following characters work as well: :, /, \, [, ] and +.
What else works is escaped delimiters: ^SPACE, ^TAB, ^,, ^; and ^=.
Nevertheless, is there a secure and reliable way to do that?
I would be very glad about a solution that works for both command prompt and batch-files.
According to this external reference, the familiar syntax echo. for returning a blank line fails under certain circumstances, hence using echo( is recommended as this is the only reliable method.
However, for rem, the ( does not work, everything after rem( is not recognised as a command.
Since I am aware of a weird bug of the rem command in Windows XP (reference this external link: rem %~), I am interested in a solution that applies to Windows Vista, Windows 7 or higher.
The "weird" REM %~ "bug" is not limited to XP. It is present in all modern versions of Windows that use CMD.EXE. After reading your question, I wrote Simon of SS64 a note to give clarification on the issue. REM can also fail if variable var exists, and you have rem %var:=.
So technically, there is no guaranteed safe way to blindly use REM.
But, if you are willing to accept the fatal % expansion risk, most of your listed hacks are safe to use, but only if the line includes at least one additional command via & or &&.
REM. is never safe to use in any situation if there exists a file named REM (without extension).
The folder dividers \ and / always fail if the current folder contains a file named test.bat and you use REM\..\test.bat.
In a similar fashion, REM:\..\test.bat always fails.
Every one of the other hacks can fail stand-alone in a similar situation. For example, REM^[tab]\..\test.bat fails stand-alone, but works if concatenated with another command. This is the only type of situation I've found where +, [, ], or ^[tab] can fail.
There are additional cases where some of the other hacks can fail.
Any character in the set C (^[space], ^,, ^;, ^=) that are valid in file names can fail stand-alone if remC.bat exists. For example, the following fails stand-alone:
rem^ Fails if "rem .bat" exists
Yet they are all safe when concatenated with another command:
echo OK&rem^ This is safe
rem^ This is safe &echo OK
Temporary Update
Some of the above is wrong. Investigations are ongoing at http://www.dostips.com/forum/viewtopic.php?f=3&t=6895&p=44813#p44813.
I believe the following are the simplest forms that are guaranteed to work in all cases (disregarding invalid % expansion)
REM: At least one space (or other token delimiter) must be after :
REM\ At least one space (or other token delimiter) must be after \
REM/ At least one space (or other token delimiter) must be after /
REM^[tab] At lease one space (or other token delimiter) must be after [tab]
But I won't correct the earlier info until the dust has settled
End Temporary Update
My favorite way to use inline comments is to use impossible variables. Only dynamic pseudo variables can contain = in a name, and no variable name can ever contain two =. So I like to use %= Remark goes here =%. The beauty of this form is it can be used pretty much anywhere with impunity, as long as the comment does not contain % or :. It can even be used safely within parenthesized blocks of code.
for %%F in (*) do (
%= Comment within code block =%
%= 2nd comment within code block =%
FINDSTR /B %=Must match beginning of line=% "string" %= Search string =% "%%F" %= File to search =%
)
This variants of REM seems to be a safe way to enable the & sign in the comment part.
REM/
REM\
REM:
Despite of #dbenham's comment, I can't create any file which would iterfere with these REM variants (I tried REM.bat, REM;.bat and so on).
It's always a good idea to add a space after the REM^<char>.
The problem with %~ can't be solved, as the cmd.exe uses multiple parser phases for each line.
And the %~ error is detected in an early phase (percent expansion phase), just before the phase where a REM would be detected.
But at all, I prefere percent comments for inline comments, described by dbenham
EDIT:
I removed the carets from REM^<char> as it's doesn't matter.
Normally a REM remarks the rest of the line, as the batch parser detects the REM keyword in phase2 of the parser and switches to a specialized parser only for REM.
But when a character is appended to REM the keyword will nt be detected in phase2.
If the character is one of \/;,=+( the parser will remove it later and executes a normal REM command.
That's the cause why the command operators &, &&, |, || can be recognized in this case.
Why rem/ | break fails, but (REM/) | break works?
It's because the pipe starts two seperate cmd child processes.
With surrounding parenthesis the command will be parsed the first time in the child process.
But without parenthesis, the parent process has already parsed the REM/ and checks if the file exists (but doesn't execute it).
But when such a file exists then the parser is smart enough to remove the seperator character and detects that REM is an internal command.
This behaviour looks a bit strange.
Why does this .BAT line split with caret fail?
Because not only does ^ continue the line by ignoring the linefeed, it also escapes the first character of the next line. So the > is treated as a literal instead of as redirection.
You can get the exact same error result using:
type C:\temp.txt ^> C:\temp2.txt
You can fix the multiline version by putting a space before the redirection
type C:\temp.txt ^
> C:\temp2.txt
The rules are actually a bit more complex than what I have described. See jeb's explanation of how caret works at end of line
I noticed that cmd seems to accept some characters at the ends of commands. for example all of the following function correctly:
cls.
cls;
cls(
cls\
cls+
cls=
cls\"whatever"
cls\$
cls\#
and these do not:
cls'
cls$
cls)
cls-
cls#
cls\/
Does anybody know why this happens?
Thanks in advance.
It depends on the batch parser.
;,= are general batch delimiters, so you can append/prepend them to the most commands without effect.
;,,= ,=; echo hello
;,cls,;,,
The . dot can be appended to the most commands, as the parser will try to find a file named cls (without extension) cls.exe cls.bat, and when nothing is found then it takes the internal command.
The opening bracket is also a special charcter that the parser removes without error.
The \ backslash is used as path delimiter, so sometimes it works but sometimes you could change even the command.
cls\..\..\..\windows\system32\calc.exe
I got a useful tip from this post: https://stackoverflow.com/a/374363/151453 , but plagued by doskey's special characters.
(env: Windows 7 and Windows XP)
Using Visual C++ command line, we have env-vars INCLUDE and LIB. So with this doskey macro,
doskey whichinclude=for %i in ($1) do #echo.%~$INCLUDE:i
we can easily findout which .h is found first in which INCLUDE directory, really convenient.
However, this trick fails for LIB. I just CANNOT simply code a macro like:
doskey whichlib=for %i in ($1) do #echo.%~$LIB:i
Call whichlib winsock32.lib, it spouts The system cannot find the file specified.
I launch Procmon to know what happens, it reveals:
So I realize $L has special meaning for doskey, it is replaced with current drive letter when run.
Try double dollar( #echo.%~$$LIB:i ), still not working, Procmon report CMD accessing C:\echo .
Counld someone kindly help me out?
My doskey book mark: http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/doskey.mspx?mfr=true
I agree with Michael Burr's comment - you may be better off with a batch file. I generally do not use DOSKEY macros because they do not work within batch files, so it seems kind of pointless. In my mind, if a command works on the command line, it should also work within a batch file.
But... it is possible to do what you want :)
The $ only has special meaning if it is followed by a character that has special meaning to DOSKEY. The $L is interpreted as the < character (input redirection). The MS documentation implies that $$L should give a $L literal, but the documentation is not correct, as you have discovered.
The DOSKEY $ substitution happens before the normal command line parsing. The trick to embed a literal $L in your macro definition is to put an intervening character between $ and L that is not treated as special by DOSKEY, but that disappears during the normal command line parsing - The ^ works perfectly. $^ has no special meaning to DOSKEY, and ^L simply becomes L during command line parsing.
You can list the definition of your DOSKEY macros by using DOSKEY /M.
The full definition you require is whichlib=for %i in ($1) do #echo(%~$^LIB:i.
The ^ must be escaped when you define the macro. So the complete line to define the macro becomes:
doskey whichlib=for %i in ($1) do #echo(%~$^^LIB:i
The following Perl statements behave identically on Unixish machines. Do they behave differently on Windows? If yes, is it because of the magic \n?
split m/\015\012/ms, $http_msg;
split m/\015\012/s, $http_msg;
I got a failure on one of my CPAN modules from a Win32 smoke tester. It looks like it's an \r\n vs \n issue. One change I made recently was to add //m to my regexes.
For these regexes:
m/\015\012/ms
m/\015\012/s
Both /m and /s are meaningless.
/s: makes . match \n too.
Your regex doesn't contain .
/m: makes ^ and $ match next to embedded \n in the string.
Your regex contains no ^ nor $, or their synonyms.
What is possible is indeed if your input handle (socket?) works in text mode, the \r (\015) characters will have been deleted on Windows.
So, what to do? I suggest making the \015 characters optional, and split against
/\015?\012/
No need for /m, /s or even the leading m//. Those are just cargo cult.
There is no magic \n. Both \n and \r always mean exactly one character, and on all ASCII-based platforms that is \cJ and \cM respectively. (The exceptions are EBCDIC platforms (for obvious reasons) and MacOS Classic (where \n and \r both mean \cM).)
The magic that happens on Windows is that when doing I/O through a file handle that is marked as being in text mode, \r\n is translated to \n upon reading and vice versa upon writing. (Also, \cZ is taken to mean end-of-file – surprise!) This is done at the C runtime library layer.
You need to binmode your socket to fix that.
You should also remove the /s and /m modifiers from your pattern: since you do not use the meta-characters whose behaviour they modify (. and the ^/$ pair, respectively), they do nothing – cargo cult.
Why did you add the /m? Are you trying to split on line? To do that with /m you need to use either ^ or $ in the regex:
my #lines = split /^/m, $big_string;
However, if you want to treat a big string as lines, just open a filehandle on a reference to the scalar:
open my $string_fh, '<', \ $big_string;
while( <$string_fh> ) {
... process a line
}