У меня есть два набора файлов в одном каталоге:
1.bam
2.bam
3.bam
и
1.txt
2.txt
3.txt
Мне нужно запустить команду, в которой 1.bam
и 1.txt
интерпретируются вместе и так далее..
Я попробовал перебрать индексы массива:
bam=(*.bam)
ids=(*.txt)
for i in ${#bam[@]}; do
f=${bam[i]}
e=${ids[i]}
samtools view -N $e $f
done
И параллельно:
parallel --dry-run 'samtools view -N {1} {2} > {2.}.fasta' ::: *txt ::: *bam
С --dry-run я вижу, что цикл повторяется 3 раза (3 набора файлов * 3 цикла); Мне нужен только один.
Любая помощь?
Просто.. Спасибо.
А как насчет параллельной команды? Вы видите что-то не так?
Просто передайте файлы .bam в GNU Parallel и получите соответствующий текстовый файл так же, как вы получаете выходной файл parallel 'samtools view -N {1.}.txt {1} > {1.}.fasta' ::: *.bam
Если некоторые файлы отсутствуют, наборы будут смещены.
Конечно, но насчет количества парных файлов я уверен.
Я голосую за закрытие этого вопроса, потому что на плакате не использовался shellcheck.net , как указано в теге bash.
Во-первых, ${#bam[@]}
сообщает количество элементов в bam
.
.
Как упоминал Джетчизель в комментариях, вам нужен "${!bam[@]}"
, который возвращает индексы.
$: touch {1..3}.{bam,txt}
$: ls
1.bam 1.txt 2.bam 2.txt 3.bam 3.txt
$: bam=(*.bam); ids=(*.txt)
$: echo "${bam[@]} ${ids[@]}"
1.bam 2.bam 3.bam 1.txt 2.txt 3.txt
$: for i in ${#bam[@]}; do f=${bam[i]}; e=${ids[i]}; echo "i='$i' f='$f' e='$e'"; done
i='3' f='' e=''
$: echo "#:'${#bam[@]}' !:'${!bam[@]}'"
#:'3' !:'0 1 2'
$: for i in ${!bam[@]}; do f=${bam[i]}; e=${ids[i]}; echo "i='$i' f='$f' e='$e'"; done
i='0' f='1.bam' e='1.txt'
i='1' f='2.bam' e='2.txt'
i='2' f='3.bam' e='3.txt'
Это предполагает, что массивы будут действительно параллельными. Я оставляю вам проверку ошибок, если вы не попросите более глубокого погружения в это.
Есть и другие способы подойти к этому. Если вы предполагаете использовать настоящие параллельные массивы, вы также можете вернуться к ним, выполнив итерацию одного и взламывая строки другого.
$: for b in ${bam[@]}; do echo "b='$b' t='${b%.bam}.txt'"; done
b='1.bam' t='1.txt'
b='2.bam' t='2.txt'
b='3.bam' t='3.txt'
Но вы можете явно интерполировать имена файлов с помощью расширения фигурных скобок -
$: for i in ${!bam[@]}; do echo "samtools view -N" $i.{bam,txt}; done
samtools view -N 0.bam 0.txt
samtools view -N 1.bam 1.txt
samtools view -N 2.bam 2.txt
Поэтому я бы сделал это примерно так:
for i in ${!bam[@]}
do samtools view -N $i.{bam,txt} > $i.fasta 2> $i.log &
done
По привычке я обычно цитирую эти переменные, но в данном случае это все целочисленные индексы, и цитирование {bam,txt}
нарушает их, поэтому вам придется исключить их из ненужных в противном случае (по обстоятельствам) кавычек, поэтому я просто оставил их. выключен по этому поводу.
"$i".{bam,txt}
цитирует ту часть, которую нужно процитировать.
Да, но это всего лишь цифра, лол
Все равно было бы неплохо иметь правильное цитирование для будущих посетителей, чей сценарий использования может немного отличаться.
100% согласен. Спасибо.
Что ж, спасибо за помощь.
Вот конвейерный способ сделать это:
printf "%s\n" *.{bam,txt} \
| sort \
| while IFS= read -r bamfile; IFS= read -r txtfile; do
echo "do something with '${bamfile}' and '${txtfile}'"
done
do something with '1.bam' and '1.txt'
do something with '2.bam' and '2.txt'
do something with '3.bam' and '3.txt'
Предполагается, что ни одно из ваших имен файлов не содержит символов новой строки. Однако этот конвейер может использовать нулевой разделитель вместо новой строки.
printf "%s\0" *.{bam,txt} \
| sort -z \
| while IFS= read -d '' -r bamfile; IFS= read -d '' -r txtfile; do
echo "do something with '${bamfile}' and '${txtfile}'"
done
Интересно, спасибо за это.
Перебрать индексы, например:
for i in "${!bam[@]}"