Как использовать вывод grep в качестве пути для CD?

9

Как я могу передать grepвывод в качестве аргумента cdкоманды?

Например:

[root@xxx xxx]# pip install django | grep '/usr.*'  
Requirement already satisfied (use --upgrade to upgrade): django in   /usr/lib64/python2.7/site-packages

Здесь /usr/lib64/python2.7/site-packagesвыделено, и я хочу передать эту строку cd.

micgeronimo
источник

Ответы:

16

Используйте подстановку команд Bash $(), вам также нужно -oс помощью grep выбрать только соответствующую часть:

cd "$(pip install django | grep -o '/usr.*')"

Обратите внимание, что, хотя в этом случае вы избежите неудачи, вы всегда должны заключать в подстановку команд двойные кавычки, чтобы оболочка не разбивала слова на пробелах (по умолчанию пробел, табуляция и символ новой строки зависят от IFSпеременной в случае bash).

heemayl
источник
9

В зависимости от того, что вы делаете и не знаете заранее о том, что pipбудет выводиться, вы можете выбрать grepчто-то иное, чем /usr.*.

Если вы знаете, что каталог начинается с/usr (и что он появляется в конце строки выходных данных pipи /usrне отображается нигде в строке перед именем каталога), то это хороший выбор; Ответ Heemayl говорит вам, как.

Если причина вы знаете , что начинается с /usrтом , что вы просто запустить команду и знать директорию , которую вы хотите изменить, я предлагаю простое решение , выполнив команду cd /usr/lib64/python2.7/site-packages. Это меньше печатать, даже если вы не используете завершение табуляции .

В противном случае вы можете выбрать другое регулярное выражение в зависимости от того, что вы знаете о анализируемом выводе. Все приведенные ниже альтернативы все еще предполагают, что имя каталога появляется в конце строки, но другие предположения различаются.

Если вы знаете, что имя каталога является абсолютным (т. Е. Начинается с a /), и /в строке перед именем каталога не отображается no , вы можете использовать то же регулярное выражение, что и в ответе heemayl, но /вместо /usr:

cd "$(pip install django | grep -o '/.*')"

Это соответствует /нулю или более ( *) любого символа ( .).

Если вы знаете, что имя каталога не содержит горизонтальных пробелов (без пробелов и табуляции) и отображается в конце строки , вы можете использовать:

cd "$(pip install django | grep -oP '[^\h]+$')"

Здесь я использовал Perl regexp ( -P), потому что \hаббревиатура (for [:blank:]) делает его проще для ввода и чтения, чем эквивалентный расширенный regexp ( -E). Это соответствует одному или нескольким ( +) любого символа в классе символов ( [ ]), который не является ( ^) пробелом или табуляцией ( \h).

Если вы знаете, что имени каталога непосредственно предшествует inгоризонтальный пробел (то есть с пробелами слева и справа, и что это единственное вхождение inв строке , вы можете использовать:

cd "$(pip install django | grep -oP '\hin\h+\K.+')"

При этом используется положительное обратное утверждение нулевой ширины ( \K) для сопоставления одного или нескольких символов ( .+), которые появляются после пробела или табуляции ( \h) in, и другого одного или нескольких пробелов или табуляции ( \h+), без фактического включения inи пробелов окружая его в матче. Проверочные утверждения являются особенностью регулярных выражений Perl.

Шаблон также сработал бы, но нам нужно искать только один пробел , независимо от того, сколько их присутствует. Напротив, мы должны сопоставить все пробелы после , иначе они не будут отброшены, и они будут сопоставлены как часть имени каталога.\h+in\h+\K.+inin\K

Если вы знаете, что имени каталога непосредственно предшествует последнее вхождение строки, за inкоторым следует горизонтальный пробел , вы можете использовать:

set +H
cd "$(pip install django | grep -oP '\hin\h+(?!.*\hin\h.*)\K.*')"
set -H

Там само положительное утверждение обратной ширины нулевой ширины содержит отрицательное предварительное утверждение нулевой ширины ( (?! )).

В !появляется таким образом, чтобы быть трудно избежать элегантно; метод, который я использовал, чтобы предотвратить запуск расширения истории оболочки перед передачей, grepзаключается в том, чтобы временно отключить расширение истории ( set +H) перед запуском команды и повторно включить его ( set -H) после этого. Если вы используете это в сценарии, а ваш сценарий не содержит этого set -H, вам не нужно этого делать, поскольку расширение истории включается автоматически, только когда оболочка работает в интерактивном режиме.

Наконец, обратите внимание, что ни один из них, ни ответ heemayl, на самом деле не передают вывод grepto cd(хотя вывод pipвсе еще передается grep). Вместо конвейеров подходящим инструментом для этой работы является подстановка команд .

Элия ​​Каган
источник
Конечно, если вы хотите получить техническую информацию , оболочка использует канал для чтения выходных данных команды.
Random832
@ Random832 Хороший вопрос - я немного отредактировал с учетом этого. Кстати, вы знаете, можно ли полагаться на использование труб "под капотом" в подстановке команд? Или это детали реализации, которые потенциально могут быть сделаны по-другому в другой, будущей версии bash? (Конечно, на практике я понимаю, что это лучший способ сделать это, и поэтому вряд ли он будет реализован по-другому.)
Элия ​​Каган,
1
Нет, это не гарантировано, теоретически оболочка может использовать именованные fifo или временные файлы, хотя для этого нет причин.
Random832
Вы уверены в расширении истории? Единственные символы, которые могут его заблокировать, это ` and '`, и вы должны использовать одинарные кавычки вокруг выражения grep.
Муру
1
@muru Да, он расширен. Я тоже не заметил, но узнал, когда проверял. Здесь !нет 'кавычек, так как 'сами кавычки цитируются. Сложность команды позволяет легко ошибочно предсказать ее эффект, но, очевидно, из-за порядка расширений ( !рано) она в итоге работает как x=foo; echo "'$x'"(-> 'foo'). Удаление внешних "кавычек приведет !к тому, что они будут 'заключены в кавычки и предотвратит расширение истории, но затем cd завершится неудачно, если в каталоге есть пробелы в имени.
Элия ​​Каган,