Jak zanegować test z wyrażeniami regularnymi w skrypcie bash?

173

Używając GNU bash (wersja 4.0.35 (1) -release (x86_64-suse-linux-gnu), chciałbym zanegować test za pomocą wyrażeń regularnych. Na przykład chciałbym warunkowo dodać ścieżkę do zmiennej PATH, jeśli ścieżki już tam nie ma, jak w:

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

Jestem pewien, że istnieje milion sposobów, aby to zrobić, ale chciałbym wiedzieć, czy warunek można w jakiś sposób zanegować, tak jak w (błędnym):

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
źródło

Odpowiedzi:

193

Miałeś rację, po prostu wstaw spację między !i tym [[podobneif ! [[

SiegeX
źródło
14
Oye vey! Kiedy bezpiecznie omijam międzygalaktyczne szaleństwo postaci specjalnych perla, gubię się w przestrzeni bash (umieszczenie)! (Czuję, że strach ściska mnie w brzuchu jak pyton.) Dzięki!
David Rogers,
126

Możesz również umieścić wykrzyknik w nawiasach:

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

ale powinieneś zakotwiczyć swój wzorzec, aby zredukować fałszywe alarmy:

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

który szuka dopasowania na początku lub na końcu dwukropkiem przed lub po nim (lub oba). Zalecam używanie nazw zmiennych pisanych małymi lub mieszanymi literami jako nawyku, aby zmniejszyć ryzyko kolizji nazw ze zmiennymi powłoki.

Wstrzymano do odwołania.
źródło
Ach, dzięki za przypomnienie o kotwiczeniu. Pomysł używania małych lub mieszanych nazw zmiennych jest mylący dla początkującego basha, ponieważ rada, którą widziałem do tej pory, to używanie wielkich liter. Rozumiem, o czym mówisz, ale nie widziałem wystarczającej liczby przykładów skryptów bash, aby odejść od (mojego zrozumienia) książki kucharskiej.
David Rogers,
4
@anyoneis zaufaj nam w tej sprawie. Należy unikać używania zmiennych definiowanych przez użytkownika zawierających duże litery. Wszystkie zmienne w bash są rozwijane za pomocą, $więc nie ma powodu, aby je pisać wielkimi literami, aby się wyróżniały.
SiegeX
Uważam, że jest if [[ ! $foo =~ bar ]]bezpieczniejsze niż if ! [[ $foo =~ bar ]], ponieważ ułatwia to wprowadzenie większej liczby warunków doif
CTodea
19

najbezpieczniej jest umieścić! dla negacji wyrażenia regularnego w ramach [[ ]]tego:

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

w przeciwnym razie może się nie powieść w niektórych systemach.

nie twój interes
źródło
9

Tak, możesz zanegować test jako SiegeX już wskazał .

Nie powinieneś jednak używać do tego wyrażeń regularnych - może się to nie powieść, jeśli twoja ścieżka zawiera znaki specjalne. Spróbuj tego zamiast tego:

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

(Źródło)

Mark Byers
źródło
1
Innym powodem używania tego formularza jest to, że nie będzie przypadkowo dopasowywać podciągów (np. Nie można dodać „/ bin” do ścieżki, ponieważ „/ usr / bin” już tam jest).
Gordon Davisson,
Zajęło mi trochę czasu, zanim zrozumiałem, w jaki sposób dwukropki po lewej stronie dały mi kotwicę, której chciałem. warto pamiętać o pomyśle zredukowania wzoru poprzez dodanie materiału do szukanego sznurka. Nie rozumiem, dlaczego znaki specjalne w ścieżce zakłócałyby rozwiązanie regex, ale nie rozwiązanie wzorców bash. Czy możesz podać mi przykład?
David Rogers,
Nie sądzę, aby to zadziałało niezawodnie we wszystkich przypadkach. Dopasowanie regex w lepszym IMHO
Felipe Alvarez
5

Lubię upraszczać kod bez używania operatorów warunkowych w takich przypadkach:

TEMP=/mnt/silo/bin
[[ ${PATH} =~ ${TEMP} ]] || PATH=$PATH:$TEMP
dimir
źródło