Liczyć pliki w katalogu z określonym ciągiem w nazwie?

12

Mam następujące pliki:

Codigo-0275_tdim.matches.tsv  
Codigo-0275_tdim.snps.tsv  
FloragenexTdim_haplotypes_SNp3filter17_single.tsv  
FloragenexTdim_haplotypes_SNp3filter17.tsv  
FloragenexTdim_SNP3Filter17.fas  
S134_tdim.alleles.tsv    
S134_tdim.snps.tsv  
S134_tdim.tags.tsv

Chcę policzyć liczbę plików, których nazwy zawierają słowo snp(z rozróżnieniem wielkich i małych liter). Próbowałem użyć

grep -a 'snp' | wc -l   

ale potem zdałem sobie sprawę, że grepwyszukiwanie w plikach. Jakie jest prawidłowe polecenie do skanowania nazw plików?

Lucia O
źródło
1
Czy próbowałeś przeszukać tę witrynę pod kątem „zliczania plików”?
don_crissti

Odpowiedzi:

18

Czy masz na myśli wyszukiwanie snpw nazwach plików ? Byłby to prosty glob globu powłoki (symbol wieloznaczny), używany w następujący sposób:

ls -dq *snp* | wc -l

Pomiń -qflagę, jeśli Twoja wersja lsgo nie rozpoznaje. Obsługuje nazwy plików zawierające „dziwne” znaki (w tym znaki nowej linii).

roaima
źródło
Nie byłem pewien, czy mogę użyć lsdo odzyskania nazw plików z określonym tekstem. To zadziałało, dzięki.
Lucia O,
@LuciaO ponownie czyta twój komentarz, to nie lspasuje do nazw plików, to jest powłoka. lswidzi listę plików pasujących do wzorca; to jest nie widzieć samego wzorca.
roaima,
2
Uwaga: może to nie działać, jeśli zwracanych jest zbyt wiele plików.
Dennis Nolte
4

Jeśli stoisz cicho na korytarzach Uniksa i Linuksa i słuchasz uważnie, usłyszysz upiorny głos, żałośnie lamentujący: „A co z nazwami plików, które zawierają nowe linie?”

ls -d *snp* | wc -l

lub, równoważnie ,

printf "%s\n" *snp* | wc -l

wypisze wszystkie nazwy plików, które zawierają snp, po których następuje nowa linia, ale także uwzględni wszystkie nowe linie w nazwach plików , a następnie policzy liczbę linii w danych wyjściowych. Jeśli istnieje plik o nazwie

                                f o o s n p \n b a r . t s v

wtedy ta nazwa zostanie zapisana jako

foosnp
bar.tsv

które oczywiście będą liczone jako dwie linie.

Istnieje kilka alternatyw, które działają lepiej przynajmniej w niektórych przypadkach:

printf "%s\n" * | grep -c snp

która liczy linie, które zawierają snp, więc foosnp(\n)bar.tsvprzykład z powyższego liczy się tylko raz. Jest to niewielka zmiana

ls -f | grep -c snp

Dwa powyższe polecenia różnią się tym, że:

  • ls -fBędzie zawierać pliki, których nazwy zaczynają się .; printf … *nie, chyba że dotglobopcja powłoki jest ustawiona.
  • printfjest wbudowaną powłoką; lsto polecenie zewnętrzne. Dlatego lsmogą zużywać nieco więcej zasobów.
  • Kiedy powłoka przetwarza a *, sortuje nazwy plików; ls -fnie sortuje nazw plików. Dlatego lsmogą zużywać nieco mniej zasobów.

Ale mają coś wspólnego: oba dadzą złe wyniki w obecności nazw plików zawierających znak nowej linii, które mają snpzarówno przed, jak i po nowej linii .

Inne:

