Grep para padrão no início ou no meio de uma linha

9

Começarei dizendo que acho que esse problema é um pouco menos inocente do que parece.

O que eu preciso fazer: verifique se há uma pasta na variável de ambiente PATH. Pode ser no começo ou em algum lugar depois. Eu só preciso verificar se essa pasta está lá.

Exemplo do meu problema - vamos usar /opt/gnome.


CENÁRIO 1: a pasta não está no início do CAMINHO

# echo "$PATH"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

# echo "$PATH" | grep ":/opt/gnome"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

Observe que o grep precisa ser específico o suficiente para não ser capturado /var/opt/gnome. Daí o cólon.


CENÁRIO 2: a pasta está no início do CAMINHO.

# echo "$PATH"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

# echo "$PATH" | grep "^/opt/gnome"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

Esse é o meu problema - preciso procurar dois pontos ou um início de linha com esta pasta. O que eu gostaria de fazer é uma dessas duas expressões de colchetes:

# echo $PATH | grep "[^:]/opt/gnome"
# echo $PATH | grep "[:^]/opt/gnome"

MAS [^e [:têm seus próprios significados. Portanto, os dois comandos acima não funcionam.

Existe uma maneira que eu possa grep para esses dois cenários em um comando?

JamesL
fonte
Note que o comentário de Gilles sobre a resposta de Costas se aplica à questão, também: desde que você não está grepping para /opt/gnome:ou /opt/gnome$, você vai encontrar /opt/gnome-fooou /opt/gnome/bar.
Scott
@ Scott - Desde que você inclua na sua partida o espaço intermediário, você sempre poderá ancorar qualquer corda na cabeça e na cauda da linha sem essas complicações. Apenas comogrep '^\(any number of other matches:*:\)*my match\(:.*\)*$'
mikeserv

Respostas:

10

Se você estiver verificando o conteúdo da PATHvariável de ambiente, em vez de procurar algo em um arquivo, grepé a ferramenta errada. É mais fácil (e mais rápido e sem dúvida mais legível) fazê-lo no shell.

No bash, ksh e zsh:

if [[ :$PATH: = *:/opt/gnome:* ]]; then
 : # already there
else
  PATH=$PATH:/opt/gnome
fi

Portably:

case :$PATH: in
  *:/opt/gnome:*) :;; # already there
  *) PATH=$PATH:/opt/gnome;;
esac

Observe o uso de em :$PATH:vez de $PATH; dessa maneira, o componente está sempre cercado por dois pontos na string de pesquisa, mesmo que estivesse no início ou no final de $PATH.

Se você estiver pesquisando em uma linha de um arquivo, poderá usar o regexp estendido (por exemplo, exigindo grep -E) (^|:)/opt/gnome($|:)para corresponder, /opt/gnomemas apenas se for no início de uma linha ou após dois pontos, e somente se estiver no final de a linha ou seguido por dois pontos.

Gilles 'SO- parar de ser mau'
fonte
8

Você pode usar expressões regulares estendidas usando apenas o grep -E

Você deve corresponder ao início e ao final do caminho que está tentando encontrar se quiser evitar falsos positivos.

Corresponde à instância no início:

$ TEST=/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

Também corresponde à instância no meio:

$ TEST=/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

Evitando falsos positivos:

$ TEST="/home/bob/opt/gnome:/opt/gnome/somethingelse:/opt/gnome-beta"
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"

Não há correspondências lá.

Compacto e elegante. Testado no Debian 7.

Luis Antolín Cano
fonte
1
egrepé o uso obsoleto grep -E(fonte: man grep)
Anthon
Obrigado, funciona como um encanto! Eu não a escolhi como resposta, porque acho que a opção -w é um pouco mais simples. Ainda mais simples do que eu imaginava!
JamesL
3
Atenção. A -wopção tem alguns problemas. Somente dígitos, letras e sublinhado são considerados "palavras". Portanto, alguns caracteres incomuns, mas possíveis, farão com que falhe. Exemplo echo '/sbin:/usr/sbin:/var-/opt/gnome' | grep -w "/opt/gnome"e echo '/sbin:/usr/sbin:/var./opt/gnome' | grep -w "/opt/gnome". Aqueles entregam resultados errados.
Luis Antolín Cano
1
Você está no caminho certo, mas ainda existem falsos positivos: /opt/gnome/somethingelse.
Gilles 'SO- stop be evil'
1
Totalmente certo. Devemos nos preocupar explicitamente com o fim, não apenas com o começo. Eu acho que isso resolve os problemas echo "/home/bob/opt/gnome:/opt/gnome/somethingelse:/opt/gnome-beta" | grep -E "(:|^)/opt/gnome(:|$)". Editando resposta.
Luis Antolín Cano
7

