【Linux 笔记】Linux 基本操作 - 02. shell编程基础
笔记接上篇【Linux 笔记】Linux 基本操作 - 01. 系统认知-文本处理-软件安装-环境变量。笔记大部分源于生信技能树的B站视频教程【生信技能树】生信人应该这样学linux(更新至第14集),如有需要,可去欣赏原汁原味的视频讲解。
8. shell编程基础
(1) 变量
1) 环境变量
环境变量会在当前Shell和这个Shell的所有子Shell当中生效。
① 设置全局环境变量:export 变量名=变量值
② 查看环境变量:env
③ 查看变量内容:echo $变量名
④ 删除变量: unset 变量名
⑤ 常见系统预定义环境变量:HOME
、PWD
、PATH
、PS1
echo $HOME #显示主目录
echo $PWD #显示当前工作目录
echo $PATH #显示系统查找命令的路径
echo $PS1 #显示已设置的系统提示符变量
# \[\e[32;1m\]\u \[\e[33;1m\]\t \[\e[35;1m\]\w \n\[\e[0;40m\]$
PS1:
变量 | 说明 |
---|---|
\d | 显示日期,格式为 “星期 月 日” |
\h | 显示简写主机名。如,默认主机名 “localhost” |
\t | 显示24小时制时间,格式为 “HH:MM:SS” |
\T | 显示12小时制时间,格式为 “HH:MM:SS” |
\A | 显示24小时制时间,格式为 “HH:MM” |
\u | 显示当前用户名 |
\w | 显示当前用户所在目录的完整名称 |
\W | 显示当前所在目录的最后一个目录 |
# | 执行的第几个命令 |
$ | 提示符。如果是root用户会显示提示符为 “#”,如果是普通用户则会显示提示符为“$”。 |
例如:[root@bogon ~]# PS1="[\u@\t \w]$ " : \u:代表root;\t代表24小时制时间;\w(w小写表示绝对路径)。
2) 位置参数变量→写入到脚本文件内
echo $12 #"$1"为变量,"2"为一个字符串;故,"$12"输出为"$1"的内容加上字符串2
echo ${12} #变量"$12"
位置参数变量 | 作用 |
---|---|
$n | n为数字,$0 代表命令本身,$1-$9 代表第1到第9个参数,10以上的参数需要用大括号{} 包含,如${10} 。 |
$* | 代表命令行中所有的参数,$* 把所有参数看成一个整体。 |
$@ | 代表命令行中所有的参数,但$@ 把每个参数区分对待。 |
$# | 代表命令行中所有参数的个数。 |
例如:创建一个脚本 canshu.sh:写入 echo $0 echo $1 echo $2 echo $3
# 执行脚本文件:
bash canshu.sh 11 22 33 #脚本名称与参数间空格分隔
# canshu.sh echo 11 echo 22 echo 33
# echo $0:$0 代表命令本身,$1-$9 代表第一个到第九个参数。
3) 预定义变量
预定义变量 | 作用 |
---|---|
$? | 最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值非0(具体返回哪一个数,由命令自己决定),则证明上一个命令执行不正确。 |
$$ | 当前进程的进程号(PID) |
$! | 后台运行的最后一个进程的进程号(PID) |
cat > variable.sh
#!/bin/bash
echo "The current process is $$" #输出当前进程的PID
#这个PID即为variable.sh脚本运行时,生成的进程PID
find /root -name hello.sh &
#使用find命令在root目录 下查找hello.sh文件
echo "The last one Daemon process is $!"
^C
cat variable.sh
bash variable.sh
#The current process is 168019
#The last one Daemon process is 168020
#hucy 16:30:58 ~/test
#find: ‘/root’: Permission denied
4) 自定义变量
用户自定义变量只在当前的Shell中生效;如果变量写入相应的配置文件,那么这个变量就会在所有的Shell中生效。
输出变量的值的格式为:echo $myvar
或者 echo ${myvar}
① 给变量赋值时,如果值包含空格,需要用单引号或者双引号包起来,否则会引起错误。
② 给变量赋值时,如果使用单引号,那么单引号里面的变量就不会解析成真正的值;双引号则可以。
③ 可以将一个命令执行的结果赋给一个变量。格式为:myvar=$(command)
或者 `comand`。值得注意的是,务必要将$()
和${}
的用途分开。
④ 变量的扩增,即将新的内容增加到变量原来的值上去。格式为"$变量名称""扩增内容"
或 ${变量}"扩增内容"
。
⑤ 使用export将变量变成环境变量,使得bash子进程可以使用变量。
⑥ 判断变量是否未设置,如果未设置则用-后面的内容赋值。语法为:var2=${var1-hellovar1}
(若var1
未设置,那么将hellovar1
赋值给var2
,否则将var1
的值赋给var2
)。需要注意的是,如果var1
设置为空值了,那么也算是赋值了。
⑦ 判断变量是否未设置或者设置为空值,如果未设置或者设置为空值则用-后面的内容赋值。语法为:var2=${var1:-hellovar1}
。
更多用法与规则:
注意:给变量赋值时,注意等号两端的空格问题。
(2) 参数
接收键盘输入=> read [选项] [变量名]
-p
“提示信息” :在等待read输入时,输出提示信息
-t
:read命令会一直等待用户输入,使用此选项可以指定等待时间
-n
字符数:read命令接受指定的字符数,就会执行
-s
:隐藏输入的数据,适用于机密信息的输入
例如:
cat > stdinput.sh
#!/bin/bash
read -t 30 -p "Please input your name:" name
#提示“请输入姓名”并等待30秒,把用户的输入保存到变量name中。
echo "Name is $name"
read -s -t 30 -p "Please enter your age:" age
#年龄是隐私,用-s选项隐藏输入。
echo "Age is $age"
echo -e "\n"
read -n 1 -t 30 -p "Please select your gender[M/F]:" gender
#使用“-n l”选项只接收一个输入字符就会执行,不用回车
echo -e "\n"
echo "Sex is $gender"
^C
(3) 通配符
wildcard
是一种命令行的路径扩展(path expansion)功能。在wildcard进行扩展后, 命令行会先完成重组,才会交给shell来处理。
一些常见的wildcard:
wildcard | 功能 |
---|---|
* | 匹配0个或多个字符 |
? | 匹配任意单一字符 |
[list] | 匹配list中任意单一字符 |
[!list] | 匹配不在list中任意单一字符 |
{string1,string2,...} | 匹配string1或者stsring2或者(…)中其一字符串 |
a*b # a与b之间可以有任意个字符(0个或多个),如aabcb, axyzb, a012b,ab等。
a?b # a与b之间只能有一个字符,但该字符可以任意字符,如 aab, abb, acb, azb等。
a[xyz]b # a与b之间只能有一个字符,但这个字符只能是x或者y或者z,如:axb, ayb, azb这三个。
a[!0-9]b # a与b之间只能有一个字符,但这个字符不能是阿拉伯数字,如aab,ayb,a-b等。
a{abc,xyz,123}b # a与b之间只能是abc或者xyz或者123这三个字串之一,扩展后是aabcb,axyzb,a123b。
(4) 标准头文件
#!/bin/bash
## Author:
## Address:
## E-mail:
## Some demo
## ……
set -e
# Function for script description and usage
usage()
{
cat <<EOF >&2
Usage:
}
# 查看系统预设的shell
cat /etc/shells
(5) 变量替换
在bash shell中, **$()**与 `` (反引号)都是用来做命令替换(command substitution)的。
A=BCD
A=${A}E
A=ls #命令
$A
${A}
B=la #参数
C=/tmp #目录
$A -$B $C #即 ls -la /tmp
echo $A -$B $C
echo the last sunday is $(date -d "last sunday" +%Y-%m-%d)
echo the last sunday is `date -d "last sunday" +%Y-%m-%d`
(6) 循环
for
变量名 in
列表;
do
循环体;
done
while
CONDITION;
do
循环体;
done
until
CONDITION;
do
循环体;
done
# 借助echo来确保,循环正确
for i in a.txt b.txt n.txt; do echo $i; done # 文件不多,手动放在in后,用空格分开
for i in `seq 1 9`; do echo SRR250${i}; done # 文件名为数字顺序,用seq命令生成连续数据,引用命令需要``
for i in `ls data/*.txt`; do echo $i; done # 匹配某类文件作为输入
for i in $(cat .*txt) ;do echo $i; done # 匹配某类文件作为输入
for i in `cat list.txt`; do echo $i; done # 使用文本源为输入列表
for i in `cat list.txt | cut -f 1`; do echo $i; done #指定某列作为输入文件名
plot_heatmap.sh -i data/${i} -o heatmap/${i}.pdf
ls $(pwd)/SRR* | while read id;do echo $id `basename $id`;done
for id in $(ls $(pwd)/SRR*) ;do echo $id `basename $id`;done
注:basename #去除路径等,只剩基本文件名。
(7) 重定向
谈到 I/O redirection,不妨先认识一下File Descriptor (fd,文件描述符)。在 shell 的进程中,最常使用的 fd 有三个,分别为:
- 0: standard Input (STDIN)
- 1: standard output (STDOUT)
- 2: standard Error output (STDERR)
在标准情况下,这些 fd 分别跟如下设备 (device) 关联:
- stdin(0): keyboard
- stdout(1): monitor
- stderr(2): monitor
Tips: linux 中的文件描述符 (fd) 用整数表示。 linux 中任何一个进程都默认打开三个文件, 这三个文件对应的文件描述符分别是:0, 1, 2; 即 stdin, stdout, stderr。
用<
来改变输入的数据通道 (stdin),使之从指定的文件读进。用>
来改变输出的数据通道 (stdout,stderr), 使之输出到指定的文件。严格来说,<符号之前需要指定一个 fd 的 (之前不能有空白),但因为 0 是<的预设值,因此,<
与0<
是一样的 。
- 标准输入:
0<
- 标准输出:
1>
#改变 stdout 的输出通道; - 标准错误:
2>
#改变 stderr 的输出通道;
<<
是所谓的here document,它可以让我们输入一段文本,直到读到<<
后指定的字符串结束。
cat <<EOF>tmp.txt
first line here
second line here
third line here
EOF #end of file: 读到此字符串便结束,无需Ctrl+C
cat tmp.txt
#first line here
#second line here
#third line here
2>&1
#将stderr并进stdout输出。
1>&2
或者 >&2
#将stdout并进stderr输出。
在 linux 的文件系统中,有个设备文件: /dev/null
。将 fd 1跟 fd 2重定向到 /dev/null 去,就可忽略 stdout, stderr 的输出。将 fd 0重定向到 /dev/null,那就是读进空 (nothing)。
(8) 进程替换
bed=exon_probe.hg38.gene.bed
for bam in ~/*.bam
do file=$(basename $bam)
sample=${file%%.*}
echo $sample
export total_reads=$(samtools idxstats $bam | awk -F '\t' '{s+=$3}END{print s}')
echo The number of reads is $total_reads
bedtools multicov -bams $bam -bed $bed | perl -alne '{$len=$F[2]-$F[1];if($len <1) {print "$.\t$F[4]\t0"}else{$rpkm=(1000000000*$F[4]/($len*$ENV{total_reads}));print "$.\t$F[4]\t$rpkm"}}' > $sample.rpkm.txt
done
注:
全局变量:export
%%.* #每个%表示删除一个点号及其后的字符串(右删除)。
export bed=13edsfd
echo $bed
perl -e 'print $ENV{bed}'
# https://github.com/jmzeng1314/my_WGCNA
总结
- 在bash shell中, $()与 `` (反引号)都是用来做命令替换的。
- $(())是用来作整数运算的。
- 全局变量:export
- basename #去除路径等,只剩基本文件名。
- %%.* #每个%表示删除一个点号及其后的字符串。
参考阅读:
1. Linux Shell基础 - Bash变量 - 环境变量 - 位置参数变量 - 预定义变量
2. 菜鸟学Linux - 变量基本规则
3. Shell 十三问
4. shell循环:for、while、until——详解