Shell笔记

Shell 笔记

概论

  • Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。Shell 既是一种命令语言,又是一种程序设计语言(解释性)。

    • bash :Bourne Again shell ,Linux缺省的 shell
    • dash :Debian Almquist Shell ,小巧,符合 POSIX 标准,比 bash 快
    • sh :Bourne shell ,之前是 UNIX 最初使用的 shell ,用户交互很烂;后来变为了 bash 的符号链接,再后来变为了 dash 的符号链接
    • zsh :最强大的 Shell ,最好看的 Shell ,程序员必备的 Shell
      • oh my zsh 是 zsh 的一个配置管理框架
  • login Shell VS nologin Shell
    • login Shell 在启动时会加载/etc/profile/etc/bashrc~/.bash_profile~/.bashrc,该种方式会切换环境变量,即启动一个全新的环境
    • nologin Shell 在启动时会加载/etc/bashrc~/.bashrc
su - kon # login
su kon # nologin
  • 脚本执行:

    • 脚本通过路径执行时默认执行脚本为当前类型的 Shell 解释器,也可以通过命令显式指定 Shell 解释器,此时脚本是在子Shell中执行,还可以通过脚本内的 sha-bang (释伴)指定脚本执行所需的 Shell 解释器;优先级:显式 > sha-bang > 当前

      #!/usr/bin/sh
    • 上述3种方法都是通过子 Shell 执行,脚本中的cd不会改路径;可以通过.source指定当前 Shell 执行,脚本中的cd会改路径

    • 只有路径执行需要文件执行权限,其它方法都不需要

./test.sh # 指定当前 Shell 的同类 Shell 执行, cd 不会改路径
sh test.sh
. ./test.sh # 指定当前的 Shell 执行, cd 会改路径
source ./test.sh # 指定当前的 Shell 执行, cd 会改路径
sh -vx test.sh # 以调试方式执行,显示整个执行过程
sh -n test.sh # 检查语法
  • ():在子 Shell 中执行
  • 退出当前 Shell
exit
  • 查看 Shell 关键字/命令
type ls if [ ]
  • 命令排序
    • ;两个命令都被执行
    • &&前者成功则后者执行,否则不执行
    • ||前者失败则后者执行,否则不执行
ls && la || ll # ls 执行成功则 la 执行,否则 ll 执行
  • Bash 中调用 Python 脚本
    • 此处的<<EOF表示后续的输入作为子命令或子 Shell 的输入,直到遇到 EOF (也可以换成别的符号)为止,再返回到主 Shell
#!/usr/bin/bash

python <<EOF # 此处若使用 python <<-EOF 据说表示忽略EOF前的空格和TAB,但我试了一下 Ubuntu 20.04 好像不行
print("hello")
print("python")
EOF # 此处必须顶格
  • Shell 快捷键
    • ctrl + a:光标移到行首
    • ctrl + e:光标移到行尾
    • ctrl + u:从光标处删到行首
    • ctrl + k:从光标处删到行尾
    • ctrl + r:搜索命令历史
    • ctrl + d:EOF,直接输效果同 exit
  • 前后台
    • 命令 + &:使该命令的执行隐藏到后台
    • nohup :即使退出依然持续执行该命令
    • ctrl + z :挂起进程
    • jobs :查看后台进程
    • bg :把进程调度到后台执行
    • fg :把后台进程调度到前台执行
    • kill %num :杀死当前 Shell 中作业号为 num 的进程
  • 捕捉信号
trap “echo !” HUP INT # trap "commands" signal-list
kill -l # 查看信号
# HUP 通知会话内的各个作业与控制终端不再关联,默认处理是终止收到该信号的进程
# INT 程序终止,ctrl+c
# QUIT 程序退出,类 INT ,但表示程序错误
# KILL 立即结束程序
# TSTP 停止程序运行,ctrl+z
  • 重定向

    • >覆盖
    • >>追加
    • 0标准输入 stdin
    • 1标准输出 stdout
    • 2错误输出 stderr
    • cat+EOF+ 重定向 一定程度可以取代简单的vim
# 输出
echo hello > test.txt # 等同与 echo hello 1> test.txt
aaa 2> test.txt
./hello.out &> test.txt # 等同于 ./hello.out > test.txt 2>&1
  • 通配符
    • 可参考 MySQL 笔记
    • *:任意多个字符
    • ?:任意一个字符
    • []:任意其中一个字符
    • {}:集合
  • 基本输出
    • echo:可以用颜色和部分转义符
    • printf:类似C

变量&数组

  • 自定义变量:在当前 Shell 生效
    • 命名规范:_、字母、数字;首字符不能是数字;区分大小写
    • 引用:${}
    • 作用域:当前的 Shell
    • 注意赋值=两边不要留空格
    • 删除变量unset