filenamelist=(*snp*)
echo ${#filenamelist[@]}

Spowoduje to utworzenie zmiennej tablicowej powłoki zawierającej wszystkie nazwy plików, które zawierają snp, a następnie zgłasza liczbę elementów w tablicy. Nazwy plików są traktowane jako ciągi znaków, a nie wiersze, więc osadzone znaki nowej linii nie stanowią problemu. Można sobie wyobrazić, że takie podejście może mieć problem, jeśli katalog jest ogromny, ponieważ lista nazw plików musi być przechowywana w pamięci powłoki.

Jeszcze inny:

Wcześniej, kiedy powiedzieliśmy printf "%s\n" *snp*, printfpolecenie powtórzyło (ponownie wykorzystało) "%s\n"ciąg formatu raz dla każdego argumentu w rozszerzeniu *snp*. Tutaj dokonujemy niewielkiej zmiany w tym:

printf "%.0s\n" *snp* | wc -l

Spowoduje to powtórzenie (ponowne użycie) ciągu "%.0s\n"formatu raz dla każdego argumentu w rozszerzeniu *snp*. Ale "%.0s"oznacza wydrukowanie pierwszych zerowych znaków każdego łańcucha - tzn. Nic. To printfpolecenie wyświetli tylko nowy wiersz (tj. Pusty wiersz) dla każdego pliku, który zawiera snpw nazwie; i wtedy wc -lje policzą. I znowu możesz dołączyć .pliki, ustawiając dotglob.

G-Man mówi „Przywróć Monikę”
źródło
1

Abstrakcyjny:

Działa dla plików o „nieparzystych” nazwach (w tym nowych wierszach).

set -- *snp* ; echo "$#"                             # change positional arguments

count=$(printf 'x%.0s' *snp*); echo "${#count}"      # most shells

printf -v count 'x%.0s' *snp*; echo "${#count}"      # bash

Opis

Ponieważ prosty glob pasuje do każdej nazwy pliku z snpnazwą, prosty echo *snp*może być wystarczający w tym przypadku, ale aby naprawdę pokazać, że będą tylko trzy pasujące pliki, których użyję:

$ ls -Q *snp*
"Codigo-0275_tdim.snps.tsv"  "foo * bar\tsnp baz.tsv"  "S134_tdim.snps.tsv"

Pozostaje tylko zliczyć pliki. Tak, grep jest zwykłym rozwiązaniem, i tak, liczenie nowych linii wc -ljest również zwykłym rozwiązaniem. Zauważ, że grep -c(liczba) naprawdę liczy, ile razy snpdopasowany jest ciąg, a jeśli jedna nazwa pliku ma więcej niż jeden snpciąg w nazwie, liczba będzie niepoprawna.

Możemy zrobić lepiej.

Jednym prostym rozwiązaniem jest ustawienie argumentów pozycyjnych:

$ set -- *snp*
$ echo "$#"
3

Aby uniknąć zmiany argumentów pozycyjnych, możemy przekształcić każdy argument na jeden znak i wydrukować długość wynikowego łańcucha (dla większości powłok):

$ printf 'x%.0s' *snp*
xxx

$ count=$(printf 'x%.0s' *snp*); echo "${#count}"
3

Lub, w skrócie, aby uniknąć podpowłoki:

$ printf -v count 'x%.0s' *snp*; echo "${#count}"
3

Lista plików

Lista plików (z pierwotnego pytania z jednym z dodanym znakiem nowej linii):

a='
Codigo-0275_tdim.matches.tsv
Codigo-0275_tdim.snps.tsv
FloragenexTdim_haplotypes_SNp3filter17_single.tsv
FloragenexTdim_haplotypes_SNp3filter17.tsv
FloragenexTdim_SNP3Filter17.fas
S134_tdim.alleles.tsv
S134_tdim.snps.tsv
S134_tdim.tags.tsv'
$ touch $a

touch $'foosnp\nbar.tsv' 

To będzie miało plik z jedną nową linią pośrodku:

f o o s n p \n b a r . t s v

Aby przetestować globalną ekspansję:

$ touch $'foo * bar\tsnp baz.tsv'

To doda gwiazdkę, która, jeśli nie będzie cytowana, rozwinie się do całej listy plików.

Izaak
źródło
-1

powiedzmy, że chcesz policzyć liczbę plików HTML:

ls | grep ".html" | wc -l

więc jeśli liczysz wystąpienia „snp”:

ls | grep "snp" | wc -l
Daniel McGrath
źródło