计算目录中具有名称特定字符串的文件?


12

我有以下文件:

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

我想计算snp名称中带有单词(区分大小写)的文件的数量。我尝试使用

grep -a 'snp' | wc -l   

但后来我意识到可以grep在文件中进行搜索。扫描文件名的正确命令是什么?


1
您是否尝试过在该站点中搜索“计数文件”?
don_crissti 2015年

Answers:


18

您是否要snp在文件名中搜索?那将是一个简单的shell glob(通配符),其用法如下:

ls -dq *snp* | wc -l

-q如果您的版本ls无法识别,请省略该标志。它处理包含“奇怪”字符(包括换行符)的文件名。


不确定是否可以使用ls其中包含特定文本的文件名进行检索。那行得通,谢谢。
露西亚(Lucia O)

@LuciaO重新阅读您的注释,不是ls与文件名匹配,而是外壳程序。ls查看与模式匹配的文件列表;它并没有看到这个模式本身。
roaima

2
请注意,如果返回的文件太多,则此方法可能不起作用。
丹尼斯·诺尔特

4

如果您安静地站在Unix&Linux的走廊上并仔细聆听,您会听到幽灵般的声音,可怜地哭着,“包含换行符的文件名呢?”

ls -d *snp* | wc -l

或者,等价地

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

将输出包含的所有文件名snp,每个文件名后跟一个换行符, 还包括文件名中的所有换行符,然后计算输出中的行数。如果有一个文件名为

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

然后该名称将写为

foosnp
bar.tsv

当然,这将被视为两行。

在至少某些情况下,有一些更好的选择:

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

它计算包含的行snp,因此上述foosnp(\n)bar.tsv示例仅计算一次。对此略有变化

ls -f | grep -c snp

以上两个命令的不同之处在于:

  • ls -f将包括文件名称开头.; 对printf … *不对,除非dotglob外壳选项设置。
  • printf是内置的shell;ls是一个外部命令。因此,ls可能会使用更多的资源。
  • 当shell处理a时*,它将对文件名进行排序; ls -f不对文件名排序。因此,ls可能会使用较少的资源。

但是它们有一个共同点:在包含换行符snp在换行符之前和之后都存在的文件名中,它们都将给出错误的结果。

另一个:

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

这将创建一个Shell数组变量,列出所有包含的文件名,snp然后报告数组中的元素数。文件名被视为字符串,而不是行,因此嵌入换行符不是问题。可以想象,如果目录很大,这种方法可能会出现问题,因为文件名列表必须保存在外壳内存中。

完后还有:

之前,当我们说时printf "%s\n" *snp*,该printf命令"%s\n"对扩展中的每个参数重复(重用)格式字符串一次*snp*。在此,我们对此进行了小的更改:

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

这将为"%.0s\n"扩展中的每个参数重复(重复使用)格式字符串一次*snp*。但是,"%.0s"意思是打印每个字符串的前零个字符-即什么也不打印。该printf命令将为snp名称中包含的每个文件仅输出换行符(即,空行)。然后wc -l将它们计数。同样,您可以.通过设置包含文件dotglob


1

抽象:

适用于名称为“奇数”(包括换行)的文件。

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

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

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

描述

因为一个简单的glob会匹配每个文件名snp的名称echo *snp*,在这种情况下,一个简单的名字就足够了,但是要真正表明只有三个文件匹配,我将使用:

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

剩下的唯一问题是对文件进行计数。是的,grep是一个常见的解决方案,是的,用新行数wc -l也是一个常见的解决方案。请注意,grep -c(计数)实际上是计算一个snp字符串被匹配的次数,并且,如果一个文件名的名称中有多个snp字符串,则该计数将是不正确的。

我们可以做得更好。

一种简单的解决方案是设置位置参数:

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

为了避免更改位置参数,我们可以将每个参数转换为一个字符并输出结果字符串的长度(对于大多数shell):

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

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

或者,在bash中,避免使用subshel​​l:

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

档案清单

文件列表(来自原始问题,其中一个带有换行符):

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' 

那将有一个文件,中间有一个换行符:

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

并测试glob扩展:

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

这将添加一个星号,如果不加引号,则将扩展到整个文件列表。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.