bash:在额外条件下循环文件

时间:2019-04-04 20:29:30

标签: arrays bash loops conditional-statements

在工作目录中,有几个文件根据文件名的后缀分为几组。这是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循环(例如,考虑第四个组只有一个文件)。这是示例,但是无法正常工作

 # 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?

1 个答案:

答案 0 :(得分:3)

  1. 您可以使用-f测试来检查文件是否存在,如果不存在,可以检查break

    if [ ! -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

更新

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

执行foo[1-99]之类的操作不起作用,因为它并不意味着您可能会认为意味着什么。 []的内部可以包含任意数量的单个字符或字符范围。例如,[1-9a-nxyz]将匹配'1''9''a''n'的任何字符,或'x',{ {1}}或'y',但匹配'z''0''q'等。或者就此而言也不会匹配任何大写字母。

因此'r'不能解释为1-99范围内的数字,它可以解释为包含范围为' 1”到“ 9”,再加上单个字符“ 9”。因此,模式[1-99][1-9]是等效的,并且将仅匹配字符[1-99]'1'。后一个表达式中的第二个'9'是多余的。

但是,您仍然可以通过扩展的glob实现所需的功能,可以使用命令9启用它:

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 表达式是一个扩展的glob表达式,它由两部分组成:+([0-9])(在这时其含义应该很明显)和封闭的[0-9]

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

但是,您应该注意,这意味着它也与+([0-9])之类的东西匹配。如果您只对大于或等于1的数字感兴趣,则可以这样做(启用000000000):

extglob

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

您可能不希望在整个脚本中都启用f1,特别是如果您在脚本中的其他位置有任何正则glob表达式,而这些表达式可能会意外地解释为extglob表达式。要在完成操作后禁用extglob,请执行以下操作:

extglob

这里还有另一件事要注意。如果全局模式与 any 文件不匹配,那么它将被解释为原始字符串,并且保持不变。

例如:

shopt -u extglob

或更具体的说,假设第4种情况的文件数为零,例如没有包含> echo This_file_totally_does_not_exist* This_file_totally_does_not_exist* 的文件。在这种情况下,如果尝试使用包含NpXynWT_com_340K的glob,则会将整个glob作为文字字符串获取:

NpXynWT_com_340K

这显然不是您想要的,尤其是在脚本的中间,您尝试> shopt -s extglob > echo NpXynWT_com_340K_[1-9]*([0-9]) echo NpXynWT_com_340K_[1-9]*([0-9]) 匹配文件。幸运的是,您可以设置另一个选项,以使不匹配的glob扩展为空:

cat

> shopt -s nullglob > echo This_file_totally_does_not_exist* # prints nothing 一样,如果您将extglob保留为打开状态,则脚本中的其他地方可能会有意外的行为。