Grep dla wzoru na początku lub na środku linii

9

Zacznę od stwierdzenia, że ​​uważam, że ten problem jest nieco mniej niewinny niż się wydaje.

Co muszę zrobić: sprawdź folder w zmiennej środowiskowej PATH. Może być na początku lub gdzieś później. Muszę tylko sprawdzić, czy ten folder tam jest.

Przykład mojego problemu - użyjmy /opt/gnome.


SCENARIUSZ 1: folder nie znajduje się na początku ŚCIEŻKI

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

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

Zauważ, że grep musi być wystarczająco specyficzny, aby się nie wyłapał /var/opt/gnome. Stąd dwukropek.


SCENARIUSZ 2: folder znajduje się na początku ŚCIEŻKI.

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

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

To jest mój problem - muszę szukać dwukropka lub początku linii w tym folderze. Chciałbym zrobić jedno z tych dwóch wyrażeń w nawiasach:

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

ALE [^i [:mają swoje własne znaczenie. Dlatego dwa powyższe polecenia nie działają.

Czy istnieje sposób na grep dla tych dwóch scenariuszy za pomocą jednego polecenia?

JamesL
źródło
Zauważ, że komentarz Gillesa do odpowiedzi Costasa dotyczy również pytania: skoro nie masz ochoty na /opt/gnome:lub /opt/gnome$, znajdziesz /opt/gnome-foolub /opt/gnome/bar.
Scott,
@Scott - Dopóki umieścisz w meczu interweniującą przestrzeń, zawsze możesz zakotwiczyć dowolny sznur do głowy i ogona linii bez takich komplikacji. Podobnie jakgrep '^\(any number of other matches:*:\)*my match\(:.*\)*$'
mikeserv

Odpowiedzi:

10

Jeśli sprawdzasz zawartość PATHzmiennej środowiskowej, zamiast szukać czegoś w pliku, grepoznacza to, że jest to niewłaściwe narzędzie. Łatwiej (i szybciej i prawdopodobnie bardziej czytelnie) jest to zrobić w powłoce.

W bash, ksh i zsh:

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

Przenośny:

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

Zwróć uwagę na użycie :$PATH:zamiast $PATH; w ten sposób komponent jest zawsze otoczony dwukropkami w ciągu wyszukiwania, nawet jeśli był na początku lub na końcu $PATH.

Jeśli przeszukujesz wiersz pliku, możesz użyć rozszerzonego wyrażenia regularnego (tj. Wymagającego grep -E), (^|:)/opt/gnome($|:)aby dopasować, /opt/gnomeale tylko wtedy, gdy jest on na początku linii lub po dwukropku i tylko jeśli jest na końcu linia lub dwukropek.

Gilles „SO- przestań być zły”
źródło
8

Możesz użyć rozszerzonych wyrażeń regularnych, używając tylko grep -E

Jeśli chcesz uniknąć fałszywych trafień, musisz dopasować początek i koniec ścieżki, którą próbujesz znaleźć.

Na początku odpowiada instancji:

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

Dopasowuje również instancję na środku:

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

Unikanie fałszywych trafień:

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

Brak meczów.

Kompaktowy i elegancki. Testowane na Debianie 7.

Luis Antolín Cano
źródło
1
egrepjest przestarzała wykorzystanie grep -E(źródło: man grep)
Anthon
Dzięki, działa jak urok! Nie wybrałem go jednak jako odpowiedzi, ponieważ myślę, że opcja -w jest nieco prostsza. Jeszcze prostsze niż początkowo sobie wyobrażałem!
JamesL
3
Ostrzeżenie. Ta -wopcja ma pewne problemy. Tylko cyfry, litery i podkreślenie są uważane za „słowa”. Więc niektóre niezwykłe, ale możliwe znaki spowodują, że się nie powiedzie. Przykład echo '/sbin:/usr/sbin:/var-/opt/gnome' | grep -w "/opt/gnome"i echo '/sbin:/usr/sbin:/var./opt/gnome' | grep -w "/opt/gnome". Te dają złe wyniki.
Luis Antolín Cano
1
Jesteś na dobrej drodze, ale wciąż są fałszywe pozytywy: /opt/gnome/somethingelse.
Gilles 'SO - przestań być zły'
1
Całkowicie prawdziwe. Powinniśmy wyraźnie dbać o koniec, a nie tylko początek. Myślę, że to rozwiązuje problemy echo "/home/bob/opt/gnome:/opt/gnome/somethingelse:/opt/gnome-beta" | grep -E "(:|^)/opt/gnome(:|$)". Edycja odpowiedzi.
Luis Antolín Cano,
7

Jeśli nie jesteś ślubem grep, możesz użyć awki oddzielić rekordy:

awk 'BEGIN {RS=":"} /^\/opt\/gnome$/'
jasonwryan
źródło
5

Możesz także użyć

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

który dzieli zmienną ścieżki na osobne linie (po jednej na ścieżkę), dzięki czemu grep -xmożna szukać dokładnych wyników. Ma to oczywiście tę wadę, że wymaga dodatkowego procesu tr. I to nie będzie działać, gdy nazwa folderu w PATHzawiera znaki nowego wiersza.

TBrandt
źródło
2

Nie wiem, czy to wystarczy na odpowiedź, ale

grep -w "/opt/gnome"

zaspokoi twoją potrzebę.

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

ale

echo '/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome' | grep "/opt/gnome" -o
/opt/gnome
/opt/gnome
Costas
źródło
Działa to doskonale, ponieważ dwukropki są znakami niebędącymi słowami. Dzięki!
JamesL
@ Sman865 Istnieje inny powód: ponieważ /nie jest częścią słowa, ale rjest.
Costas
2
Ostrzeżenie. Jak powiedziałem w komentarzu do mojej odpowiedzi. W nazwie katalogu znajdują się znaki prawne, które nie są wyrazami. To prowadzi do błędnych wyników. Kończenie nazwy katalogu nie jest zwykle - ale może się zdarzyć.
Luis Antolín Cano,
4
@ Sman865 fałszywych alarmów: /opt/gnome-beta, /home/bob/opt/gnome...
Gilles 'SO- przestać być zła'
Sprawa nie działa: 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

Aby wybrać /opt/gnomeotoczony znaków spoza słowa (nowe linie :, /itp), spróbuj tego:

grep '\B/opt/gnome'
jimmij
źródło
0

Możesz to zrobić niezawodnie i przy niewielkim wysiłku grep. Możesz skorzystać z rozszerzeń, które są szeroko dostępne, spośród których wiele rozwiązań zostało już zaoferowanych, ale nawet w przypadku zwykłego wyrażenia regularnego można to łatwo zrobić, chociaż na pierwszy rzut oka może to nie być intuicyjne.

Z podstawowym wyrażeniem regularnym - i tak też jest grep- zawsze masz dwie niezawodne kotwice - głowę i ogon linii. Możesz zakotwiczyć dopasowanie do obu tych elementów, niezależnie od ich położenia na linii, np .:

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

grepdopasuje od początku wiersza tyle wystąpień \(grouped\)podwyrażeń, ile musi napotkać następny separator, a następnie wyraźne dopasowanie, i od końca dopasowania do końca linii w ten sam sposób. Jeśli twoje jawne dopasowanie nie zostanie wyraźnie dopasowane , nie powiedzie się i nic nie wydrukuje.

A więc możesz zrobić na przykład:

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

Sam zobacz:

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

WYNIK

/opt/gnome
:/opt/gnome:
/home/bob/opt/gnome:/opt/gnome:/opt/gnome-beta
/opt/gnomenot::::/opt/gnome
mikeserv
źródło
0

zauważyłeś przypadek na krawędzi ... można go uniknąć, wymuszając pojawienie się: na początku wiersza:

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

lub jeśli ścieżka jest dokładna, dodaj również jeden na końcu, aby upewnić się, że jest ograniczona:

 echo ":${PATH}:" | grep ":/opt/gnome:"
Olivier Dulac
źródło