Contents

Bash基础

1 常用快捷键

1.1 命令跳转

  • crtl + a (ahead) 跳转到命令首部
  • ctrl + e (end) 跳转到命令尾部
  • ctrl + p (previous) 上一条命令
  • ctrl + n (next) 下一条命令
  • ctrl + 左 (向左移动一个单词)
  • ctrl + 右 (向右移动一个单词)

1.2 删除

  • ctrl + k 删除到行尾
  • ctrl + u 删除到行首
  • ctrl + y 撤销,实际上为粘贴操作, 上述的删除实际有复制操作的副作用
  • ctrl + r 快速搜索

2. 交互方式

  • 交互式:1问1答
  • 非交互式:脚本方式

2.1 交互式bash

全局配置

  • /etc/bashrc

  • /etc/profile

    一般不修改

个人配置

  • ~/.bashrc

仍然会索引全局配置。 个人配置的查找顺序: .bash_profile .bash_login .profile, 只生效第一个查找到的目标

指定Login shell

useradd -s ${shell}

2.2 非交互式

  • 检查是否是交互式$- 输出结果包含i表明是交互式的。
  • 不同OS之间脚本文件可能存在格式报错, 使用dos2unix ${file}

3. 命令解析过程

  1. 花括号扩展
echo {1,2}{a,b} -> 1a 1b 2a 2b
mkdir /data{1..4}  -> 创建data1...data4 四个文件夹
  1. 波浪线扩展 ~:扩展为HOME地址
  2. 变量扩展 ${var} 替换为真实变量
  3. 算术扩展 计算表达式的值
  4. 命令替换 执行命令,将结果替换
  5. 词拆分 将变量值按照分隔符切分
var="a b c"
for v in ${var};do
	echo ${v}
done
  1. 路径名扩展 通配符扩展成实际名

命令解析实例

i=1
j=10
echo {$i..$j}
# 实际输出结构为 {1..10}, 原因在于首先执行{}扩展时没有可扩展的内容(此时变量还没有替换)

eval echo {$i..$j} 可输出 1 2 3 4 5 6 7 8 9 10, eval 会对字符串内容再进行一次解析

这个例子的${cmd} 替换为 echo hello | grep he 的时候输出为 ‘hello | grep he’ 没看懂

cmd='echo hello | grep he'; echo $cmd

# 执行变量替换 变成 echo "echo hello | grep he", 输出结果为: echo hello | grep he
cmd='echo hello | grep he'; $cmd
# 替换后为输出结果为: hello | grep he

4. vim 配置

  • 全局配置 /etc/vimrc

  • 个人配置 ~/.vimrc

    set expandtab # tab转空格

5. 语法

5.1 变量

  • 变量赋值 name=value # 对空格强敏感
  • 变量引用 ${name}

命令

# 1.
`commond`
# 2.
$(commond)

特殊变量

  • $? : 上一次命令的执行状态
  • $! : 最后一个后台进程的PID
  • $0 : 脚本本身
  • $$ : 当前脚本的PID
  • $@ : 多有入参
  • $# : 入参格式

常用变量

  • $UID: 当前登录用户的UID
  • $LOGNAME: 当前登录用户名
  • $HOSTNAME: 当前主机名
  • $RANDOM : 随机数(0-32767)
  • $(( RANDON % 10 )) : 0-9之间的随机数
  • $SECONDS: 本bash进程运行的时间
  • $HISTTIMEFORMAT: history 命令中的时间格式

环境变量

  • $EDITOR : 默认编辑器
  • $LANG : 语言环境变量
全局变量
  • export name=value

5.2 数值运算

  • let, expr
let a=1+2
let a=a+2
c=`expr 1 + 2` #注意空格
  • (( ))
c=((a+b))
c=(( a + b )) #双括号中的空格不敏感
  • bc
echo "1 + 2" | bc

5.4. 流程控制

  • if 双括号是bash提供的, 单括号是sh提供的

if [[ ]]

if (( ))

if [[ ]];then

elif [[ ]];then

else

fi
-f ${file} 是否为普通文件
- -d ${dir}	是否为目录
-n ${str} 	不为控制
-z ${str}	为空值
-e ${path}	文件是否存在
  • 逻辑运算符 && ||

if [[ con1 ]];then do else do not fi

可以替换为 con1 && do || do not

  • if 简写
if [[ -f ${file} ]];then
	exist=ok
fi

[[ -f ${file} ]] && exist=ok
  • case
case ${var} in
	con1)
	...
	;;
	con2)
	...
	;;
	*)
	...
	;;
esac

case 中的匹配符

  • 匹配0个或多个任意字符 ? 匹配一个或多个任意字符 [] 字符组, 匹配组中的任一字符 | A|B A或者B
  • while/for
while (());d0
	
done

for var in var1,var2 ...;do

done

for (( i=0; i<10;i++));do

done

6. 管道

6.1 重定向

进程运行时,内核为其准备三个默认文件描述符, 0-stdin,1-stdout,2-stderr

commond > file 	# 命令标准输出到file文件
commond >> file # 命令标准输出追加到file文件
commond 2 > file # 命令错误输出到file文件
commond > file 2>&1  commond &> file 	#命令标准/错误输出到文件

commond  | tee file # 命令标准输出到控制台同时输出到file文件
commond  | tee -a file # 命令标准输出到控制台同时追加到file文件

块重定向

块if,case; for,while; {}, ()命令分组;

if [[ $error ]];then
	echo "error" > file
else
	echo "ok" > file
fi
if [[ $error ]];then
	echo "error" 
else
	echo "ok" 
fi >> file
echo "msg1" >> file
echo "msg2" >> file

{
	echo "msg1"
	echo "msg2"
} >> file

6.2 重定向与管道

上层管道的输出作为本层管道的输入

cat file | grep xx

find -type f -name ‘.bak’ | xargs rm find -type f -name ‘.bak’ | xargs -i grep xxx {}

7. 函数

  • 函数返回值尽量是0/1
  • 尽量使用局部变量

8. 脚本

  • 变量
  • 函数
  • 主程序 配置、函数、主流程分别放在不同的文件

执行之前: bash -n src 检测语法 执行: bash -x 打印执行过程

9. 注意事项

  1. 获取脚本所在的绝对路径
pwd=$(cd $(dirname $0 ) && pwd )
  1. 匿名文件 <(commond) diff <(sort file_a) <(sort file_b)
  2. 获取每个管道的执行情况 PIPESTATUS[n]
  3. $* 和 $@ $* 会按照空格跟个符分割参数
  4. /*/过滤
# 只删除文件夹
rm -rf path/*/
# 只展示路径下的文件夹
echo path/*/

10. 常用指令

xargs (extended arguments)