Jak używać wyjścia grep jako ścieżki do cd?

9

Jak mogę grepprzesyłać dane wyjściowe jako argument do cdpolecenia?

Na przykład:

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

Tutaj /usr/lib64/python2.7/site-packagesjest podświetlone i chcę przekazać ten ciąg cd.

micgeronimo
źródło

Odpowiedzi:

16

Użyj podstawienia poleceń Basha $(), potrzebujesz również -oz grep, aby wybrać tylko pasującą część:

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

Zauważ, że chociaż w tym przypadku uciekniesz, ale zawsze powinieneś zawrzeć podstawienie polecenia podwójnymi cudzysłowami, aby powłoka nie dokonywała podziału słów na białe spacje (domyślnie spacja, tabulator i nowa linia, zależą od IFSzmiennej w przypadku bash).

heemayl
źródło
9

W zależności od tego, co robisz i czego wcześniej nie wiesz o pipwynikach, możesz zdecydować się grepna coś innego niż /usr.*.

Jeśli wiesz, że katalog zaczyna się od/usr (i że pojawia się na końcu wiersza danych wyjściowych z pip, i że /usrnie pojawia się nigdzie w linii przed nazwą katalogu), to dobry wybór; Odpowiedź heemayla mówi ci jak.

Jeśli powodem wiesz, że zaczyna się /usrto, że wystarczy uruchomić komendę i poznać katalog, który chcesz zmienić, proponuję prostsze rozwiązanie uruchamiając komendę cd /usr/lib64/python2.7/site-packages. To mniej pisania, nawet jeśli nie korzystasz z uzupełniania tabulatorów .

W przeciwnym razie możesz wybrać inne wyrażenie regularne w zależności od tego, co wiesz o analizowanym wyjściu. Wszystkie poniższe opcje nadal zakładają, że nazwa katalogu pojawia się na końcu wiersza, ale inne założenia są różne.

Jeśli wiesz, że nazwa katalogu jest bezwzględna (tzn. Zaczyna się od a /) i nie /pojawia się w wierszu przed nazwą katalogu , możesz użyć tego samego wyrażenia regularnego, co w odpowiedzi heemayl, ale /zamiast /usr:

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

Odpowiada to /zeru lub więcej ( *) dowolnego znaku ( .).

Jeśli wiesz, że nazwa katalogu nie zawiera poziomych białych znaków (bez spacji i tabulatorów) i jest wyświetlana na końcu wiersza , możesz użyć:

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

Tutaj użyłem wyrażenia regularnego Perl ( -P), ponieważ \hskrót (for [:blank:]) ułatwia to pisanie i czytanie niż równoważne rozszerzone wyrażenie regularne ( -E). Dopasowuje jeden lub więcej ( +) dowolnego znaku w klasie znaków ( [ ]), który nie jest ( ^) spacją ani tabulatorem ( \h).

Jeśli wiesz, że nazwa katalogu jest bezpośrednio poprzedzona inotoczeniem poziomymi białymi spacjami (tj. Wypełnionymi po lewej i prawej stronie spacjami) i że jest to jedyne takie wystąpienie inna linii , możesz użyć:

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

Korzysta z asercji dodatniej zerowej szerokości ( \K), aby dopasować jeden lub więcej znaków ( .+) pojawiających się po spacji lub tabulatorze ( \h) inoraz inną jedną lub więcej spacji lub tabulatorów ( \h+), bez faktycznego uwzględnienia ini pustych spacji otaczając go w meczu. Asercje obejrzane są funkcją wyrażeń regularnych Perla.

Wzorzec również by zadziałał, ale musimy wcześniej szukać tylko jednego pustego miejsca , niezależnie od tego, ile jest obecnych. W przeciwieństwie do tego musimy dopasować wszystkie puste pola później , w przeciwnym razie nie zostaną odrzucone i zostaną dopasowane jako część nazwy katalogu.\h+in\h+\K.+inin\K

Jeśli wiesz, że nazwa katalogu jest poprzedzona ostatnim wystąpieniem linii, ina następnie poziomą białą spacją , możesz użyć:

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

Tam samo twierdzenie o dodatniej zerowej szerokości zawiera ujemne aserowanie wyprzedzające o zerowej szerokości ( (?! )).

Gdy !pojawia się w taki sposób, że trudno uniknąć elegancko; metoda, której użyłem, aby zapobiec wyzwalaniu ekspansji historii powłoki przed przekazaniem, grepto tymczasowe wyłączenie ekspansji historii ( set +H) przed uruchomieniem polecenia i ponowne włączenie go ( set -H) później. Jeśli używasz tego w skrypcie, a twój skrypt nie zawiera set -H, nie musisz tego robić, ponieważ rozszerzanie historii jest włączane automatycznie tylko wtedy, gdy powłoka działa interaktywnie.

Na koniec zauważ, że żadna z nich, ani odpowiedź heemayla , nie przesyłają potokowo danych wyjściowych grepdo cd(choć dane wyjściowe pipsą nadal przesyłane do grep). Zamiast rur , odpowiednim narzędziem do tego zadania jest zastępowanie poleceń .

Eliah Kagan
źródło
Oczywiście, jeśli chcesz uzyskać informacje techniczne , powłoka używa potoku wewnętrznie do odczytu danych wyjściowych polecenia.
Random832
@ Random832 Dobra uwaga - nieznacznie zredagowałem ten fakt. Przy okazji, czy wiesz, czy można polegać na stosowaniu rur „pod maską” w zastępowaniu poleceń? A może jest to szczegół implementacji, który może potencjalnie zostać wykonany inaczej w innej, przyszłej wersji bash? (Oczywiście w praktyce rozumiem, że jest to najlepszy sposób, aby to zrobić, a zatem jest mało prawdopodobne, aby został zaimplementowany inaczej.)
Eliah Kagan
1
Nie, nie jest to gwarantowane, teoretycznie powłoka mogłaby użyć nazwanego pliku fifo lub temp, chociaż nie ma powodu.
Random832
Czy jesteś pewien rozszerzenia historii? Jedynymi znakami, które mogą go zablokować, są ` and '', a wokół wyrażenia grep używasz pojedynczych cudzysłowów.
mur
1
@muru Tak, jest rozwinięty. Też tego nie zauważyłem, ale dowiedziałem się, kiedy to przetestowałem. Nie !ma tam cudzysłowu ', ponieważ 'same cytaty są cytowane. Złożoność polecenia ułatwia błędne przewidzenie jego efektu, ale najwyraźniej z powodu kolejności rozszerzeń ( !jest wczesny) ostatecznie działa jak x=foo; echo "'$x'"(-> 'foo'). Usunięcie zewnętrznych "cudzysłowów spowodowałoby, !że zostaną one 'cytowane i uniemożliwiłyby ekspansję historii, ale cd nie powiodłyby się, gdyby katalog zawierał spacje w nazwie.
Eliah Kagan