Se você não estiver casado grep, poderá usar awke separar os registros em:

awk 'BEGIN {RS=":"} /^\/opt\/gnome$/'
jasonwryan
fonte
5

Você também pode usar

echo "$PATH" | tr ':' '\n' | grep -x "/opt/gnome"

que divide a variável de caminho em linhas separadas (uma por caminho), para grep -xprocurar resultados exatos. Obviamente, isso tem a desvantagem de que ele precisa de um processo adicional tr. E não funcionará quando o nome de uma pasta PATHcontiver caracteres de nova linha.

TBrandt
fonte
2

Eu não sei, é suficiente para responder, mas

grep -w "/opt/gnome"

irá satisfazer a sua necessidade.

echo '/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome' | grep -w "/opt/gnome" -o
/opt/gnome
echo '/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome' | grep -w "/opt/gnome" -o
/opt/gnome

mas

echo '/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome' | grep "/opt/gnome" -o
/opt/gnome
/opt/gnome
Costas
fonte
Isso funciona perfeitamente porque dois pontos são caracteres que não são palavras. Obrigado!
JamesL
@ Sman865 Há outra razão: porque /não faz parte da palavra, mas ré.
Costas
2
Atenção. Como eu disse no comentário da minha resposta. Existem caracteres legais para um nome de diretório que são caracteres que não são de palavra. Isso leva a resultados errados. Não é comum finalizar o nome de um diretório - mas isso pode acontecer.
Luis Antolín Cano
4
@ Sman865 Falsos positivos: /opt/gnome-beta, /home/bob/opt/gnome, ...
Gilles 'SO-estar parada mal'
Caso não está funcionando: grep -w /usr/local -o <<< /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games------/usr/local /usr/local /usr/local
pabouk
0

Para selecionar /opt/gnomecercado por caracteres não-palavra (novas linhas, :, /, etc) tente este:

grep '\B/opt/gnome'
jimmij
fonte
0

Você pode fazer isso de forma confiável e com pouco esforço grep. Você pode tirar proveito das extensões amplamente disponíveis e dentre as quais muitas soluções já foram oferecidas, mas mesmo com o regex básico isso é feito com facilidade, embora possa não ser intuitivo à primeira vista.

Com regex básico - e assim por diante grep- você sempre tem duas âncoras confiáveis ​​- a cabeça e a cauda da linha. Você pode ancorar uma correspondência para ambos, independentemente de sua localização na linha, como:

grep '^\(ignore case, delimiter\)*match\(delimiter, ignore case\)*$'

grepcorresponderá do início da linha ao número de ocorrências das \(grouped\)subexpressões necessárias para encontrar o próximo delimitador e, em seguida, a correspondência explícita, e do final da partida até o final da linha da mesma maneira. Se sua correspondência explícita não corresponder explicitamente, ela falhará e não imprimirá nada.

E assim você pode fazer, por exemplo:

grep '^\(.*:\)*/opt/gnome\(:.*\)*$'

Veja por si mesmo:

grep '^\(.*:\)*/opt/gnome\(:.*\)*$
' <<\INPUT
/opt/gnome-beta
/opt/gnome
/home/bob/opt/gnome
:/opt/gnome:
/home/bob/opt/gnome:/opt/gnome:/opt/gnome-beta
/opt-gnome-beta
/opt/gnomenot::::/opt/gnome
INPUT

RESULTADO

/opt/gnome
:/opt/gnome:
/home/bob/opt/gnome:/opt/gnome:/opt/gnome-beta
/opt/gnomenot::::/opt/gnome
mikeserv
fonte
0

você notou um caso extremo ... você pode evitá-lo forçando a aparição de a: no início da linha:

 echo ":$PATH" | grep ":/opt/gnome"

ou se o caminho for exato, adicione também um no final para garantir que seja delimitado:

 echo ":${PATH}:" | grep ":/opt/gnome:"
Olivier Dulac
fonte