Como nego um teste com expressões regulares em um script bash?

173

Usando o GNU bash (versão 4.0.35 (1) -release (x86_64-suse-linux-gnu)), eu gostaria de negar um teste com expressões regulares. Por exemplo, gostaria de adicionar condicionalmente um caminho à variável PATH, se o caminho ainda não estiver lá, como em:

TEMP=/mnt/silo/bin
if [[ ${PATH} =~ ${TEMP} ]] ; then PATH=$PATH; else PATH=$PATH:$TEMP; fi
TEMP=/mnt/silo/Scripts:
if [[ ${PATH} =~ ${TEMP} ]] ; then PATH=$PATH; else PATH=$PATH:$TEMP; fi
TEMP=/mnt/silo/local/bin
if [[ ${PATH} =~ ${TEMP} ]] ; then PATH=$PATH; else PATH=$PATH:$TEMP; fi
export PATH

Tenho certeza de que existem milhões de maneiras de fazer isso, mas o que eu gostaria de saber é se o condicional pode ser negado de alguma forma, como em (o incorreto):

TEMP=/mnt/silo/bin
if ![[ ${PATH} =~ ${TEMP} ]] ; then PATH=$PATH:$TEMP; fi
TEMP=/mnt/silo/Scripts:
if ![[ ${PATH} =~ ${TEMP} ]] ; then PATH=$PATH:$TEMP; fi
TEMP=/mnt/silo/local/bin
if ![[ ${PATH} =~ ${TEMP} ]] ; then PATH=$PATH:$TEMP; fi
export PATH
David Rogers
fonte

Respostas:

193

Você acertou, basta colocar um espaço entre o !e [[assim por dianteif ! [[

SiegeX
fonte
14
Oye vey! Apenas quando eu evito com segurança a loucura intergaláctica do perl por caracteres especiais, me vejo perdido no espaço do bash (posicionamento)! (Sinto medo apertando meu intestino como uma píton.) Obrigado!
David Rogers
126

Você também pode colocar o ponto de exclamação dentro dos colchetes:

if [[ ! $PATH =~ $temp ]]

mas você deve ancorar seu padrão para reduzir falsos positivos:

temp=/mnt/silo/bin
pattern="(^|:)${temp}(:|$)"
if [[ ! "${PATH}" =~ ${pattern} ]]

que procura uma correspondência no início ou no final com dois pontos antes ou depois dela (ou de ambos). Eu recomendo o uso de nomes de variáveis ​​em minúsculas ou mistas como hábito para reduzir a chance de colisões de nomes com variáveis ​​de shell.

Pausado até novo aviso.
fonte
Ah, obrigado pelo lembrete sobre ancoragem. A idéia de usar nomes de variáveis ​​minúsculas ou mistas é confusa para um iniciante do bash, pois o conselho que vi até agora é usar letras maiúsculas. Entendo o que você está dizendo, mas não vi exemplos suficientes de scripts do bash para me sentir confortável em desviar do (meu entendimento) do livro de receitas.
David Rogers
4
@anyoneis confia em nós neste. O uso de variáveis ​​maiúsculas definidas pelo usuário deve ser evitado. Todas as variáveis ​​no bash são expandidas e, $portanto, não há razão para colocá-las em maiúsculas para destacá-las.
SiegeX 01/01
Acho if [[ ! $foo =~ bar ]]mais seguro do que if ! [[ $foo =~ bar ]], porque facilita a introdução de mais condições para oif
CTodea 27/17/17
19

a maneira mais segura é colocar o! para a negação de regex dentro da [[ ]]seguinte maneira:

if [[ ! ${STR} =~ YOUR_REGEX ]]; then

caso contrário, poderá falhar em certos sistemas.

não é da sua conta
fonte
9

Sim, você pode negar o teste, como o SiegeX já apontou.

No entanto, você não deve usar expressões regulares para isso - pode falhar se o seu caminho contiver caracteres especiais. Tente isso:

[[ ":$PATH:" != *":$1:"* ]]

(Fonte)

Mark Byers
fonte
1
Outro motivo para usar este formulário é que ele não coincide acidentalmente com as substrings (por exemplo, falha ao adicionar "/ bin" ao caminho porque "/ usr / bin" já está lá).
Gordon Davisson
Demorei um pouco para entender como os dois pontos à esquerda me deram a ancoragem que eu queria. vale a pena lembrar a idéia de reduzir o padrão adicionando material à sequência a ser pesquisada. Não entendo por que caracteres especiais no caminho interromperiam a solução regex, mas não a solução do padrão bash. Você pode me dar um exemplo?
David Rogers
Eu não acho que isso funcione de maneira confiável em todos os casos. Correspondência de regex em IMHO superiores
Felipe Alvarez
5

Eu gosto de simplificar o código sem usar operadores condicionais nesses casos:

TEMP=/mnt/silo/bin
[[ ${PATH} =~ ${TEMP} ]] || PATH=$PATH:$TEMP
dimir
fonte