无法通过管道传输或重定向 Cygwin grep 输出

无法通过管道传输或重定向 Cygwin grep 输出

如何使 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然后可以使用\dfor [0-9],但第二部分必须用引号引起来。 或者\\d

事实上,这里有一个很好的解决方案,可以完全解决您原来的问题。您只需要在有问题的部分周围加一个引号即可。(顺便说一句,我可以使用重复运算符使后半部分的正则表达式更有效,但这与我关注的引号问题无关)。

这有效。从第一位删除单引号,并使用\"使其成为文字引号。这解决了双引号需要单引号的问题。(如果 Windows NTfindstr有类似的问题,这是一个奇怪的错误,尽管毫无疑问不是单引号。)

grep -P在第二部分中,允许我们使用\d。我们可以在后半部分将正则表达式放在引号中。或者,我们可以只将引号放在'\d\或周围,我们可以像我所做的那样使用\\d.(\d单独使用 -unescaped 和 unquoted,不会匹配,因为它会被 Bash 解释并简化为dgrep获取它时。)

$ 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命令知道单引号,并将从命令解释器接收到的命令尾分解为七个单词,该命令尾仍包含双引号(因为命令解释器可以识别它们,但不会删除它们):

  1. -o
  2. ProductVersion\".*\".*\"
  3. foo.txt
  4. |
  5. grep
  6. -o
  7. [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+

因此会出现有关这些文件的错误消息。但这是你的grep命令命令解释器不以单词的形式运行,Win32 程序只传递一个命令尾,不是参数向量就像在 Unix 范例中一样。如果被调用程序想要以 Unix(和 C 语言)方式运行,则将其命令尾拆分为单词由被调用程序决定。(大多数 Win32 C 和 C++ 语言实现的运行时支持库在后台执行此拆分。不过,执行此操作的仍然是被调用程序,而不是命令解释器。)

事实上,许多 Win32 C 和 C++ 程序不是使用 Cygwin 库不会对单引号进行特殊处理,命令解释器本身也是如此。它们最终会将命令尾部拆分为两个单词:

  1. -o
  2. '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]+'

相关内容