Problemas com expressões regulares no Bash: [^ negate] parece não funcionar

8

Quando executo ls /directory | grep '[^term]'no Bash, recebo uma listagem regular, como se o grepcomando fosse ignorado de alguma forma. Tentei a mesma coisa egrep, tentei usá-lo com aspas duplas e simples, mas sem resultados melhores. Quando tento ls /directory | grep '^[term], recebo todas as entradas que começam com term - como esperado.

Eu tentei esse comando em um editor online, onde posso testar meu regex e ele funcionou como deveria. Mas não no Bash. Por isso, funciona em uma simulação, mas não na vida real.

Eu trabalho no Crunchbang Linux 10. Espero que sejam informações suficientes e estou ansioso por todas as dicas, porque não executar em um nível tão básico e perder horas de tempo é realmente frustrante!

erch
fonte
Estou confuso por causa do negar no título. Deseja greplinhas começando com term. Ou você deseja grep para linhas que não contêm termo?
Bernhard
@ Bernhard: Eu quero uma lista sem o termo entre colchetes. Não precisa ser exatamente "termo"! Até onde eu entendi, [^ abc] significa que qualquer coisa que contenha a, b ou c ou qualquer combinação dela não deve estar na listagem.
erch

Respostas:

12

Tem certeza de que o que você quer está acontecendo? Quando você executa, ls /directory | grep '[^term]'você está essencialmente esperando por não as letras ter m. Isso significa que se um arquivo tiver outras letras em seu nome, ele ainda aparecerá na saída de ls. Tome o seguinte diretório, por exemplo:

$ ls
alpha  brave  bravo  charlie  delta

Agora, se eu executar ls |grep '^[brav]', recebo o seguinte:

$ ls |grep '^[brav]'
alpha
brave
bravo

Como você pode ver, não só recebi bravecomo bravotambém alphaporque a classe de personagem []receberá qualquer letra dessa lista.

Conseqüentemente, se eu executar ls |grep '[^brav]', vou obter todos os arquivos que não contêm os caracteres brav em qualquer lugar do nome.

$ ls |grep '[^brav]'
alpha
bravo
brave
charlie
delta

Se você notar, ela incluiu toda a lista de diretórios porque todos os arquivos tinham pelo menos uma letra que não foi incluída na classe de caracteres.

Então, como Kanvuanza disse, para grep pelo inverso de "termo", em oposição aos caracteres, t e r mvocê deve fazê-lo usando grep -v.

Por exemplo:

$ ls |grep -v 'brav'
alpha
charlie
delta

Além disso, se você não quiser usar os arquivos que possuem caracteres na classe grep -v '[term]'. Isso impedirá a exibição de arquivos com esses caracteres. (Resposta de Kanvuanza)

Por exemplo:

$ ls |grep -v '[brav]'

Como você pode ver, não havia arquivos listados porque todos os arquivos desse diretório incluíam pelo menos uma letra dessa classe.

Termo aditivo:

Eu queria acrescentar que, usando o PCRE, é possível usar apenas regex para filtrar usando expressões negadas. Para fazer isso você usaria algo conhecido como um negativo regex olhar em frente: (?!<regex>).

Portanto, usando o exemplo acima, você pode fazer algo assim para obter os resultados desejados sem usar grepsinalizadores.

$ ls | grep -P '^(?!brav)'
alpha
charlie
delta

Para desconstruir esse regex, ele primeiro corresponde no início de uma linha ^e, em seguida, procura por seqüências que não correspondem brava seguir. Apenas alpha, charliee deltajogo assim que aqueles são os únicos que são impressos.

prateek61
fonte
1
Isso significa que, se um arquivo tiver outras letras em seu nome, ele ainda aparecerá na saída de ls. Isso responde a algumas perguntas! :) Então, o melhor caminho para o momento parece ser a -vopção. Obrigado por seu apoio! Esta pergunta realmente arruinou minha tarde, onde sua resposta ilumina minha noite!
erch
+1 para negative look-ahead regex.
Abhishek Kashyap
3

Eu acho que a grep -vbandeira faz o que você quer. Na página do manual :

-v, --invert-match
    Invert the sense of matching, to select non-matching lines.

Você pode usar ls /directory | grep -v [term]para imprimir linhas não correspondentes.

Pedro Lacerda
fonte
Estou ciente dessa opção, mas estou errado ao supor que [^ xyz] é o oposto de [xyz] e deve funcionar em qualquer caso? Também quero evitar editar configurações em qualquer lugar em um nível tão básico. Usar uma opção de inversão e / ou editar configurações com certeza é uma boa maneira de contornar, mas, tanto quanto eu entendi, isso deve funcionar sem, fora da caixa.
erch
Acho que você está certo, essa é a notação comum para negação de classe (ou seja, [^abc]mas tenho certeza de que o grep não suporta negações de classe, exceto algumas padrão (por exemplo [[:^digits:]]).) O suporte do grep para negação é horrível !
Pedro Lacerda
O suporte ao Grep para negação é horrível! E estas são as dicas que são a verdadeira cereja do bolo. Eu tenho os mesmos problemas com o egrep e estou longe de usar [pelo menos para mim, aparentemente] comandos mais avançados no momento. Você pode sugerir um comando que forneça melhores resultados e menos dor de cabeça?
erch 31/03
@ cellar.dweller, grepo manuseio de classes de caracteres é ótimo. Significa apenas algo bem diferente do que você (entende) mal. [abc]significa uma das a, b, ou c; [^abc]significa qualquer coisa, exceto o acima. É um personagem.
vonbrand
@ cellar.dweller: Eu acho que seu maior problema é um mal-entendido sobre regex, especificamente classes de caracteres dentro de regex.
tink