bash: зацикливание файлов с дополнительными условиями - PullRequest
2 голосов
/ 04 апреля 2019

В рабочем каталоге есть несколько файлов, сгруппированных в несколько групп на основе конечного суффикса имени файла.Вот пример для 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) как можно было бы добавить во внутреннюю (пока) Зациклите некоторые условия проверки (например, с помощью оператора 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?

1 Ответ

3 голосов
/ 04 апреля 2019
  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.

Обновление

Если вы хотите, чтобы ваше выражение 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 в последнем выражении является избыточным.

Однако вы все равно можете достичь желаемого с помощью расширенных глобусов, которые вы можете включить с помощью команды 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]) представляет собой расширенное глобальное выражение, состоящее из двух частей: [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, вы получите весь глобус в виде буквенной строки:

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

Это явно не то, что вам нужно, особенно в середине вашего скрипта, где вы пытаетесь cat сопоставить файлы. К счастью, есть еще одна опция, которую вы можете установить, чтобы несоответствующие глобусы расширялись до нуля:

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

Как и в случае extglob, в любом месте вашего сценария может возникнуть непреднамеренное поведение, если вы оставите nullglob включенным.

...