我有以下文件:
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
snp
您的意思是要在文件中搜索名字?这将是一个简单的 shell glob(通配符),使用方式如下:
ls -dq *snp* | wc -l
-q
如果您的版本ls
无法识别该标志,请忽略该标志。它处理包含“奇怪”字符(包括换行符)的文件名。
答案2
如果你静静地站在 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
设置了 shell 选项,否则不会。 printf
是一个内置的 shell;ls
是一个外部命令。因此,ls
可能会使用稍多的资源。- 当 shell 处理 a 时
*
,它会对文件名进行排序;ls -f
不对文件名进行排序。因此,ls
使用的资源可能会稍微少一些。
但它们有一些共同点:当文件名包含换行符时,它们都会给出错误的结果并且snp
在换行符之前和之后都有。
其他:
filenamelist=(*snp*)
echo ${#filenamelist[@]}
这将创建一个 shell 数组变量,列出包含 的所有文件名snp
,然后报告数组中的元素数量。文件名被视为字符串,而不是行,因此嵌入的换行符不是问题。可以想象,如果目录很大,这种方法可能会出现问题,因为文件名列表必须保存在 shell 内存中。
完后还有:
早些时候,当我们说 时printf "%s\n" *snp*
,该命令为 扩展中的每个参数printf
重复(重用)一次格式字符串。在这里,我们做了一个小小的改变:"%s\n"
*snp*
printf "%.0s\n" *snp* | wc -l
这将为."%.0s\n"
扩展中的每个参数重复(重用)一次格式字符串*snp*
。但"%.0s"
意味着打印每个字符串的前零个字符——即什么也不打印。该命令将为名称中printf
包含的每个文件仅输出一个换行符(即空行) ;snp
然后wc -l
会计算它们。并且,您可以.
通过设置包含这些文件dotglob
。
答案3
抽象的:
适用于具有“奇数”名称(包括换行符)的文件。
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
(count) 实际上计算一个字符串匹配的次数snp
,并且,如果一个文件名的名称中包含多个snp
字符串,则计数将不正确。
我们可以做得更好。
一种简单的解决方案是设置位置参数:
$ set -- *snp*
$ echo "$#"
3
为了避免更改位置参数,我们可以将每个参数转换为一个字符并打印结果字符串的长度(对于大多数 shell):
$ printf 'x%.0s' *snp*
xxx
$ count=$(printf 'x%.0s' *snp*); echo "${#count}"
3
或者,在 bash 中,避免使用子 shell:
$ 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
并测试全局扩展:
$ touch $'foo * bar\tsnp baz.tsv'
这将添加一个星号,如果不加引号,它将扩展到整个文件列表。
答案4
假设您想计算 html 文件的数量:
ls | grep ".html" | wc -l
因此,如果您要计算“snp”的出现次数:
ls | grep "snp" | wc -l