jhc*_*ark 8 windows powershell
我想使用Powershell命令行在awk/gawk中打印字符串文字(具体程序不重要).但是,我认为我误解了引用规则 - PowerShell显然删除了本机命令的单引号内的双引号,但在将它们传递给命令行开关时却没有.
这适用于bash:
bash$ awk 'BEGIN {print "hello"}'
hello <-- GOOD
Run Code Online (Sandbox Code Playgroud)
这适用于PowerShell - 但重要的是我不知道为什么需要转义:
PS> awk 'BEGIN {print \"hello\"}'
hello <-- GOOD
Run Code Online (Sandbox Code Playgroud)
这在PowerShell中不打印任何内容:
PS> awk 'BEGIN {print "hello"}'
<-- NOTHING IS BAD
Run Code Online (Sandbox Code Playgroud)
如果这确实是在PowerShell中执行此操作的唯一方法,那么我想了解解释原因的引用规则链.根据http://technet.microsoft.com/en-us/library/hh847740.aspx上的PowerShell引用规则,这不是必需的.
开始解决方案
下面的Duncan提供的妙语是,您应该将此功能添加到您的powershell配置文件中:
filter Run-Native($command) { $_ | & $command ($args -replace'(\\*)"','$1$1\"') }
Run Code Online (Sandbox Code Playgroud)
或者特别是对于awk:
filter awk { $_ | gawk.exe ($args -replace'(\\*)"','$1$1\"') }
Run Code Online (Sandbox Code Playgroud)
结束解决方案
引号正确传递给PowerShell的echo:
PS> echo '"hello"'
"hello" <-- GOOD
Run Code Online (Sandbox Code Playgroud)
但是当呼唤外部"本地"程序时,引号会消失:
PS> c:\cygwin\bin\echo.exe '"hello"'
hello <-- BAD, POWERSHELL REMOVED THE QUOTES
Run Code Online (Sandbox Code Playgroud)
这是一个更清晰的例子,如果您担心Cygwin可能与此有关:
echo @"
>>> // program guaranteed not to interfere with command line parsing
>>> public class Program
>>> {
>>> public static void Main(string[] args)
>>> {
>>> System.Console.WriteLine(args[0]);
>>> }
>>> }
>>> "@ > Program.cs
csc.exe Program.cs
.\Program.exe '"hello"'
hello <-- BAD, POWERSHELL REMOVED THE QUOTES
Run Code Online (Sandbox Code Playgroud)
DEPRECATED示例,用于传递给cmd,它执行自己的解析(请参阅下面的Etan的评论):
PS> cmd /c 'echo "hello"'
"hello" <-- GOOD
Run Code Online (Sandbox Code Playgroud)
DEPRECATED示例,用于传递给bash,它执行自己的解析(请参阅下面的Etan的评论):
PS> bash -c 'echo "hello"'
hello <-- BAD, WHERE DID THE QUOTES GO
Run Code Online (Sandbox Code Playgroud)
任何解决方案,更优雅的解决方案或解释?
这里的问题是,在解析命令行时,Windows标准C运行时会从参数中删除未转义的双引号.Powershell通过在参数周围加上双引号将参数传递给本机命令,但它不会转义参数中包含的任何双引号.
这是一个测试程序,它打印出使用C stdlib给出的参数,来自Windows的'raw'命令行,以及Windows命令行处理(它似乎与stdlib的行为相同):
C:\Temp> type t.c
#include <stdio.h>
#include <windows.h>
#include <ShellAPI.h>
int main(int argc,char **argv){
int i;
for(i=0; i < argc; i++) {
printf("Arg[%d]: %s\n", i, argv[i]);
}
LPWSTR *szArglist;
LPWSTR cmdLine = GetCommandLineW();
wprintf(L"Command Line: %s\n", cmdLine);
int nArgs;
szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
if( NULL == szArglist )
{
wprintf(L"CommandLineToArgvW failed\n");
return 0;
}
else for( i=0; i<nArgs; i++) printf("%d: %ws\n", i, szArglist[i]);
// Free memory allocated for CommandLineToArgvW arguments.
LocalFree(szArglist);
return 0;
}
C:\Temp>cl t.c "C:\Program Files (x86)\Windows Kits\8.1\lib\winv6.3\um\x86\shell32.lib"
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
t.c
Microsoft (R) Incremental Linker Version 12.00.21005.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:t.exe
t.obj
"C:\Program Files (x86)\Windows Kits\8.1\lib\winv6.3\um\x86\shell32.lib"
Run Code Online (Sandbox Code Playgroud)
运行此操作cmd我们可以看到所有未转义的引号都被剥离,并且当存在偶数个未转义的引号时,空格只是单独的参数:
C:\Temp>t "a"b" "\"escaped\""
Arg[0]: t
Arg[1]: ab "escaped"
Command Line: t "a"b" "\"escaped\""
0: t
1: ab "escaped"
C:\Temp>t "a"b c"d e"
Arg[0]: t
Arg[1]: ab
Arg[2]: cd e
Command Line: t "a"b c"d e"
0: t
1: ab
2: cd e
Run Code Online (Sandbox Code Playgroud)
Powershell的表现略有不同:
C:\Temp>powershell
Windows PowerShell
Copyright (C) 2012 Microsoft Corporation. All rights reserved.
C:\Temp> .\t 'a"b'
Arg[0]: C:\Temp\t.exe
Arg[1]: ab
Command Line: "C:\Temp\t.exe" a"b
0: C:\Temp\t.exe
1: ab
C:\Temp> $a = "string with `"double quotes`""
C:\Temp> $a
string with "double quotes"
C:\Temp> .\t $a nospaces
Arg[0]: C:\Temp\t.exe
Arg[1]: string with double
Arg[2]: quotes
Arg[3]: nospaces
Command Line: "C:\Temp\t.exe" "string with "double quotes"" nospaces
0: C:\Temp\t.exe
1: string with double
2: quotes
3: nospaces
Run Code Online (Sandbox Code Playgroud)
在Powershell中,任何包含空格的参数都用双引号括起来.即使没有任何空格,命令本身也会得到引号.其他参数即使包含双引号等标点符号也不引用,并且我认为这是一个错误 Powershell不会转义出现在参数中的任何双引号.
如果你想知道(我是),Powershell甚至懒得引用包含换行符的参数,但参数处理也不会将换行符视为空格:
C:\Temp> $a = @"
>> a
>> b
>> "@
>>
C:\Temp> .\t $a
Arg[0]: C:\Temp\t.exe
Arg[1]: a
b
Command Line: "C:\Temp\t.exe" a
b
0: C:\Temp\t.exe
1: a
b
Run Code Online (Sandbox Code Playgroud)
自从Powershell没有逃脱引号后,唯一的选择就是自己动手:
C:\Temp> .\t 'BEGIN {print "hello"}'.replace('"','\"')
Arg[0]: C:\Temp\t.exe
Arg[1]: BEGIN {print "hello"}
Command Line: "C:\Temp\t.exe" "BEGIN {print \"hello\"}"
0: C:\Temp\t.exe
1: BEGIN {print "hello"}
Run Code Online (Sandbox Code Playgroud)
为避免每次都这样做,您可以定义一个简单的函数:
C:\Temp> function run-native($command) { & $command $args.replace('\','\\').replace('"','\"') }
C:\Temp> run-native .\t 'BEGIN {print "hello"}' 'And "another"'
Arg[0]: C:\Temp\t.exe
Arg[1]: BEGIN {print "hello"}
Arg[2]: And "another"
Command Line: "C:\Temp\t.exe" "BEGIN {print \"hello\"}" "And \"another\""
0: C:\Temp\t.exe
1: BEGIN {print "hello"}
2: And "another"
Run Code Online (Sandbox Code Playgroud)
注意你必须转义反斜杠和双引号,否则这不起作用(这不起作用,请参阅下面的进一步编辑):
C:\Temp> run-native .\t 'BEGIN {print "hello"}' 'And \"another\"'
Arg[0]: C:\Temp\t.exe
Arg[1]: BEGIN {print "hello"}
Arg[2]: And \"another\"
Command Line: "C:\Temp\t.exe" "B EGIN {print \"hello\"}" "And \\\"another\\\""
0: C:\Temp\t.exe
1: BEGIN {print "hello"}
2: And \"another\"
Run Code Online (Sandbox Code Playgroud)
另一个编辑: Microsoft Universe中的反斜杠和引用处理甚至比我意识到的还要怪.最后,我不得不去阅读C stdlib源代码,了解它们如何解释反斜杠和引号:
/* Rules: 2N backslashes + " ==> N backslashes and begin/end quote
2N+1 backslashes + " ==> N backslashes + literal "
N backslashes ==> N backslashes */
Run Code Online (Sandbox Code Playgroud)
这意味着run-native应该是:
function run-native($command) { & $command ($args -replace'(\\*)"','$1$1\"') }
Run Code Online (Sandbox Code Playgroud)
并且所有反斜杠和引号都将在命令行处理中继续存在.或者,如果要运行特定命令:
filter awk() { $_ | awk.exe ($args -replace'(\\*)"','$1$1\"') }
Run Code Online (Sandbox Code Playgroud)
(更新后@jhclark的评论:它需要是一个过滤器,允许管道进入标准输入).
| 归档时间: |
|
| 查看次数: |
4266 次 |
| 最近记录: |