Bash基础
Contents
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. 命令解析过程
- 花括号扩展
echo {1,2}{a,b} -> 1a 1b 2a 2b
mkdir /data{1..4} -> 创建data1...data4 四个文件夹
- 波浪线扩展
~:扩展为HOME地址
- 变量扩展 ${var} 替换为真实变量
- 算术扩展 计算表达式的值
- 命令替换 执行命令,将结果替换
- 词拆分 将变量值按照分隔符切分
var="a b c"
for v in ${var};do
echo ${v}
done
- 路径名扩展 通配符扩展成实际名
命令解析实例
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. 注意事项
- 获取脚本所在的绝对路径
pwd=$(cd $(dirname $0 ) && pwd )
- 匿名文件 <(commond) diff <(sort file_a) <(sort file_b)
- 获取每个管道的执行情况 PIPESTATUS[n]
- $* 和 $@
$*
会按照空格跟个符分割参数 /*/
过滤
# 只删除文件夹
rm -rf path/*/
# 只展示路径下的文件夹
echo path/*/