Chcę usunąć wiersz z pliku, który zawiera określony znak tylko raz, jeśli jest obecny więcej niż jeden raz lub nie jest obecny, zachowaj ten wiersz w pliku.
Na przykład:
DTHGTY
FGTHDC
HYTRHD
HTCCYD
JUTDYC
Tu, postać którą chcesz usunąć to C
tak, komenda powinna usunąć linie FGTHDC
i JUTDYC
ponieważ mają C
dokładnie jeden raz.
W jaki sposób można to zrobić przy użyciu albo sed
czy awk
?
źródło
awk
separatora pól!awk 'BEGIN { print "FS={" FS"}","OFS={" OFS "}";} {printf "%d fields : ",NF; for (i=1;i<=NF;i++) {printf "{" $i "} ";}; print "" }'
i nakarmić go niektórymi wierszami, niektóre mające wiele szpiegów, a inne zaczynają się od spacji)podejście sed :
-i
opcja pozwala na modyfikację plików w miejscu/^[^C]*C[^C]*$/
- dopasowuje wiersze zawierająceC
tylko jeden razd
- usuń dopasowane linieźródło
Można to zrobić za pomocą
sed
:Kod:
Wyniki:
W jaki sposób?
C
via/C.*C/p
C
via/C/d
, dotyczy to również linii wydrukowanych już w kroku 1źródło
To usuwa linie z dokładnie jednym wystąpieniem C.
Wyrażenie regularne
[^C]
pasuje do jednego znaku, który nie jest C (lub znakiem nowej linii), a operator powtarzania (aka gwiazda Kleene)*
określa zero lub więcej powtórzeń poprzedniego wyrażenia.Domyślnym wyjściem
grep
(i większości innych narzędzi tekstowych) jest wyjście standardowe; przekieruj do nowego pliku i może przenieś go na oryginalny plik, jeśli tego chcesz. Tego samego wyrażenia regularnego można używaćsed -i
do edycji w miejscu:(Na niektórych platformach, zwłaszcza * BSD, w tym macOS,
-i
opcja wymaga argumentu, takiego jak-i ''
.)źródło
sed -i '/^[^C]*C[^C]*$/d' file
- brzmi tak, jakby był opublikowany wcześniej, jak myślisz, plagiat?grep
odpowiedzi, która oczywiście łatwo rozszerza się nased -i
wariant. Nie widziałem twojej odpowiedzi, ponieważ szukałem poprzednichgrep
odpowiedzi.-i
zesed
i zamiast przekierowywać do nowego pliku i zastąpić oryginał że jeślised
narzędzie wyszedł bez błędu.grep -vx '[^C]*C[^C]*'
grep
ponieważ jest jaśniejszy i bardziej niezawodny (w szczególnoścised
ma mniej informacyjny kod wyjścia).Narzędzie POSIX do skryptowych edycji pliku (zamiast drukowania zmodyfikowanej zawartości na standardowe wyjście) to
ex
.Oczywiście możesz go użyć,
sed -i
jeśli Twoja wersja Sed go obsługuje, ale pamiętaj, że nie jest przenośny, jeśli piszesz skrypt przeznaczony do uruchamiania na różnych typach systemów.David Foerster zapytał w komentarzach:
Odpowiedź: Tak
Dla
printf
kontraecho
jest to kwestia przenośności; zobacz Dlaczego printf jest lepszy niż echo? Łatwiej jest także przeplatać znaki nowej linii między poleceniamiprintf
.Dla
printf ... | ex
kontraex -c ...
jest to kwestia obsługi błędów. Dla tego konkretnego polecenia nie miałoby to znaczenia, ale ogólnie ma to znaczenie; na przykład spróbuj umieścićw skrypcie. Porównaj z następującymi:
Pierwszy zawiesza się i oczekuje na wejście; drugi zakończy działanie po otrzymaniu EOF przez
ex
polecenie, więc skrypt będzie kontynuowany. Istnieją alternatywne obejścia, takie jaks///e
, ale nie są one określone przez POSIX. Wolę używać przenośnego formularza, który pokazano powyżej.W przypadku
g
polecenia na końcu musi znajdować się nowa linia i wolę używaćprintf
zawijania poleceń zamiast osadzania nowej linii w pojedynczych cudzysłowach.źródło
printf
, a nieecho
czy coś takiegoex -c COMMAND
?printf
kontraecho
(chociaż zazwyczaj wolę,echo
gdy argument jest na stałe zakodowany), ale do tej pory nie używałem go zbyt częstoex
.Oto kilka opcji przy użyciu Perla.
Ponieważ dopasowujesz tylko jeden znak, możesz użyć
tr/C//
(tłumaczenie, bez zamienników), aby zwrócić liczbę dopasowańC
:Mówiąc bardziej ogólnie, jeśli chcesz dopasować ciąg znaków lub wyrażenie regularne, możesz użyć tego:
To przypisuje dopasowania wyrażenia regularnego
/C/g
do listy@m
i drukuje linie, gdy długość tej listy nie jest1
.-i
Przełącznik mogą być dodawane do edycji „w miejscu”.źródło
źródło
sed
,t #...
zwykle rozgałęzia się do etykiety wywoływanej#...
w większości innychsed
implementacji.!b
GNU sed, ponieważ gałąź nie lubi niczego oprócz etykiety lub nowego wiersza po nim.b
,t
,:
,}
(ir file
,w file
...) mogą nie mieć polecenia za nimi na tej samej linii. Możesz także użyć osobnych-e
opcji.g
modyfikator.Dla każdego, kto chce
awk
konkretnie, zaoferowałbympomiń linię, jeśli pasuje do wzoru, wydrukuj w przeciwnym razie. Tak naprawdę nie potrzebujesz
{print}
, możesz użyć//
domyślnego wydruku, ale myślę, że jest to wyraźniejsze.Moją pierwszą myślą było użycie
egrep -v
tego samego wzoru, ale tak naprawdę to nie odpowiada na postawione pytanie.źródło
{next}
? Po prostu powiedz,awk '/pattern/ {next} 1'
a wszystkie linie nie pasujące do wzoru zostaną wydrukowane. Lub, lepiej,awk '!/pattern/'
bezpośrednio je wydrukować.!/pattern/
(co jakoś wymknęło mi się z głowy), ale zdecydowanie wolałbym, aby było to zrozumiałe//{print}
niż tajemnicze1
. Zakładaj, że Twój kod zachowuje jak najmniej kompetencji i biegłości od następnej osoby, co nie oznacza, że jest on znacznie mniej wydajny lub efektywny.