Policz wystąpienia znaku w ciągu znaków za pomocą funkcji Bash

124

Muszę policzyć liczbę wystąpień znaku w ciągu znaków za pomocą funkcji Bash.

W poniższym przykładzie, gdy char to (na przykład) t, echojest to poprawna liczba wystąpień tin var, ale gdy znak jest przecinkiem lub średnikiem, wypisuje zero:

var = "text,text,text,text" 
num = `expr match $var [,]`
echo "$num"
Jericob
źródło
unix.stackexchange.com/questions/18736/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

118

Użyłbym następującego awkpolecenia:

string="text,text,text,text"
char=","
awk -F"${char}" '{print NF-1}' <<< "${string}"

Dzielę ciąg według $chari wypisuję liczbę wynikowych pól minus 1.

Jeśli twoja powłoka nie obsługuje <<<operatora, użyj echo:

echo "${string}" | awk -F"${char}" '{print NF-1}'
hek2mgl
źródło
5
@HattrickNZ Następnie użyj:$(grep -o "$needle" < filename | wc -l)
hek2mgl
13
@Amir Czego oczekujesz?
hek2mgl
3
Możesz pominąć wc -l, po prostu użyj grep -c, działa zarówno na bsd grep, jak i linux grep.
andsens,
8
@andsens grep -cwyświetli tylko liczbę pasujących wierszy. Nie liczy wielu dopasowań w jednej linii.
hek2mgl
1
Chcę policzyć '$' w ciągu, jak mogę uciec '$' z głównego ciągu?
MasT
118

możesz na przykład usunąć wszystkie inne znaki i policzyć, co zostało, na przykład:

var="text,text,text,text"
res="${var//[^,]}"
echo "$res"
echo "${#res}"

wydrukuje

,,,
3

lub

tr -dc ',' <<<"$var" | awk '{ print length; }'

lub

tr -dc ',' <<<"$var" | wc -c    #works, but i don't like wc.. ;)

lub

awk -F, '{print NF-1}' <<<"$var"

lub

grep -o ',' <<<"$var" | grep -c .

lub

perl -nle 'print s/,//g' <<<"$var"
jm666
źródło
1
trochę więcej sztuczek, takich jaky="${x//[^s|S]}"; echo "${#y}"
Aquarius Power,
4
użyj pierwszego, należy zawsze unikać tworzenia kolejnego procesu, aby wykonać taką pracę, może to poważnie wpłynąć na wydajność podczas używania z dużymi pętlami iteracji. Z reguły zewnętrzne wykonywanie procesów powinno być ostatecznością podczas korzystania z operacji iteracyjnych lub powtarzających się.
osirisgothra
Dlaczego nie lubisz wc? To golf!
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
@CiroSantilli 六四 事件 法轮功 包 卓 轩 ponieważ na przykładecho -n some line | wc -l
jm666
Blok kodu 4 jest moim zdaniem najlepszy. Musimy ułatwić dotarcie do:tr -dc ',' <<<"$var" | wc -c
bgStack15
69

Możesz to zrobić, łącząc tri wcpolecenia. Na przykład, aby liczyć ew ciągu referee

echo "referee" | tr -cd 'e' | wc -c

wynik

4

Objaśnienia: Polecenie tr -cd 'e'usuwa wszystkie znaki inne niż „e”, a Polecenie wc -cliczy pozostałe znaki.

Wiele wierszy danych wejściowych jest również dobrych dla tego rozwiązania, tak jak polecenie cat mytext.txt | tr -cd 'e' | wc -cmoże liczyć ew pliku mytext.txt, nawet jeśli plik może zawierać wiele wierszy.

Robin Hsu
źródło
3
Twoje rozwiązanie wydaje się najczystsze i najłatwiejsze do zapamiętania, dzięki!
jirislav,
To jest świetne. Dziękuję Ci!
Kodie Grantham
Uwielbiam to, bo nienawidzę awk!
franzisk
3

Opierając się na świetnych odpowiedziach i komentarzach wszystkich, oto najkrótsza i najsłodsza wersja:

grep -o "$needle" <<< "$haystack" | wc -l

rmanna
źródło
2

awk działa dobrze, jeśli masz go na serwerze

var="text,text,text,text" 
num=$(echo "${var}" | awk -F, '{print NF-1}')
echo "${num}"
user2508516
źródło
Tak jak uwaga: awk -F,szuka pliku ,. Możesz wykonać następujące czynności:awk -F"${your_char}"
Emixam23
1

Sugerowałbym, co następuje:

var="any given string"
N=${#var}
G=${var//g/}
G=${#G}
(( G = N - G ))
echo "$G"

Żadnego wywołania innego programu

Mathew PV
źródło
1

też to sprawdź, na przykład chcemy liczyć t

echo "test" | awk -v RS='t' 'END{print NR-1}'

lub w python

python -c 'print "this is for test".count("t")'

lub nawet lepiej, możemy uczynić nasz skrypt dynamicznym za pomocą awk

echo 'test' | awk '{for (i=1 ; i<=NF ; i++) array[$i]++ } END{ for (char in array) print char,array[char]}' FS=""

w tym przypadku wynik wygląda tak:

e 1
s 1
t 2
Obywatel
źródło