x1=1 # 1是字符串,同x1="1"
echo $x1
x1=2
echo ${x1}
echo ${#x1} # 长度
echo "${x1}"
echo "\${x1}"
  • 变量 IO
read -p "Please enter a string: " x1 # -p 提示信息
echo $x1
read x1 x2 x3 x4 # 一次读入多个变量,空格分割,回车结束
  • 预定义变量
echo $* # 所有命令行参数
echo $@ # 所有命令行参数
echo $# # 命令行参数个数
echo $$ # 当前进程 pid
echo $? # 上一条命令返回值
echo $! # 上一个后台进程 pid
echo $0 # 位置变量`$num`:表示第 num 个命令行参数
echo $1 # 位置变量`$num`:表示第 num 个命令行参数
  • 环境变量:类似全局变量,在当前 Shell 和子 Shell 中生效;在 Shell 编程中一般不用
export x1=1
x2=2
export x2
env # 查看环境变量
  • 命令替换:将字符串中命令(注意不能是变量)的输出替换字符串;若输出有多行,会默认用空格替换换行

    • $()
    • 反引号 ``
  • 强弱引用

    • 单引号'':强引用,引用的是字符串本身的值,所见即所得
    • 双引号"":弱引用,字符串中的变量(通过变量引用)、命令(通过命令替换)及转义字符会被解析替换
  • 变量删除

    • #:从前往后删除,最短匹配
    • ##:从前往后删除,最长匹配,贪婪匹配
    url=www.zhangty15226.com
    echo ${url#*.}
    echo ${url##*.}
    • %:从后往前删
    • %%:从后往前删除,最长匹配,贪婪匹配
    echo ${url%.*}
    echo ${url%%.*}
  • 变量替换

    • /:从前往后替换,最短匹配;后一个/接新值
    • //:从前往后替换,最长匹配,贪婪匹配;后一个/接新值
    echo ${url/w/W}
    echo ${url//w/W}
    • :--:++:有定义不替换;无定义替换
    echo ${url-aaa}
    unset url
    echo ${url-aaa}
    • :==:有定义不替换;无定义替换;同时赋值变量
    • :??:有定义不替换;无定义报错
  • 字符串切片

    • 类似substr
    echo ${url:0:10}
  • 整数运算

    • expr:会把字符串参数当做整数
      • \*:乘一般需要转义
    • $(())$[]
    • let
    let x=1+1
  • 浮点数运算

    • bc
    echo "1.1*1.2"|bc
    • python/python3
    echo "print 1.1*1.2"|python
    echo "print(1.1*1.2)"|python3
  • 更自由的变量方式:declare

  • 数组:索引数组(列表),关联数组(字典)

    • 索引数组切片同字符串
    • 都尽量使用索引遍历
a=(a1 a2 a3)
b=(`b.txt`) # 一行一个值
d[0]=d1;d[1]=d2;d[2]=d3
echo ${a[1]}
echo ${a[*]} # 所有元素,同@
echo ${#a[*]} # 元素个数
echo ${!a[*]} # 索引
declare -A c=([c1]=val1 [c2]=val2 [c3]=val3) # 关联数组

条件测试&分支

  • test[]
    • 括号内两端需要有空格,因为[]是命令
    • -a与;-o
  • 文件、数值、字符串
    • 详见man test
    • 也可以使用 C 风格脚本进行数值比较(())
  • [[]][]

    • [[]]支持正则匹配=~[]不支持
    • [[]]支持逻辑运算符&&||![]不支持
  • if-else

if exp ; then
	dosomething
else if exp2 ; then
	dosomething
else 
	dosomething
fi # 在 shell 中命令的结束基本都是反过来的相应关键字
  • case
case val in
	const1)
		dosomething;;
	const2)
		dosomething;;
	const3)
		dosomething;;
	*)
		dosomething;;
esac
  • for
for val in {1..10} # `seq 1 10` # `cat num.txt`
do
	dosomething
done

for ((i=0;i<10;i++))
do
	dosomething
done
  • while
while exp
do
	dosomething
done
  • until:为假执行
until exp
do
	dosomething
done

文件

  • 当前进程文件描述符:文件描述符是文件的索引,rm只是回收 inode num 和处理文件的 link count 等
ll /proc/$$/fd
  • 打开关闭文件
exec 6<> test.txt # 6为文件描述符
exec 6<&-
  • 管道

    • 管道是 FIFO 文件,是一个强制互斥的环形缓冲区
    • 允许两个进程以生产者-消费者模型进行通信,有最大长度
  • 匿名管道|

    • 配合grep/egrep进行筛选,egrep支持更多拓展元字符;详见man grep
      • 配合tee进行复制;详见man tee
      • 配合ag进行搜素
      • 相关:awk一门语言,可用于统计过滤等;详见man awk
      • 相关:sed流编辑器;详见man sed
  • 命名管道
mkfifo fifotest # 创建名为 fifotest 的管道
file fifotest

函数

  • 定义
fun(){
	dosomething
}
function fun(){
	dosomething
}
  • 使用基本变量local
  • 使用位置变量传入参
  • 返回值
    • 通过return和退出状态码$?,是一个unsigned char
    • 通过echo和命令替换

  • expect:自动化处理交互式命令的命令
  • shift:将位置变量左移,用于可变参数传参

个人总结

  • Shell 的语法实在是灵活多样非常繁琐
  • awksed的细节其实非常多,这里暂略
  • 感觉关键还是写好正则

参考


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!