Mam kilka plików i chcę znaleźć, który zawiera sekwencyjne linie zaczynające się od określonego ciągu.
Na przykład dla następującego pliku:
Aaaaaaaaaaaa
Baaaaaaaaaaa
Cxxxxxxxxx
Cyyyyyyyyy
Czzzzzzzzz
Abbbbbbbbbbb
Bbbbbbbbbbbb
Caaaaaa
Accccccccccc
Bccccccccccc
Cdddddd
Ceeeeee
Istnieje więcej niż jedna linia rozpoczynająca się od „C”, więc chcę, aby ten plik został znaleziony za pomocą polecenia.
Na przykład dla następującego pliku:
Aaaaaaaaaaaa
Baaaaaaaaaaa
Cxxxxxxxxx
Abbbbbbbbbbb
Bbbbbbbbbbbb
Caaaaaa
Accccccccccc
Bccccccccccc
Cdddddd
Zawsze jest jedna linia zaczynająca się od „C”, nie chcę tego pliku. Myślałem o użyciu a grep
lub a, sed
ale nie wiem dokładnie, jak to zrobić. Może za pomocą wyrażenia regularnego ^C.*$^C
lub czegoś takiego. Dowolny pomysł ?
C
drugim przykładzie zaczynają się dwie linie .C
?grep
wersji.Odpowiedzi:
Z
pcregrep
:POSIXly:
(choć oznacza to, że w pełni odczytuje wszystkie pliki z tymi
awk
implementacjami, które nie obsługująnextfile
).W wersjach GNU
grep
do 2.5.4:wydaje się działać, ale jest to przypadek i nie ma gwarancji, że zadziała.
Zanim zostało to naprawione w 2.6 (przez to zatwierdzenie ), GNU
grep
przeoczyło, że używana funkcja wyszukiwania pcre pasuje do całego aktualnie przetwarzanego buforagrep
, powodując różnego rodzaju zaskakujące zachowanie. Na przykład:pasuje do pliku zawierającego:
To by pasowało:
Ale to:
Lub:
nie zrobiłby tego (tak jak
1\n2\n
w dwóch przetwarzanych buforachgrep
).Zachowanie to zostało jednak udokumentowane:
Po tym, jak został naprawiony w wersji 2.6, dokumentacja nie została zmieniona (raz go tam zgłosiłem ).
źródło
exit
i-exec \;
zamiast nextfile?awk
na plik. Zrobisz to tylko wtedy,awk
gdy nie obsługujesznextfile
i masz dużą część plików, które są duże i mają pasujące linie na początku pliku.-z
z-P
. Nie ma\N
bez tego-P
, musisz go napisać,$'[\01-\011\013-\0377]'
który działałby tylko w lokalizacjach C (patrz thread.gmane.org/gmane.comp.gnu.grep.bugs/5187 )Z
awk
:Spowoduje to wydrukowanie zawartości pliku, jeśli są kolejne wiersze zaczynające się od
C
. Wyrażenie(p ~ /^C/ && $1 ~ /^C/)
zajrzy do kolejnych linii w pliku i oceni, czy pierwszy znak w obu jest zgodnyC
. W takim przypadku linia zostanie wydrukowana.W celu znalezienia wszystkich plików, które mają taki wzór, można uruchomić powyższą awk poprzez
find
komendy:W tym poleceniu
find
+exec
przejdzie przez każdy z plików i przeprowadzi podobneawk
filtrowanie na każdym pliku i wydrukuje jego nazwę,FILENAME
jeśli wyrażenie awk zostanie ocenione jako prawda. Aby uniknąćFILENAME
wielokrotnego drukowania pojedynczego pliku z wieloma dopasowaniami,exit
używana jest instrukcja (dzięki @terdon).źródło
C
flag
, tylkoexit
zamiast tego. W ten sposób nie musisz kontynuować przetwarzania plików po znalezieniu dopasowania.Jeszcze jedna opcja z GNU
sed
:W przypadku pojedynczego pliku:
(chociaż zgłosi również pliki, których nie może odczytać).
Dla
find
:Problemu z drukowaniem nieczytelnych plików można uniknąć, pisząc je:
źródło
sed -n '$q1;/^C/{n;/^C/q}'
?$q1
- zmusza sed do wyjścia z błędem, jeśli wzorzec nie zostanie znaleziony. Zakończy się również błędem, jeśli coś jest nie tak z plikiem (jest nieczytelne lub uszkodzone). Więc wyjdzie z 0 statusem wyjścia tylko w przypadku znalezienia wzoru i zostanie przekazany do drukowania. Część z/^C/{n;/^C/q
jest dość prosta. Jeśli znajdzie ciąg rozpoczynający się od C, odczyta następny wiersz, a jeśli zacznie także od C, wyjdzie ze stanu zerowego wyjścia.Zakładając, że twoje pliki są wystarczająco małe, aby można je było wczytać do pamięci:
Wyjaśnienie:
000
: ustawiony\n\n
jako separator rekordów, włącza tryb akapitowy, który będzie traktował akapity (oddzielone kolejnymi znakami nowej linii) jako pojedyncze linie.-ne
: zastosuj skrypt podany jako argument-e
do każdego wiersza plików wejściowych.$ARGV
: jest aktualnie przetwarzanym plikiem/^C[^\n]*\nC/
: dopasujC
na początku wiersza (zobacz opissm
modyfikatorów poniżej, dlaczego to działa tutaj), a następnie 0 lub więcej znaków innych niż nowa linia, nowa linia, a następnie kolejne C. Innymi słowy, znajdź kolejne linie zaczynające się odC
. *//sm
: te modyfikatory dopasowania są (jak udokumentowano [tutaj]):Możesz także zrobić coś brzydkiego jak:
Tutaj
perl
kod zastępuje znaki nowej linii,%%
więc zakładając, że nie masz%%
w pliku wejściowym (duży, jeśli oczywiście),grep
dopasuje kolejne wiersze zaczynające się odC
.źródło
ROZWIĄZANIE:
PRÓBNY:
Najpierw stworzymy bazę testową:
Powyżej tworzy 26 plików
/tmp
o nazwiefile1-26
. W każdym pliku znajduje się 27 lub 28 linii rozpoczynających się od liter,a-z
po których następuje reszta alfabetu. Co trzeci plik zawiera dwa kolejne wiersze, w których pierwszy znak jest duplikowany.PRÓBA:
A kiedy się zmieniam:
do:
Dostaję...
WYNIK:
Krótko mówiąc, rozwiązanie działa w ten sposób:
źródło
Ten skrypt używa
grep
i,cut
aby uzyskać numery pasujących pasujących linii, i sprawdza, czy są jakieś dwie kolejne liczby. Przyjmuje się, że plik jest poprawną nazwą pliku przekazaną jako pierwszy argument do skryptu:źródło