聊聊改名——哪个更优雅?
聊聊改名——哪个更优雅? by 单细胞天地
分享是一种态度
前段时间,生信技能树发了一个学徒作业:使用纯shell脚本完成24个fq文件的样品,与对应的6个病人样本的改名操作;详见:如何优雅的给单细胞转录组fastq文件改名
陆续收到两个投稿,以及我们尝试问了一下 chatGPT,下面就让我们看看三种方法哪种更优雅吧
读者投稿一
完整步骤代码
##step1
touch {A..X}_S1_L001_R1_001.fastq.gz {A..X}_S1_L001_R2_001.fastq.gz
##step2
ls *gz > filename;split -l 8 filename -d;ls x* |awk '{print "mv "$0,NR}'|sh
##step3
for i in {1..6};do (awk '$0~/R1/' ${i}|awk -v p=${i} '{split($0,a,"_");print "mv "$0,"P" p "_S" NR "_" a[3] "_" a[4] "_" a[5]}');done |sh
##step4
for i in {1..6};do (awk '$0~/R2/' ${i}|awk -v p=${i} '{split($0,a,"_");print "mv "$0,"P" p "_S" NR "_" a[3] "_" a[4] "_" a[5]}');done |sh
1、首先是创建文件,很好理解
touch {A..X}_S1_L001_R1_001.fastq.gz {A..X}_S1_L001_R2_001.fastq.gz
共48个文件
2、按一定规律划分文件
ls *gz > filename;split -l 8 filename -d;ls x* |awk '{print "mv "$0,NR}'|sh
代码解释:
ls *gz > filename;split -l 8 filename -d;ls x*
把48个文件名,分成6个文件,每个文件存储8个文件名,也即4个样本
可以看出,脚本的目的是把48个文件划分成存储了对应的6个病人的测序数据信息的独立文件。并按数字命名(方便后面循环)【注:其实也可以根据实际需求,改成按病例名来命名这个6个文件名,只是后面的循环也需要稍作改动】。
#把拆分出来的文件改为数字命名
ls *gz > filename;split -l 8 filename -d;ls x* |awk '{print "mv "$0,NR }'
### 命令输出结果
mv x00 1
mv x01 2
mv x02 3
mv x03 4
mv x04 5
mv x05 6
3、文件改名
for i in {1..6};do (awk '$0~/R1/' ${i}|awk -v p=${i} '{split($0,a,"_");print "mv "$0,"P" p "_S" NR "_" a[3] "_" a[4] "_" a[5]}');done |sh
for i in {1..6};do (awk '$0~/R2/' ${i}|awk -v p=${i} '{split($0,a,"_");print "mv "$0,"P" p "_S" NR "_" a[3] "_" a[4] "_" a[5]}');done |sh
代码解释:
for i in {1..6};do (awk '$0~/R1/' ${i});done
匹配出所有的R1
快速生成改名的命令
for i in {1..6};do (awk '$0~/R1/' ${i}|awk -v p=${i} '{split($0,a,"_");print "mv "$0,"P" p "_S" NR "_" a[3] "_" a[4] "_" a[5]}');done
-v
自定义变量p
split($0,a,"_"
split 以下划线分割拆分 $0
并赋值为变量a
;a[3]
变量取值。
亮点
脚本中
以要合并的样本单独作为一个文件 然后以行号来赋值S的递增很巧妙。
读者投稿二
完整步骤代码
##step1
touch {A..X}_S1_L001_R1_001.fastq.gz {A..X}_S1_L001_R2_001.fastq.gz
##step2
ls *.fastq.gz|sed 's/\x1B\[[0-9;]*[a-zA-Z]//g'|sed 's/\.fastq.gz//'|awk '{print NR" "$0}' >raw.txt
##step3
parallel -j 1 echo {1}{2}_{3}{4}_{5} ::: p ::: 1 2 3 4 5 6 ::: S ::: 1 2 3 4 ::: L001_R1_001 L001_R2_001 |awk '{print NR " " $0}' > new.txt
##step4
join raw.txt new.txt >name.txt
##step5
cat name.txt |cut -d " " -f 2,3|while read id; do name=($id); raw=${name[0]}; new=${name[1]}; echo $raw $new ; mv ${raw}\.fastq.gz ${new}\.fastq.gz; done
## 或者更简洁一点的写法
cat name.txt |cut -d " " -f 2,3|while read raw new; do echo $raw $new ; mv ${raw}\.fastq.gz ${new}\.fastq.gz; done
st1 首先建立模拟文件,共48个
# 建立模拟数据,6个病人,每个4个,分R1和R2,共48个文件
touch {A..X}_S1_L001_R1_001.fastq.gz {A..X}_S1_L001_R2_001.fastq.gz
st2 获取原始文件名
取出原始样本的文件名前缀,并赋值一个行号(这个行号是后面实现匹配对应的关键),生成一个raw.txt
文件存储
# 取出原始文件名称
ls *.fastq.gz|sed 's/\x1B\[[0-9;]*[a-zA-Z]//g'|sed 's/\.fastq.gz//'|awk '{print NR" "$0}' >raw.txt
步骤结果
## cat raw.txt
1 A_S1_L001_R1_001
2 A_S1_L001_R2_001
3 B_S1_L001_R1_001
4 B_S1_L001_R2_001
5 C_S1_L001_R1_001
.....
.....
42 U_S1_L001_R2_001
43 V_S1_L001_R1_001
44 V_S1_L001_R2_001
45 W_S1_L001_R1_001
46 W_S1_L001_R2_001
47 X_S1_L001_R1_001
48 X_S1_L001_R2_001
代码理解
sed 's/\x1B\[[0-9;]*[a-zA-Z]//g
用sed删除数字、特殊字符等
压缩文件终端打印的时候是带红色的字符,红色打印的ANSI转义码应该为:\x1b[31m
st3 生成要改名的信息
按改名需求,生成需要改成的文件名的信息,并存储到一个新的文件 new.txt
parallel -j 1 echo {1}{2}_{3}{4}_{5} ::: p ::: 1 2 3 4 5 6 ::: S ::: 1 2 3 4 ::: L001_R1_001 L001_R2_001 |awk '{print NR " " $0}' > new.txt
代码很巧妙的借用了parallel的用法;如果没有安装,使用代码前需要安装一下。
安装parallel
##激活小环境
mamba activate downsoft
##安装软件
mamba install parallel
生成要改名的信息
parallel -j 1 echo {1}{2}_{3}{4}_{5} ::: p ::: 1 2 3 4 5 6 ::: S ::: 1 2 3 4 ::: L001_R1_001 L001_R2_001
parallel 用法
:::
传递参数
{1}{2}_{3}{4}_{5}
定义需要传递的参数位
st4 生成原始名与修改名的对应信息
根据目前所得到的文件,剩下的就很简单了,就是一个改名前后的文件名对应,并改名
join raw.txt new.txt >name.txt
join 找出两个文件中,指定栏位内容相同的行,并加以合并,再输出到标准输出设备。
## 查看name.txt
head name.txt
1 A_S1_L001_R1_001 p1_S1_L001_R1_001
2 A_S1_L001_R2_001 p1_S1_L001_R2_001
3 B_S1_L001_R1_001 p1_S2_L001_R1_001
4 B_S1_L001_R2_001 p1_S2_L001_R2_001
5 C_S1_L001_R1_001 p1_S3_L001_R1_001
6 C_S1_L001_R2_001 p1_S3_L001_R2_001
7 D_S1_L001_R1_001 p1_S4_L001_R1_001
8 D_S1_L001_R2_001 p1_S4_L001_R2_001
9 E_S1_L001_R1_001 p2_S1_L001_R1_001
10 E_S1_L001_R2_001 p2_S1_L001_R2_001
....
st5 文件改名
cat name.txt |cut -d " " -f 2,3|while read id; do name=($id); raw=${name[0]}; new=${name[1]}; echo $raw $new ; mv ${raw}\.fastq.gz ${new}\.fastq.gz; done
## 或者更简洁一点的写法
cat name.txt |cut -d " " -f 2,3|while read raw new; do echo $raw $new ; mv ${raw}\.fastq.gz ${new}\.fastq.gz; done
亮点
paraller的用法很妙,实现生成递增的文件名 while改名同时指定多个变量,简洁明了
chatGPT
需要改名的文件
chatGPT直接给出了一个shell脚本,我们命名为rename_files.sh
完整步骤代码rename_files.sh
# touch {A..X}_S1_L001_R{1..2}_001.fastq.gz
x=1
i=0
previous_y=""
files=($(ls *_S1_L001_R*_001.fastq.gz | sort))
for file in "${files[@]}"; do
IFS="_"
parts=($file)
IFS=""
y="${parts[0]}"
sz="${parts[1]}"
if [ "$previous_y" != "$y" ]; then
((i++))
fi
if [ $i -gt 4 ]; then
((x++))
i=1
fi
new_y="p$x"
new_sz="S$i"
new_name="${new_y}_${new_sz}_${parts[2]}_${parts[3]}_${parts[4]}"
echo mv "$file" "$new_name"
previous_y="$y"
done
可以看到简单快捷,执行这个代码即可完美实现改名,那么现在我们来逐一理解一下这个chatGPT生成的代码。
分步理解
($(ls *_S1_L001_R*_001.fastq.gz | sort))
$( )
命令替换,会将$( )
里的内容先执行,然后将执行结果替换到当前命令中。等同于 反引号``
的 用法()
定义数组;且bash只支持一维数组,不支持多维数组
${files[@]}
取出files数组的所有值
IFS
定义shell的分隔符
首选以下划线分割文件名,并赋值为数组 parts
再将自定义的IFS变量赋值空,恢复为默认。
分别取出数值的0 和1号位置的值,如图所示
if [ "$previous_y" != "$y" ]; then
((i++))
fi
## 判断 两个字符是否相等,不等的话,i将加1
这一步骤的目的,即可实现对 S 的递增。
那么递增的何时停止呢,就是下面这个判断。
实现S从S1到S4之后,重新将 i 恢复为1,从而实现重新递增。
if [ $i -gt 4 ]; then
((x++))
i=1
fi
## 判断 i 是否大于4 ;如果大于 ;x 将加1,i 赋值为1
-gt
关系运算符;表示是否大于 ;检测左边的数是否大于右边的,如果是,则返回 true
理解了上面,下面就是很简单的赋值,改名
new_y="p$x"
new_sz="S$i"
new_name="${new_y}_${new_sz}_${parts[2]}_${parts[3]}_${parts[4]}"
echo mv "$file" "$new_name"
亮点
这个脚本逻辑很清晰,执行起来只需 bash rename_files.sh
即可完成改名,操作上来说也相对更简单。
以上就是单细胞转录组上游处理流程中一个关于文件改名的简单小结,算上如何优雅的给单细胞转录组fastq文件改名 中提供的R代码,已经有4个实现方案,那么你觉得哪种更简单易懂呢?
如果你对单细胞转录组研究感兴趣,但又不知道如何入门,也许你可以关注一下下面的课程
看完记得顺手点个“在看”哦!
长按扫码可关注
原文链接