bash:使用额外条件循环遍历文件

问题描述 投票:2回答:1

在工作目录中,有几个文件根据文件名的结尾后缀分成几个组。以下是4组的示例:

# group 1 has 5 files
NpXynWT_apo_300K_1.pdb
NpXynWT_apo_300K_2.pdb
NpXynWT_apo_300K_3.pdb
NpXynWT_apo_300K_4.pdb
NpXynWT_apo_300K_5.pdb
# group 2 has two files
NpXynWT_apo_340K_1.pdb
NpXynWT_apo_340K_2.pdb
# group 3 has 4 files
NpXynWT_com_300K_1.pdb
NpXynWT_com_300K_2.pdb
NpXynWT_com_300K_3.pdb
NpXynWT_com_300K_4.pdb
# group 4 has 1 file
NpXynWT_com_340K_1.pdb

我写了一个简单的bash工作流程

  1. 列表项通过SED预处理每个文件:在每个文件中添加一些内容
  2. cat将属于同一组的预处理文件放在一起

这是我实现工作流的脚本,我创建了一个包含组名称的数组,并根据文件索引从1到5循环它

# list of 4 groups
systems=(NpXynWT_apo_300K NpXynWT_apo_340K NpXynWT_com_300K NpXynWT_com_340K)

 # loop over the groups
for model in "${systems[@]}"; do  
    # loop over the files inside of each group
    for i in {0001..0005}; do
    # edit file via SED
    sed -i "1 i\This is $i file of the group" "${pdbs}"/"${model}"_"$i"_FA.pdb
    done
# after editing cat the pre-processed filles
  cat "${pdbs}"/"${model}"_[1-5]_FA.pdb > "${output}/${model}.pdb"
done

改进这个脚本的问题:1)如何在内部(while)循环中添加一些检查条件(例如通过IF语句)来仅考虑现有文件?在我的示例中,脚本始终根据组中的一个(此处为第一组中的5个文件)中的最大数量循环5个文件(对于每个组)

for i in {0001..0005}; do

我宁愿循环给定组的所有现有文件,如果文件不存在则打破while循环(例如,考虑第4组只有1个文件)。这是一个例子,然而它不能正常工作

 # loop over the groups with the checking of the presence of the file
for model in "${systems[@]}"; do  
    i="0"
    # loop over the files inside of each group
    for i in {0001..9999}; do
    if [ ! -f "${pdbs}/${model}_00${i}_FA.pdb" ]; then
echo 'File '${pdbs}/${model}_00${i}_FA.pdb' does not exits!'
    break
    else
    # edit file via SED
    sed -i "1 i\This is $i file of the group" "${pdbs}"/"${model}"_00"$i"_FA.pdb
    i=$[$i+1]
    fi
    done
done

是否有可能从组中循环任意数量的现有文件(而不仅仅是限制给出例如非常大量的文件)

for i in {0001..9999}; do?
arrays bash loops condition
1个回答
3
投票
  1. 您可以使用-f测试检查文件是否存在,如果不存在,则检查breakif [ ! -f "${pdbs}/${model}_${i}_FA.pdb" ]; then break fi
  2. 您现有的cat命令已经只计算每个组中的现有文件,因为"${pdbs}"/"${model}"_[1-5]_FA.pdb bash在这里执行文件名扩展,而不是简单地将[1-5]扩展为所有可能的值。您可以在以下示例中看到此信息: > touch f1 f2 f5 # files f3 and f4 do not exist > echo f[1-5] f1 f2 f5 请注意,f[1-5]没有扩展到f1 f2 f3 f4 f5

更新:

如果您希望glob表达式匹配以大于9的数字结尾的文件,则[1-n]语法将不起作用。原因是[...]语法定义了匹配单个字符的模式。例如,表达式foo[1-9]将匹配文件foo1通过foo9,但不匹配foo10foo99

做像foo[1-99]这样的事情是行不通的,因为它并不代表你认为它意味着什么。 []的内部可以包含任意数量的单个字符或字符范围。例如,[1-9a-nxyz]将匹配任何来自'1''9''a''n'或任何角色'x''y''z'的角色,但它不匹配'0''q''r'等。或者就此而言,它会也不匹配任何大写字母。

所以[1-99]不被解释为1-99的数字范围,它被解释为由'1'到'9'的范围组成的字符集加上单个字符'9'。因此,模式[1-9][1-99]是等价的,只会匹配字符'1'通过'9'。后一种表达中的第二个9是多余的。

但是,您仍然可以使用扩展的globs实现所需的功能,您可以使用命令shopt -s extglob启用它:

> touch f1 f2 f5 f99 f100000 f129828523
> echo f[1-99999999999]       # Doesn't work like you want it to
f1 f2 f5
> shopt -s extglob
> echo f+([0-9])
f1 f2 f5 f99 f100000 f129828523

+([0-9])表达式是一个扩展的glob表达式,由两部分组成:[0-9],其含义在此时应该是显而易见的,以及封闭的+(...)

+(pattern)语法是extglob表达式,表示匹配pattern的一个或多个实例。在这种情况下,我们的模式是[0-9],所以extglob表达式+([0-9])匹配任何数字0-9的字符串。

但是,你应该注意到这意味着它也匹配像000000000这样的东西。如果您只对大于或等于1的数字感兴趣,则可以改为(启用extglob):

> echo f[1-9]*([0-9])

请注意这里的*(pattern)而不是+(pattern)*表示匹配零个或多个模式实例。我们想要的是因为我们已经将第一个数字与[1-9]匹配了。例如,f[1-9]+([0-9])与文件名f1不匹配。

您可能不希望在整个脚本中启用extglob,特别是如果您的脚本中有任何常规的glob表达式可能会被意外地解释为extglob表达式。要在完成后禁用extglob,请执行以下操作:

shopt -u extglob

这里还有另外一件重要的事情需要注意。如果glob模式与任何文件都不匹配,则将其解释为原始字符串,并保持不变。

例如:

> echo This_file_totally_does_not_exist*
This_file_totally_does_not_exist*

或者更重要的是,假设您的第四种情况中没有文件,例如没有包含NpXynWT_com_340K的文件。在这种情况下,如果您尝试使用包含NpXynWT_com_340K的glob,则将整个glob作为文字字符串:

> shopt -s extglob
> echo NpXynWT_com_340K_[1-9]*([0-9])
echo NpXynWT_com_340K_[1-9]*([0-9])

这显然不是你想要的,特别是在你尝试cat匹配文件的脚本中间。幸运的是,您可以设置另一个选项,使非匹配的globs扩展为空:

> shopt -s nullglob
> echo This_file_totally_does_not_exist*   # prints nothing

extglob一样,如果你打开nullglob,你的脚本中的其他地方可能会出现意想不到的行为。

© www.soinside.com 2019 - 2024. All rights reserved.