Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I escape an arbitrary string for use as a command line argument in Windows?

Tags:

shell

windows

cmd

I have a list of strings and I want to pass those strings as arguments in a single Windows command line call. For simple alphanumeric strings it suffices to just pass them verbatim:

> script.pl foo bar baz yes no
foo
bar
baz
yes
no

I understand that if an argument contains spaces or double-quotes, I need to backslash-escape the double-quotes and backslashes, and then double-quote the argument.

> script.pl foo bar baz "\"yes\"\\\"no\""
foo
bar
baz
"yes"\"no"

But when I try to pass an argument with literal percent signs, this happens:

> script.pl %PATH%
C:\Program
Files\PHP\;C:\spaceless\perl\bin\;C:\Program
Files\IBM\Java60\bin;
(...etc.)

Double quoting doesn't work:

> script.pl "%PATH%"
C:\Program Files\PHP\;C:\spaceless\perl\bin\;C:\Program Files\IBM\Java60\bin; (...etc.)

Nor does backslash-escaping (notice how the backslashes are present in the output):

> script.pl \%PATH\%
\%PATH\%

Also, the rules are inconsistent for backslash-escaping backslashes:

> script.pl "\\yes\\"
\\yes\
> script.pl "\yes\\"
\yes\
> script.pl "\yes\"
\yes"

Also, doubtless there are special characters in the Windows command line shell, much like there are in all shells. What, then, is the general procedure for safely escaping arbitrary command line arguments for use at the Windows command line?

The ideal answer will describe a function escape() which can be used in situations like the following (a Perl example):

$cmd = join " ", map { escape($_); } @args;

Here are some more example strings which should be safely escaped by this function (I know some of these look Unix-like, that's deliberate):

yes
no
child.exe
argument 1
Hello, world
Hello"world
\some\path with\spaces
C:\Program Files\
she said, "you had me at hello"
argument"2
\some\directory with\spaces\
"
\
\\
\\\
\\\\
\\\\\
"\
"\T
"\\T
!1
!A
"!\/'"
"Jeff's!"
$PATH
%PATH%
&
<>|&^
()%!^"<>&|
>\\.\nul
malicious argument"&whoami
*@$$A$@#?-_
like image 232
qntm Avatar asked Jun 21 '11 15:06

qntm


People also ask

How do I pass a command line argument in Windows?

For example, entering C:\abc.exe /W /F on a command line would run a program called abc.exe and pass two command line arguments to it: /W and /F. The abc.exe program would see those arguments and handle them internally.

How do I escape special characters in cmd?

The Windows command-line interpreter uses a caret character ( ^ ) to escape reserved characters that have special meanings (in particular: & , | , ( , ) , < , > , ^ ).

How do I escape from command line?

To close or exit the Windows command line window, also referred to as command or cmd mode or DOS mode, type exit and press Enter . The exit command can also be placed in a batch file. Alternatively, if the window is not fullscreen, you can click the X close button in the top-right corner of the window.

How do I use special characters in cmd?

In Windows, you can type any character you want by holding down the ALT key, typing a sequence of numbers, then releasing the ALT key.


2 Answers

Here is an msdn blogpost showing how. It however assumes that every command line program internally uses CommandLineToArgvW to parse it's command line (not a shabby assumption, since it's part of the Shell32 library).

Original link (may not work): http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx

Web archive link: https://web.archive.org/web/20190109172835/https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/

like image 151
Wood Avatar answered Sep 21 '22 13:09

Wood


To escape a command line argument, use the following:

sub escapeArg {
  my $arg = shift;

  # Sequence of backslashes followed by a double quote:
  # double up all the backslashes and escape the double quote
  $arg =~ s/(\\*)"/$1$1\\"/g;

  # Sequence of backslashes followed by the end of the arg,
  # which will become a double quote later:
  # double up all the backslashes
  $arg =~ s/(\\*)$/$1$1/;

  # All other backslashes do not need modifying

  # Double-quote the whole thing
  $arg = "\"".$arg."\"";

  # Escape shell metacharacters
  $arg =~ s/([()%!^"<>&|;, ])/\^$1/g;

  return $arg;
}

To escape the actual command line command, for example when invoking a command with a ridiculous name such as ()!&%PATH%^;, .exe (which is perfectly legal), use the following:

sub escapeCmd {
  my $arg = shift;

  # Escape shell metacharacters
  $arg =~ s/([()%!^"<>&|;, ])/\^$1/g;
  return $arg;
}

Note that using escapeArg() for the command will not work.

Sources:

  • http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
  • http://support.microsoft.com/kb/103368
like image 30
qntm Avatar answered Sep 18 '22 13:09

qntm