在工作目录中,有几个文件根据文件名的结尾后缀分成几个组。以下是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工作流程
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?
-f
测试检查文件是否存在,如果不存在,则检查break
:
if [ ! -f "${pdbs}/${model}_${i}_FA.pdb" ]; then
break
fi
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
,但不匹配foo10
或foo99
。
做像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
,你的脚本中的其他地方可能会出现意想不到的行为。