3

Как я могу заставить greg Cygwin работать должным образом в обычном 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

Я бы назвал это скорее обходным путем.

2 ответа2

4

Вы не используете оболочку Unix. Цитирование другое.

По какой-то причине вы думаете, что одинарные кавычки являются метасимволами в интерпретаторе команд Microsoft. Они не. Они не имеют особого значения. Кроме того, обратная косая черта также не имеет особого значения для интерпретатора команд Microsoft. Они не являются escape-символом для цитирования метасимволов. Это карета. Важны именно двойные кавычки: они заключают в кавычки такие метасимволы, как < , > и | остановить интерпретатор команд, распознающий их, и любые предшествующие им обратные слеши не имеют значения.

Таким образом, ваша командная строка разбивается следующим образом с выделенными в кавычках строками:

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). (Библиотеки поддержки времени выполнения большинства реализаций языка C и C++ для Win32 делают это за кулисами. Это все еще вызываемая программа, но не интерпретатор команд.)

Действительно, многие программы на Win32 C и C++, которые не используют библиотеки Cygwin, не обращаются с одиночными кавычками специально, равно как и сам интерпретатор команд. В основном они разбивают командный хвост на два слова:

  1. -o
  2. 'ProductVersion\.*".*"' foo.txt | grep -o [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+

Предыдущее - это то, что могла бы делать, например, программа, скомпилированная с помощью компилятора Microsoft C/C++ . По иронии судьбы, такие программы на C/C++ распознают обратную косую черту внутри строк в кавычках, хотя интерпретатор команд этого не делает. Таким образом, они думают, что их командный хвост выглядит следующим образом, с одной большой строкой в кавычках, оканчивающейся неправильно, вместо двух строк в кавычках:

 -o 'ProductVersion\".*\".*\"' foo.txt | grep -o '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'

Да, это командный сценарий написания кошмара. По сути, вы должны знать, каких соглашений придерживается ваша программа, чтобы решить, как процитировать вектор аргументов, который вы хотите передать ей. У Cygwin есть одно соглашение. Программы на C и C++, скомпилированные с помощью коммерческих компиляторов C и C++ для Win32, имеют другие. (В 1980-х и 1990-х Borland, Watcom и Microsoft не соглашались с лексингом командного хвоста в своих компиляторах C/C++ для DOS, и как следствие было очень тонкое различие в обработке символов обратной косой черты между программами.) Другие языки программирования делают что-то еще иным образом.

Вы знаете, что ваша команда grep является программой Cygwin, поэтому вам нужно создать командную строку, которая (a) интерпретатор команд будет правильно распознавать как две простые команды, объединенные в конвейер команд, и что затем (b) команда grep будет корректно разделить на слова с помощью алгоритма Cygwin. Вот один из способов:

grep -o 'ProductVersion^".*\^".*\^"' foo.txt | grep -o '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'
1

Для Cygwin's grep

Обходной путь заключается в том, что вы можете указать значение ASCII в Bash. " 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

ADDED

Если вы замените [0-9]+ на [0-9][0-9]* (то же самое), то вы можете использовать grep без -E .

Вы можете использовать grep -P а затем вы можете использовать \d для [0-9] , но вы должны использовать кавычки вокруг второй части. Или \\d .

На самом деле, вот отличное решение, которое полностью решает вашу первоначальную проблему .. Вам нужна только цитата вокруг проблемного бита. (Между прочим, я мог бы сделать регулярное выражение во второй половине более эффективным, используя оператор повторения, но это не относится к проблеме, с которой мы столкнулись в кавычках, на которые я обращаю внимание).

Это работает. Удаление одиночных кавычек из первого бита и использование \" чтобы сделать их буквальными кавычками. Это обходит ошибку двойных кавычек, нуждающихся в одинарных кавычках. (Странная ошибка, если в findstr Windows NT есть что-то подобное, хотя, без сомнения, без одинарных кавычек.)

grep -P во второй части позволяет нам использовать \d . Мы могли бы поставить кавычки вокруг регулярного выражения во второй половине. Или мы можем просто поставить кавычки вокруг '\d\ или мы можем сделать то же, что и я, и использовать \\d. (\d отдельно - ununesped и без кавычек, не будут совпадать, потому что он интерпретируется 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""

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .