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

9

Vou começar dizendo que acho que esse problema é um pouco menos inocente do que parece.

O que preciso fazer: verifique se há uma pasta dentro da 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 começo do PATH

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

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

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

CENÁRIO 2: a pasta está no começo do PATH.

# 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 - eu preciso procurar um ponto-e-vírgula ou um início de linha com essa 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 fazer para esses dois cenários em um único comando?

    
por Sman865 11.11.2014 / 21:57

8 respostas

10

Se você estiver verificando o conteúdo da variável de ambiente PATH , em vez de procurar algo em um arquivo, então grep é a ferramenta errada. É mais fácil (e mais rápido e indiscutivelmente 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

Portável:

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

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

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

    
por 12.11.2014 / 01:31
8

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

Você precisa combinar o começo e o fim do caminho que está tentando encontrar se quiser evitar falsos positivos.

Corresponde à instância no começo:

$ 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(:|$)"

Nenhuma correspondência lá.

Compacto e elegante. Testado no Debian 7.

Espero que ajude.

    
por 11.11.2014 / 22:08
7

Se você não é casado com grep , pode usar awk e separar os registros em :

awk 'BEGIN {RS=":"} /^\/opt\/gnome$/'
    
por 11.11.2014 / 22:03
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), portanto, grep -x pode procurar por resultados exatos. Isso tem a desvantagem de precisar de um processo adicional para tr . E não funcionará quando um nome de pasta em PATH contiver caracteres de nova linha.

    
por 11.11.2014 / 22:41
2

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

grep -w "/opt/gnome"

satisfará 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
    
por 11.11.2014 / 22:08
0

Para selecionar /opt/gnome cercado por caracteres não pertencentes à palavra (novas linhas, : , / , etc), tente este:

grep '\B/opt/gnome'
    
por 11.11.2014 / 22:19
0

Você pode fazer isso de forma confiável e com pouco esforço em grep . Você pode tirar proveito de extensões que estão amplamente disponíveis e de entre as quais muitas soluções já foram oferecidas, mas mesmo com o regex básico é fácil, embora não seja intuitivamente assim à primeira vista.

Com regex básico - e assim com 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\)*$'

grep corresponderá ao início da linha como muitas ocorrências das subexpressões \(grouped\) , conforme necessário para encontrar o próximo delimitador e a correspondência explícita, e da cauda da correspondência até a cauda da linha o mesmo caminho. Se sua correspondência explícita não for correspondida explicitamente , ela falhará e não imprimirá nada.

E você pode fazer, por exemplo:

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

Veja você 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

OUTPUT

/opt/gnome
:/opt/gnome:
/home/bob/opt/gnome:/opt/gnome:/opt/gnome-beta
/opt/gnomenot::::/opt/gnome
    
por 12.11.2014 / 07:01
0

você notou um caso extremo ... você poderia evitá-lo forçando a aparição de um: no começo da linha:

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

ou se o caminho for exato, adicione também um no final para garantir que ele esteja limitado:

 echo ":${PATH}:" | grep ":/opt/gnome:"
    
por 13.11.2014 / 00:35