![无法通过管道传输或重定向 Cygwin grep 输出](https://linux22.com/image/1295834/%E6%97%A0%E6%B3%95%E9%80%9A%E8%BF%87%E7%AE%A1%E9%81%93%E4%BC%A0%E8%BE%93%E6%88%96%E9%87%8D%E5%AE%9A%E5%90%91%20Cygwin%20grep%20%E8%BE%93%E5%87%BA.png)
如何使 Cygwin 的 grep 在常规 cmd.exe 中正常工作?
> grep -o 'ProductVersion\".*\".*\"' foo.txt | grep -o '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'
foo.txt:ProductVersion" Value="59.59.140.59"
grep: |: No such file or directory
grep: grep: No such file or directory
grep: [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+: No such file or directory
和
> grep -o 'ProductVersion\".*\".*\"' foo.txt >> blah.txt
foo.txt:ProductVersion" Value="59.59.140.59"
grep: >>: No such file or directory
grep: blah.txt: No such file or directory
很乐意接受其他人的答案,但修改我的命令以不使用转义引号解决了我的问题。谢谢,@barlop。
在我的特定搜索中,我能够改变
grep -o 'ProductVersion\".*\".*\"' foo.txt >> blah.txt
到
grep -o 'ProductVersion.*Value.*' foo.txt | grep -v Name >> blah.txt
我认为这更像是一种解决方法。
答案1
对于 Cygwin 的 grep
一种解决方法是,您可以在 Bash 中指定 ASCII 值。"
十六进制为 22。
两点:您必须删除第一部分周围的单引号,这样它$'\x22'
才会被解释为特殊的,而不是文字的。
对于表达式的第二部分,您不能仅仅使用-o
,而必须是-oE
。
因为+
是 的一部分ERE
,没有-E
,它就是BRE
。它认为+
是字面意思。
证明+
是字面意思.. 55.55.55.55 不匹配,但是这会匹配:
$ echo 3+.3+.3+.3+ | grep -o [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+
3+.3+.3+.3+
这是您已调整过的线路。
使用 Bash 扩展 ASCII 代码的功能,而不是使用引号。删除第一部分周围的引号,并添加-E
到第二部分:
$ grep -o ProductVersion$'\x22'.*$'\x22'.*$'\x22' foo.txt | grep -oE [0-9]+\.[0
-9]+\.[0-9]+\.[0-9]+
59.59.140.59
添加
如果用 替换[0-9]+
([0-9][0-9]*
它们是相同的),那么您可以使用不带 的 grep -E
。
您可以使用grep -P
然后可以使用\d
for [0-9]
,但第二部分必须用引号引起来。 或者\\d
。
事实上,这里有一个很好的解决方案,可以完全解决您原来的问题。您只需要在有问题的部分周围加一个引号即可。(顺便说一句,我可以使用重复运算符使后半部分的正则表达式更有效,但这与我关注的引号问题无关)。
这有效。从第一位删除单引号,并使用\"
使其成为文字引号。这解决了双引号需要单引号的问题。(如果 Windows NTfindstr
有类似的问题,这是一个奇怪的错误,尽管毫无疑问不是单引号。)
grep -P
在第二部分中,允许我们使用\d
。我们可以在后半部分将正则表达式放在引号中。或者,我们可以只将引号放在'\d\
或周围,我们可以像我所做的那样使用\\d.
(\d
单独使用 -unescaped 和 unquoted,不会匹配,因为它会被 Bash 解释并简化为d
当grep
获取它时。)
$ grep -o ProductVersion\".*\".*\" foo.txt | grep -oP \\d+\.[0-9]+\.[0-9\]+\.[0
-9]+
59.59.140.59
现在我们已经处理了引号问题,我将使用重复运算符使其更高效。 的正则表达式3{4}
意味着3333
。 的正则表达式(fg){4}
意味着fgfgfgfg
。
$ grep -o ProductVersion\".*\".*\" foo.txt | grep -P '(\d.){4}'
ProductVersion" Value="59.59.140.59""
$ grep -o ProductVersion\".*\".*\" foo.txt | grep -P '('\\d.')'{4}
ProductVersion" Value="59.59.140.59""
$ grep -o ProductVersion'"'.*'"'.*'"' foo.txt | grep -P '('\\d.')'{4}
ProductVersion" Value="59.59.140.59""
答案2
您没有使用 Unix shell。引用有所不同。
出于某种原因,您认为单引号是 Microsoft 命令解释器中的元字符。它们不是。它们没有特殊意义。此外,反斜杠对 Microsoft 的命令解释器也没有特殊意义。它们是不是用于引用元字符的转义字符。即插入符号。重要的是双引号:它们引用诸如<
、和 之类的元字符>
,|
以阻止命令解释器识别它们,并且它们前面的任何反斜杠都是无关紧要的。
因此,您的命令行分解如下,并强调引用的字符串:
grep -o 'ProductVersion\".*\".*\"' foo.txt | grep -o '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'
如您所见,您认为的管道实际上是一个未正确终止的引号字符串,从第三个双引号开始一直延伸到行尾。您实际上正在运行只有一个 grep
命令,并在其命令尾的末尾为其提供整个带引号的字符串。您的grep
命令知道单引号,并将从命令解释器接收到的命令尾分解为七个单词,该命令尾仍包含双引号(因为命令解释器可以识别它们,但不会删除它们):
-o
ProductVersion\".*\".*\"
foo.txt
|
grep
-o
[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+
因此会出现有关这些文件的错误消息。但这是你的grep
命令命令解释器不以单词的形式运行,Win32 程序只传递一个命令尾,不是参数向量就像在 Unix 范例中一样。如果被调用程序想要以 Unix(和 C 语言)方式运行,则将其命令尾拆分为单词由被调用程序决定。(大多数 Win32 C 和 C++ 语言实现的运行时支持库在后台执行此拆分。不过,执行此操作的仍然是被调用程序,而不是命令解释器。)
事实上,许多 Win32 C 和 C++ 程序不是使用 Cygwin 库不会对单引号进行特殊处理,命令解释器本身也是如此。它们最终会将命令尾部拆分为两个单词:
-o
'ProductVersion\.*".*"' foo.txt | grep -o [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+
前面是使用微软的 C/C++ 编译器编译的程序会做什么例如。讽刺的是,这样的 C/C++ 程序会识别引号字符串中的反斜杠,即使命令解释器无法识别。因此,它们认为命令尾部看起来像这样,带有一个大的未正确终止的引号字符串,而不是两个引号字符串:
-o 'ProductVersion\".*\".*\"' foo.txt | grep -o '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'
是的,这是编写命令脚本的噩梦。本质上,您必须知道您正在运行的程序遵循哪些约定,以便决定如何引用您希望传递给它的参数向量。Cygwin 有一个约定。使用 Win32 的商业 C 和 C++ 编译器编译的 C 和 C++ 程序还有其他约定。(在 20 世纪 80 年代和 90 年代,Borland、Watcom 和 Microsoft 在其 DOS 的 C/C++ 编译器中对命令尾部词法分析存在分歧,因此程序之间在反斜杠字符处理方面存在非常细微的差异。)其他编程语言以其他微妙的方式执行操作。
您知道您的grep
命令是一个 Cygwin 程序,因此您需要构造一个命令行,以便 (a) 命令解释器能够正确识别为两个连接在命令管道中的简单命令,然后 (b) 该grep
命令能够使用 Cygwin 算法正确地拆分成单词。以下是一种方法:
grep -o 'ProductVersion^".*\^".*\^"' foo.txt | grep -o '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'