Como usar a saída grep como um caminho para o cd?

9

Como posso canalizar a grepsaída como argumento para o cdcomando?

Por exemplo:

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

Aqui /usr/lib64/python2.7/site-packagesestá destacado e quero passar essa string para cd.

micgeronimo
fonte

Respostas:

16

Use a substituição de comando do Bash $(), você também precisará do -ogrep para selecionar apenas a parte correspondente:

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

Observe que, apesar de você se safar nesse caso, mas sempre deve colocar a substituição do comando entre aspas duplas, para que o shell não realize divisão de palavras nos espaços em branco (por padrão, espaço, tab e nova linha, depende da IFSvariável no caso de bash).

heemail
fonte
9

Dependendo do que você faz e não sabe de antemão o pipque produzirá, você pode optar greppor algo diferente /usr.*.

Se você souber que o diretório começa com/usr (e que ele aparece no final da linha de saída de pipe que /usrnão aparece em nenhum lugar da linha antes do nome do diretório), é uma ótima opção; A resposta de heemayl diz como.

Se o motivo pelo qual você sabe que começa /usré que você acabou de executar o comando e conhecer o diretório para o qual deseja alterar, sugiro a solução mais simples de executar o comando cd /usr/lib64/python2.7/site-packages. Isso é menos digitado, mesmo se você não usar o preenchimento de guias .

Caso contrário, você poderá escolher uma regexp diferente, dependendo do que sabe sobre a saída que está sendo analisada. Todas as alternativas abaixo ainda assumem que o nome do diretório aparece no final da linha, mas as outras suposições variam.

Se você sabe que o nome do diretório é absoluto (ou seja, começa com a /) e não /aparece na linha antes do nome do diretório , você pode usar o mesmo regexp que na resposta do heemayl, mas com em /vez de /usr:

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

Corresponde a um /seguido por zero ou mais ( *) de qualquer caractere ( .).

Se você sabe que o nome do diretório não contém espaços em branco horizontais (sem espaços ou tabulações) e aparece no final da linha , você pode usar:

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

Aqui eu usei um Perl regexp ( -P) porque a \habreviação (for [:blank:]) torna mais fácil digitar e ler do que um regexp estendido equivalente ( -E). Isso corresponde a um ou mais ( +) de qualquer caractere em uma classe de caracteres ( [ ]) que não seja ( ^) um espaço ou tabulação ( \h).

Se você souber que o nome do diretório é precedido imediatamente por um inespaço em branco horizontal (ou seja, preenchido à esquerda e à direita com espaços em branco), e que essa é a única ocorrência dessa inlinha , você pode usar:

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

Isso usa uma asserção de look-behind positiva de largura zero ( \K) para corresponder a um ou mais caracteres ( .+) que aparecem após um espaço ou tabulação ( \h) ine outro ou mais espaços ou tabulações ( \h+), sem incluir ine espaços em branco cercando-o na partida. As asserções gerais são um recurso das expressões regulares do Perl.

O padrão teria funcionado também, mas precisamos procurar apenas um espaço em branco antes , independentemente de quantos estejam presentes. Por outro lado, precisamos corresponder a todos os espaços em branco depois , ou eles não serão descartados e corresponderão como parte do nome do diretório.\h+in\h+\K.+inin\K

Se você souber que o nome do diretório é precedido imediatamente pela última ocorrência da linha inseguida por espaço em branco horizontal , você pode usar:

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

Lá, a própria afirmação positiva de largura zero contém uma afirmação negativa de largura zero ( (?! )).

O !aparece de maneira a ser difícil escapar com elegância; o método que eu usei para impedir que ele desencadeie a expansão do histórico do shell antes de ser transmitido grepé desativar temporariamente a expansão do histórico ( set +H) antes de executar o comando e reativá-lo ( set -H) posteriormente. Se você estiver usando isso em um script e seu script não contiver set -H, não será necessário fazer isso, pois a expansão do histórico será ativada automaticamente somente quando um shell for executado de forma interativa.

Por fim, observe que nada disso, nem a resposta de heemayl , estão na verdade canalizando a saída de greppara cd(embora a saída de pipainda esteja sendo canalizada grep). Em vez de tubos , a ferramenta apropriada para este trabalho é a substituição de comandos .

Eliah Kagan
fonte
Obviamente, se você quiser ser técnico , o shell usa um pipe internamente para ler a saída do comando.
usar o seguinte comando
@ Random832 Bom ponto - eu editei um pouco em consideração a isso. Btw, você sabe se o uso de tubos "sob o capô" na substituição de comando pode ser invocado? Ou é um detalhe de implementação que pode ser feito de maneira diferente em uma versão futura diferente do bash? (É claro que, na prática, eu entendo que é a melhor maneira de fazê-lo e, assim, é pouco provável a ser implementado de forma diferente.)
Elias Kagan
11
Não, não é garantido, em teoria, um shell pode usar um arquivo fifo ou temp nomeado, embora não haja motivo para isso.
usar o seguinte código
Você tem certeza da expansão do histórico? Os únicos caracteres que podem bloqueá-lo são ` and '' e você usa aspas simples na expressão grep.
Muru
11
@ muru Sim, está expandido. Eu também não tinha notado, mas descobri quando o testei. O !não é citado com '-quotes, como 'eles são citados. A complexidade do comando facilita a previsão errada de seus efeitos, mas aparentemente devido à ordem das expansões ( !é precoce), ele acaba funcionando como x=foo; echo "'$x'"(-> 'foo'). A remoção das "aspas externas faria com !que fossem 'citadas e impedisse a expansão do histórico, mas cd falharia se o diretório tivesse espaços em branco em seu nome.
Eliah Kagan 28/04