C #에서 명령 줄 인수 이스케이프
짧은 버전 :
그것은 따옴표로 인수를 마무리 탈출하기에 충분 \
하고 "
?
코드 버전
string[] args
ProcessInfo.Arguments를 사용하여 명령 줄 인수 를 다른 프로세스 에 전달하고 싶습니다 .
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = Application.ExecutablePath;
info.UseShellExecute = true;
info.Verb = "runas"; // Provides Run as Administrator
info.Arguments = EscapeCommandLineArguments(args);
Process.Start(info);
문제는 인수를 배열로 가져 와서 단일 문자열로 병합해야한다는 것입니다. 내 프로그램을 속이기 위해 인수를 만들 수 있습니다.
my.exe "C:\Documents and Settings\MyPath \" --kill-all-humans \" except fry"
이 답변 에 따르면 단일 인수를 이스케이프하기 위해 다음 함수를 만들었지 만 무언가를 놓쳤을 수도 있습니다.
private static string EscapeCommandLineArguments(string[] args)
{
string arguments = "";
foreach (string arg in args)
{
arguments += " \"" +
arg.Replace ("\\", "\\\\").Replace("\"", "\\\"") +
"\"";
}
return arguments;
}
이것으로 충분합니까 아니면 이것에 대한 프레임 워크 기능이 있습니까?
그래도 그것보다 더 복잡합니다!
나는 관련 문제 (모든 매개 변수 + 일부 추가 매개 변수를 사용하여 백 엔드를 호출하는 프런트 엔드 .exe 작성)가 있었으므로 사람들이 어떻게하는지 살펴보고 귀하의 질문에 부딪 혔습니다. 처음에는 모든 것이 당신이 제안한대로 잘하는 것 같았습니다 arg.Replace (@"\", @"\\").Replace(quote, @"\"+quote)
.
그러나 내가 arguments를 호출 할 때 c:\temp a\\b
, 이것은 c:\temp
and로 전달되어 a\\b
백엔드가 호출되는 결과를 가져옵니다 "c:\\temp" "a\\\\b"
.-이것은 잘못된 것입니다. 왜냐하면 그것은 두 개의 인수가 c:\\temp
있고 a\\\\b
-우리가 원하는 것이 아니기 때문입니다 ! 우리는 탈출에 지나치게 열광했습니다 (창은 유닉스가 아닙니다!).
그래서 나는 자세히 http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs.aspx를 읽고 실제로 그러한 경우가 처리되는 방법을 설명합니다. 백 슬래시는 이중 앞에서 만 이스케이프 처리됩니다. 인용문.
다중 \
이 처리 되는 방식에 비틀림이 있으며 설명은 잠시 동안 현기증을 남길 수 있습니다. 여기서 말한 이스케이프 제거 규칙을 다시 말하겠습니다 . N 의 하위 문자열이 \
있고 뒤에 "
. 이스케이프 취소 할 때, 우리는 그 문자열로 대체 INT (N / 2) \
과 IFF에 N이 홀수이고, 우리는 추가 "
끝에.
이러한 디코딩을위한 인코딩은 다음과 같습니다. 인수의 경우 0 개 이상의 각 하위 문자열을 찾은 \
다음 "
이를 두 번 다수로 교체 한 \
다음 \"
. 우리는 그렇게 할 수 있습니다 :
s = Regex.Replace(arg, @"(\\*)" + "\"", @"$1$1\" + "\"");
그게 다야...
추신. ... 아니 . 잠깐만 요-더 있습니다! :)
인코딩을 올바르게 수행했지만 모든 매개 변수를 큰 따옴표로 묶기 때문에 문제가 있습니다 (일부 공백이있는 경우). 경계 문제가 있습니다. 매개 변수가로 끝나는 경우 뒤에 \
추가하면 "
닫는 따옴표의 의미가 깨집니다. 예 c:\one\ two
를 구문 분석 c:\one\
하고 two
다음에 재 조립 될 것입니다 "c:\one\" "two"
그 것이다 나 (오)은 하나 개의 인수로 이해 c:\one" two
(나는, 내가 그것을 만드는 아니라고 시도). 따라서 추가로 필요한 것은 인수가 끝나는 지 확인 \
하고 만약 그렇다면 다음과 같이 끝에 백 슬래시 수를 두 배로 늘리는 것입니다.
s = "\"" + Regex.Replace(s, @"(\\+)$", @"$1$1") + "\"";
내 대답은 Nas Banov의 대답과 비슷했지만 필요한 경우에만 큰 따옴표를 원했습니다 .
불필요한 큰 따옴표 잘라 내기
내 코드는 매개 변수의 문자 제한에 가까워 질 때 중요한 * 중요한 큰 따옴표 를 항상 주위에 불필요하게 두는 것을 저장 합니다 .
/// <summary>
/// Encodes an argument for passing into a program
/// </summary>
/// <param name="original">The value that should be received by the program</param>
/// <returns>The value which needs to be passed to the program for the original value
/// to come through</returns>
public static string EncodeParameterArgument(string original)
{
if( string.IsNullOrEmpty(original))
return original;
string value = Regex.Replace(original, @"(\\*)" + "\"", @"$1\$0");
value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\"");
return value;
}
// This is an EDIT
// Note that this version does the same but handles new lines in the arugments
public static string EncodeParameterArgumentMultiLine(string original)
{
if (string.IsNullOrEmpty(original))
return original;
string value = Regex.Replace(original, @"(\\*)" + "\"", @"$1\$0");
value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\"", RegexOptions.Singleline);
return value;
}
설명
백 슬래시 와 큰 따옴표를 올바르게 이스케이프하려면 여러 개의 백 슬래시 와 작은 큰 따옴표 를 다음으로 대체하면 됩니다.
string value = Regex.Replace(original, @"(\\*)" + "\"", @"\$1$0");
원래 백 슬래시 + 1과 원래 큰 따옴표의 두 배 추가 . 즉, '\'+ originalbackslash + originalbackslashes + ' "'. $ 0에는 원래의 백 슬래시 와 원래의 큰 따옴표가 있기 때문에 $ 1 $ 0을 사용 했습니다.
value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\"");
이것은 공백을 포함하는 전체 줄과 만 일치 할 수 있습니다.
일치 하면 시작과 끝에 큰 따옴표 를 추가합니다 .
인수의 끝에 원래 백 슬래시 가 있었다면 인용되지 않았을 것입니다. 이제 끝에 큰 따옴표 가 있어야합니다. 따라서 중복되어 모두 따옴표를 붙이고 의도하지 않게 최종 큰 따옴표를 인용하는 것을 방지합니다.
첫 번째 섹션에 대해 최소 일치를 수행하므로 마지막. *? 마지막 백 슬래시와 일치하지 않습니다.
산출
따라서 이러한 입력은 다음 출력을 생성합니다.
여보세요
여보세요
\ 안녕하세요 \ 12 \ 3 \
\ 안녕하세요 \ 12 \ 3 \
안녕하세요 세계
"안녕하세요"
\"여보세요\"
\\"여보세요\\\"
\ "안녕 \\ 세계
"\\"안녕 \ 세계 "
\ "안녕하세요 \\\ 세상 \
"\\"안녕하세요 \\\ 세계 \\ "
안녕 세상 \\
"안녕하세요. \\\\"
나는 이것에도 문제가 있었다. args를 파싱하는 대신 원본 명령 줄 전체를 가져와 실행 파일을 잘라 냈습니다. 이것은 필요 / 사용되지 않더라도 호출에서 공백을 유지하는 추가적인 이점이있었습니다. 여전히 실행 파일에서 이스케이프를 추적해야하지만 args보다 쉬웠습니다.
var commandLine = Environment.CommandLine;
var argumentsString = "";
if(args.Length > 0)
{
// Re-escaping args to be the exact same as they were passed is hard and misses whitespace.
// Use the original command line and trim off the executable to get the args.
var argIndex = -1;
if(commandLine[0] == '"')
{
//Double-quotes mean we need to dig to find the closing double-quote.
var backslashPending = false;
var secondDoublequoteIndex = -1;
for(var i = 1; i < commandLine.Length; i++)
{
if(backslashPending)
{
backslashPending = false;
continue;
}
if(commandLine[i] == '\\')
{
backslashPending = true;
continue;
}
if(commandLine[i] == '"')
{
secondDoublequoteIndex = i + 1;
break;
}
}
argIndex = secondDoublequoteIndex;
}
else
{
// No double-quotes, so args begin after first whitespace.
argIndex = commandLine.IndexOf(" ", System.StringComparison.Ordinal);
}
if(argIndex != -1)
{
argumentsString = commandLine.Substring(argIndex + 1);
}
}
Console.WriteLine("argumentsString: " + argumentsString);
Everyone quotes 명령 줄 인수의 C ++ 함수를 잘못된 방식으로 이식했습니다 .
잘 작동하지만 cmd.exe
명령 줄을 다르게 해석 한다는 점에 유의해야합니다 . 만약 당신의 명령 줄이 당신의 명령 줄을 해석 할 것이라면 ( 그리고 만약 에 언급 된 기사의 원저자처럼) cmd.exe
쉘 메타 문자도 이스케이프해야합니다.
/// <summary>
/// This routine appends the given argument to a command line such that
/// CommandLineToArgvW will return the argument string unchanged. Arguments
/// in a command line should be separated by spaces; this function does
/// not add these spaces.
/// </summary>
/// <param name="argument">Supplies the argument to encode.</param>
/// <param name="force">
/// Supplies an indication of whether we should quote the argument even if it
/// does not contain any characters that would ordinarily require quoting.
/// </param>
private static string EncodeParameterArgument(string argument, bool force = false)
{
if (argument == null) throw new ArgumentNullException(nameof(argument));
// Unless we're told otherwise, don't quote unless we actually
// need to do so --- hopefully avoid problems if programs won't
// parse quotes properly
if (force == false
&& argument.Length > 0
&& argument.IndexOfAny(" \t\n\v\"".ToCharArray()) == -1)
{
return argument;
}
var quoted = new StringBuilder();
quoted.Append('"');
var numberBackslashes = 0;
foreach (var chr in argument)
{
switch (chr)
{
case '\\':
numberBackslashes++;
continue;
case '"':
// Escape all backslashes and the following
// double quotation mark.
quoted.Append('\\', numberBackslashes*2 + 1);
quoted.Append(chr);
break;
default:
// Backslashes aren't special here.
quoted.Append('\\', numberBackslashes);
quoted.Append(chr);
break;
}
numberBackslashes = 0;
}
// Escape all backslashes, but let the terminating
// double quotation mark we add below be interpreted
// as a metacharacter.
quoted.Append('\\', numberBackslashes*2);
quoted.Append('"');
return quoted.ToString();
}
명령 줄 인코딩 / 이스케이프와 관련된 대부분의 문제를 처리하는 작은 프로젝트를 GitHub에 게시했습니다.
https://github.com/ericpopivker/Command-Line-Encoder
있습니다 CommandLineEncoder.Utils.cs의 클래스뿐만 아니라 인코딩 / 디코딩 기능을 확인하는 단위 테스트는.
명령 줄에서 이스케이프 문자를 사용하는 방법을 보여주기 위해 작은 샘플을 작성했습니다.
public static string BuildCommandLineArgs(List<string> argsList)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (string arg in argsList)
{
sb.Append("\"\"" + arg.Replace("\"", @"\" + "\"") + "\"\" ");
}
if (sb.Length > 0)
{
sb = sb.Remove(sb.Length - 1, 1);
}
return sb.ToString();
}
다음은 테스트 방법입니다.
List<string> myArgs = new List<string>();
myArgs.Add("test\"123"); // test"123
myArgs.Add("test\"\"123\"\"234"); // test""123""234
myArgs.Add("test123\"\"\"234"); // test123"""234
string cmargs = BuildCommandLineArgs(myArgs);
// result: ""test\"123"" ""test\"\"123\"\"234"" ""test123\"\"\"234""
// when you pass this result to your app, you will get this args list:
// test"123
// test""123""234
// test123"""234
요점은 각 arg를 큰 따옴표 ( ""arg "")로 감싸고 arg 값 안의 모든 따옴표를 이스케이프 된 따옴표 (test \ "123)로 바꾸는 것입니다.
static string BuildCommandLineFromArgs(params string[] args)
{
if (args == null)
return null;
string result = "";
if (Environment.OSVersion.Platform == PlatformID.Unix
||
Environment.OSVersion.Platform == PlatformID.MacOSX)
{
foreach (string arg in args)
{
result += (result.Length > 0 ? " " : "")
+ arg
.Replace(@" ", @"\ ")
.Replace("\t", "\\\t")
.Replace(@"\", @"\\")
.Replace(@"""", @"\""")
.Replace(@"<", @"\<")
.Replace(@">", @"\>")
.Replace(@"|", @"\|")
.Replace(@"@", @"\@")
.Replace(@"&", @"\&");
}
}
else //Windows family
{
bool enclosedInApo, wasApo;
string subResult;
foreach (string arg in args)
{
enclosedInApo = arg.LastIndexOfAny(
new char[] { ' ', '\t', '|', '@', '^', '<', '>', '&'}) >= 0;
wasApo = enclosedInApo;
subResult = "";
for (int i = arg.Length - 1; i >= 0; i--)
{
switch (arg[i])
{
case '"':
subResult = @"\""" + subResult;
wasApo = true;
break;
case '\\':
subResult = (wasApo ? @"\\" : @"\") + subResult;
break;
default:
subResult = arg[i] + subResult;
wasApo = false;
break;
}
}
result += (result.Length > 0 ? " " : "")
+ (enclosedInApo ? "\"" + subResult + "\"" : subResult);
}
}
return result;
}
인수를 추가하는 것은 훌륭하지만 탈출하지는 않습니다. 이스케이프 시퀀스가 이동해야하는 메서드에 주석을 추가했습니다.
public static string ApplicationArguments()
{
List<string> args = Environment.GetCommandLineArgs().ToList();
args.RemoveAt(0); // remove executable
StringBuilder sb = new StringBuilder();
foreach (string s in args)
{
// todo: add escape double quotes here
sb.Append(string.Format("\"{0}\" ", s)); // wrap all args in quotes
}
return sb.ToString().Trim();
}
대체 접근법
If you're passing a complex object such as nested JSON and you have control over the system that's receiving the command line arguments, it's far easier to just encode the command line arg/s as base64 and then decode them from the receiving system.
See here: Encode/Decode String to/from Base64
Use Case: I needed to pass a JSON object that contained an XML string in one of the properties which was overly complicated to escape. This solved it.
참고URL : https://stackoverflow.com/questions/5510343/escape-command-line-arguments-in-c-sharp
'code' 카테고리의 다른 글
SQL Server 프로파일 러와 동등한 PostgreSQL이 있습니까? (0) | 2020.10.27 |
---|---|
컬렉션에 추가 한 다음 정렬하거나 정렬 된 컬렉션에 추가하는 것이 더 빠릅니까? (0) | 2020.10.27 |
Clojure 1.4에서 require 내에서 refer의 사용은 무엇입니까? (0) | 2020.10.27 |
IntelliJ IDEA에서 '기본 gradle 래퍼'의 버전을 변경하는 방법은 무엇입니까? (0) | 2020.10.27 |
얼마나 많은 소켓 연결이 가능합니까? (0) | 2020.10.27 |