如何在 Bash 中按自然顺序循环文件?

发布于 2024-12-13 05:48:17 字数 296 浏览 4 评论 0原文

我使用以下命令循环遍历目录中的所有文件:

for i in *.fas; do some_code; done;

但是,我按此顺序

vvchr1.fas  
vvchr10.fas  
vvchr11.fas
vvchr2.fas
...

而不是

vvchr1.fas
vvchr2.fas
vvchr3.fas
...

自然顺序获取它们。

我尝试过排序命令,但无济于事。

I am looping over all the files in a directory with the following command:

for i in *.fas; do some_code; done;

However, I get them in this order

vvchr1.fas  
vvchr10.fas  
vvchr11.fas
vvchr2.fas
...

instead of

vvchr1.fas
vvchr2.fas
vvchr3.fas
...

what is natural order.

I have tried sort command, but to no avail.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

弥繁 2024-12-20 05:48:17
readarray -d '' entries < <(printf '%s\0' *.fas | sort -zV)
for entry in "${entries[@]}"; do
  # do something with $entry
done

其中 printf '%s\0' *.fas 生成一个 NUL 分隔的目录条目列表,扩展名为 .fas,并且 sort -zV按自然顺序对它们进行排序。

请注意,您需要安装 GNU sort 才能使其工作。

readarray -d '' entries < <(printf '%s\0' *.fas | sort -zV)
for entry in "${entries[@]}"; do
  # do something with $entry
done

where printf '%s\0' *.fas yields a NUL separated list of directory entries with the extension .fas, and sort -zV sorts them in natural order.

Note that you need GNU sort installed in order for this to work.

我很坚强 2024-12-20 05:48:17

使用选项sort -g,它根据一般数值

 for FILE in `ls ./raw/ | sort -g`; do echo "$FILE"; done

0.log 进行比较
1.日志
2.日志
...
10.日志
11.log

仅当文件名是数字时这才有效。如果它们是字符串,您将按字母顺序获得它们。例如:

 for FILE in `ls ./raw/* | sort -g`; do echo "$FILE"; done

raw/0.log
原始/10.log
原始/11.log
...
原始/2.log

With option sort -g it compares according to general numerical value

 for FILE in `ls ./raw/ | sort -g`; do echo "$FILE"; done

0.log
1.log
2.log
...
10.log
11.log

This will only work if the name of the files are numerical. If they are string you will get them in alphabetical order. E.g.:

 for FILE in `ls ./raw/* | sort -g`; do echo "$FILE"; done

raw/0.log
raw/10.log
raw/11.log
...
raw/2.log

余生再见 2024-12-20 05:48:17

您将获得按 ASCII 顺序排列的文件。这意味着 vvchr10* 位于 vvchr2* 之前。我意识到你无法重命名你的文件(我的生物信息学家的大脑告诉我它们包含染色体数据,而且我们根本不将 1 号染色体称为“chr01”),所以这是另一个解决方案(不使用 sort -V 我在我使用的任何操作系统上都找不到):

ls *.fas | sed 's/^\([^0-9]*\)\([0-9]*\)/\1 \2/' | sort -k2,2n | tr -d ' ' |
while read filename; do
  # do work with $filename
done

这有点复杂,不适用于包含空格的文件名。

另一个解决方案:假设我们想按大小顺序迭代文件,这可能更适合某些生物信息学任务:

du *.fas | sort -k2,2n |
while read filesize filename; do
  # do work with $filename
done

要反转排序,只需在 -k2 之后添加 r ,2n(获取-k2,2nr)。

You will get the files in ASCII order. This means that vvchr10* comes before vvchr2*. I realise that you can not rename your files (my bioinformatician brain tells me they contain chromosome data, and we simply don't call chromosome 1 "chr01"), so here's another solution (not using sort -V which I can't find on any operating system I'm using):

ls *.fas | sed 's/^\([^0-9]*\)\([0-9]*\)/\1 \2/' | sort -k2,2n | tr -d ' ' |
while read filename; do
  # do work with $filename
done

This is a bit convoluted and will not work with filenames containing spaces.

Another solution: Suppose we'd like to iterate over the files in size-order instead, which might be more appropriate for some bioinformatics tasks:

du *.fas | sort -k2,2n |
while read filesize filename; do
  # do work with $filename
done

To reverse the sorting, just add r after -k2,2n (to get -k2,2nr).

年少掌心 2024-12-20 05:48:17

您的意思是,列表中编号为 10 的文件位于编号为 3 的文件之前?那是因为 ls 对结果的排序非常简单,因此 something-10.whateversomething-3.whatever >。

一种解决方案是重命名所有文件,使它们具有相同的位数(其中包含个位数的文件以 0 开头)。

You mean that files with the number 10 comes before files with number 3 in your list? Thats because ls sorts its result very simple, so something-10.whatever is smaller than something-3.whatever.

One solution is to rename all files so they have the same number of digits (the files with single-digit in them start with 0 in the number).

素食主义者 2024-12-20 05:48:17
while IFS= read -r file ; do
    ls -l "$file" # or whatever
done < <(find . -name '*.fas' 2>/dev/null | sed -r -e 's/([0-9]+)/ \1/' | sort -k 2 -n | sed -e 's/ //;')

解决了问题,假设文件命名保持一致,不依赖于最新版本的 GNU sort,不依赖于读取 ls 的输出,并且不依赖不要成为管道到时间问题的受害者。

while IFS= read -r file ; do
    ls -l "$file" # or whatever
done < <(find . -name '*.fas' 2>/dev/null | sed -r -e 's/([0-9]+)/ \1/' | sort -k 2 -n | sed -e 's/ //;')

Solves the problem, presuming the file naming stays consistent, doesn't rely on very-recent versions of GNU sort, does not rely on reading the output of ls and doesn't fall victim to the pipe-to-while problems.

沙沙粒小 2024-12-20 05:48:17

就像@Kusalananda的解决方案(也许更容易记住?),但适合所有文件(?):

array=("$(ls |sed 's/[^0-9]*\([0-9]*\)\..*/\1 &/'| sort -n | sed 's/^[^ ]* //')")
for x in "${array[@]}";do echo "$x";done

本质上添加排序键,排序,删除排序键。

编辑:将评论移至适当的解决方案

Like @Kusalananda's solution (perhaps easier to remember?) but catering for all files(?):

array=("$(ls |sed 's/[^0-9]*\([0-9]*\)\..*/\1 &/'| sort -n | sed 's/^[^ ]* //')")
for x in "${array[@]}";do echo "$x";done

In essence add a sort key, sort, remove sort key.

EDIT: moved comment to appropriate solution

尛丟丟 2024-12-20 05:48:17

使用 sort -rh 和 while 循环

du -sh * | sort -rh | grep -P "avi$" |awk '{print $2}' | while read f; do fp=`pwd`/$f; echo $fp; done;

use sort -rh and the while loop

du -sh * | sort -rh | grep -P "avi$" |awk '{print $2}' | while read f; do fp=`pwd`/$f; echo $fp; done;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文