Shell
查看shell
- 查看当前设备的默认shellshell
echo $SHELL
- 查看当前使用的shellshell
ps #倒数第二行为当前使用的shell
- 查看当前系统安全的所有shellshell
cat /etc/shells
- 退出Bash环境shell
exit #快捷键Ctrl + d
- 查看Bash版本shell
bash --version
基本语法
- 在屏幕中输出文本shell
echo hello world
- 在屏幕中输出文本shell
echo hello world #参数含义 -n #取消换行 -e #解析特殊字符, 如\n为换行
- 组合符&&和||shell
ls && clear #ls执行成功则执行clear ls || clear #ls执行失败则执行clear
- 判断命令的来源shell
type ls #ls是外部程序 #参数含义 -a #查看命令的所有定义 -t #返回一个命令的类型:别名(alias),关键词(keyword),函数(function),内置命令(builtin)和文件(file)
模式扩展
- 关闭扩展shell
set -o noglob #或 set -f
- 开启扩展shell
set +o noglob #或 set +f
- 波浪线扩展shell
echo ~ #波浪线代表用户主目录 echo ~+ #表示当前所在目录, 等同pwd
- 问号扩展shell
ls ?.txt ~ #问号匹配文件路径的单个字符, 若文件路径是多个字符,则使用多个问号占位
- 星号扩展shell
ls *.txt ~ #星号匹配文件路径里任意数量的任意字符, 不会匹配隐藏文件 echo .* #显示所有隐藏文件 ls */*.txt #匹配子目录
- 方括号扩展shell
ls [ab].txt #匹配满足方括号内的单字符的文件 ls [!ab].txt 或 ls [^ab].txt #匹配不满足方括号内的单字符的文件 ls */*.txt #匹配子目录
- 范围扩展shell
ls [a-z].txt #匹配满足a到z之间的单字符的文件
- 大括号扩展shell
echo {a,b,c} #分别扩展成大括号里的所有值, 遍历式输出 echo {a..c} #范围连续扩展 echo {a..c,{b,c,d}} #嵌套扩展 echo {0..8..2} #指定步长为2, 输出: 0 2 4 6 8 echo {001..3} #若开头有前导字符则每项都有, 输出: 001 002 003
- 变量扩展shell
$SHELL 或 ${SHELL} #表示变量 ${!S*} 或 ${!S@} #返回所有匹配给定字符串S的变量名
- 子命令扩展shell
$(date) 或 `date` #返回date命令的结果 $(ls $(pwd)) #子命令嵌套 $((2 + 2)) #算术运算
- 字符类shell
#字符类属于文件名扩展 [[:alnum:]] #匹配任意英文字母与数字 [[:alpha:]] #匹配任意英文字母 [[:blank:]] #空格和 Tab 键 [[:cntrl:]] #ASCII 码 0-31 的不可打印字符 [[:digit:]] #匹配任意数字 0-9 [[:graph:]] #A-Z、a-z、0-9 和标点符号 [[:lower:]] #匹配任意小写字母 a-z [[:upper:]] #匹配任意大写字母 A-Z [[:print:]] #ASCII 码 32-127 的可打印字符 [[:punct:]] #标点符号(除了 A-Z、a-z、0-9 的可打印字符) [[:space:]] #空格、Tab、LF(10)、VT(11)、FF(12)、CR(13) [[:xdigit:]] #16进制字符(A-F、a-f、0-9)
- 查询extglob是否打开shell
shopt extglob #量词语法在bash的extglob参数打开的情况下才能使用
- 开启extglobshell
shopt -s extglob
- 量词语法shell
?(s) #模式匹配零次或一次, 如: ls abc?(.)txt 输出: abctxt abc.txt *(s) #模式匹配零次或多次, 如: ls abc*(.txt) 输出: abctxt abc.txt.txt +(s) #模式匹配一次或多次, 如: ls abc+(.txt) 输出: abc.txt abc.txt.txt @(s) #只匹配一次模式, 如: ls abc@(.txt|.php) 输出: abc.php abc.txt !(s) #匹配给定模式以外的任何内容, 如: ls a!(b).txt 输出: a.txt abb.txt ac.txt
- shopt命令shell
#用于调整bash的行为 shopt -s [optionname] #开启某个参数 shopt -u [optionname] #关闭某个参数 shopt [optionname] #查看某个参数状态 #参数含义 dotglob #使扩展结果包含隐藏文件 nullglob #使通配符不匹配任何文件名时,返回空字符 failglob #使通配符不匹配任何文件名时,Bash 会直接报错 extglob #使Bash支持扩展语法 nocaseglob #使通配符扩展不区分大小写 globstar #使**匹配零个或多个子目录。该参数默认是关闭的
- here文档shell
cat << _EOF_ _EOF_ #_EOF_名称可任意取,
- here字符串shell
cat <<< 'holle world' #将字符串通过标准输入,传递给命令
变量
- 显示所有环境变量shell
env 或 printenv
- 常见环境变量shell
BASHPID #Bash 进程的进程 ID BASHOPTS #当前 Shell 的参数,可以用shopt命令修改 DISPLAY #图形环境的显示器名字,通常是:0,表示 X Server 的第一个显示器 EDITOR #默认的文本编辑器 HOME #用户的主目录 HOST #当前主机的名称 IFS #词与词之间的分隔符,默认为空格 LANG #字符集以及语言编码,比如zh_CN.UTF-8 PATH #由冒号分开的目录列表,当输入可执行程序名后,会搜索这个目录列表 PS1 #Shell 提示符 PS2 #输入多行命令时,次要的 Shell 提示符 PWD #当前工作目录 RANDOM #返回一个0到32767之间的随机数 SHELL #Shell 的名字 SHELLOPTS #启动当前 Shell 的set命令的参数 TERM #终端类型名,即终端仿真器所用的协议 UID #当前用户的 ID 编号 USER #当前用户的用户名
- 显示所有变量(包括环境变量和自定义变量),以及所有的 Bash 函数shell
set
- 创建变量shell
#规则如下: #字母、数字和下划线字符组成 #第一个字符必须是一个字母或一个下划线,不能是数字 #不允许出现空格和标点符号 variable=value #等号左边是变量名,右边是变量。注意,等号两边不能有空格 variable="hello world" #如果变量的值包含空格,则必须将值放在引号中
- 读取变量shell
echo $variable
- 删除变量shell
unset variable 或 variable=''
- 输出变量传递给子shellshell
export variable #子shell修改变量不会影响父shell
- 特殊变量shell
$? #上一个命令的退出码: 0表示上个命令执行成功, 不是0表示上个命令执行失败 $$ #当前shell的进程ID $_ #上一个命令的最后一个参数 $! #最近一个后台执行的异步命令的进程 ID, firefox & 或 echo $! $0 #为当前 Shell 的名称 $- #为当前 Shell 的启动参数 $# #表示脚本的参数数量 $@ #表示脚本的参数值
- 变量的默认值shell
${varname:-word} #表示变量varname不存在时返回word ${varname:=word} #表示变量varname不存在时返回word, 并将varname设置为word ${varname:+word} #表示变量varname不存在时返回空值, 存在时返回word, 用于测试变量是否存在 ${varname:?word} #表示变量varname不存在时打印出varname:word, 并中断脚本的执行, 它的目的是防止变量未定义
- declare命令shell
#declare命令可以声明一些特殊类型的变量,为变量设置一些限制,比如声明只读类型的变量和整数类型的变量 #declare命令如果用在函数中,声明的变量只在函数内部有效,等同于local命令 declare OPTION varname=value #OPTION参数如下: -a #声明数组变量 -f #输出所有函数定义 -F #输出所有函数名 -i #声明整数变量 -l #声明变量为小写字母 -p #查看变量信息 -r #声明只读变量 -u #声明变量为大写字母 -x #该变量输出为环境变量
- readonly命令shell
#readonly命令等同于declare -r,用来声明只读变量,不能改变变量值,也不能unset变量 readonly OPTION varname=value #OPTION参数如下: -a #声明的变量为数组 -f #声明的变量为函数名 -p #打印出所有的只读变量
- let命令shell
#let命令声明变量时,可以直接执行算术表达式 let foo=1+2 #let命令的参数表达式如果包含空格,就需要使用引号 let "foo = 1 + 2" #let可以同时对多个变量赋值,赋值表达式之间使用空格分隔 let "v1 = 1" "v2 = v1++"
字符串操作
- 获取变量长度shell
${#varname}
- 获取子串shell
${varname:offset:length} #offset起始下标, length获取的长度 ${varname: -5:2} #负值起始下标前面必须有空格 ${varname: -5:-2} #length为负值表示排除从字符串末尾开始的length个字符
- 转换大小写shell
${varname^^} #转为大写 ${varname,,} #转为小写
- 字符串头部的模式匹配shell
${varname#/*/} #如果 /*/ 匹配变量 varname 的开头, 则删除最短匹配的部分,返回剩余部分 ${varname##/*/} #如果 /*/ 匹配变量 varname 的开头, 则删除最长匹配的部分,返回剩余部分
- 字符串尾部的模式匹配shell
${varname%/*/} #如果 /*/ 匹配变量 varname 的开头, 则删除最短匹配的部分,返回剩余部分 ${varname%%/*/} #如果 /*/ 匹配变量 varname 的开头, 则删除最长匹配的部分,返回剩余部分
- 任意位置的模式匹配shell
${varname//*//abc} #如果 /*/ 匹配变量 varname 的任意位置, 则最长匹配的那部分替换为abc, 仅替换第一个匹配的 ${varname///*//abc} #如果 /*/ 匹配变量 varname 的任意位置, 则最长匹配的那部分替换为abc, 所有匹配都替换 #abc为替换字符串, 若不设置则替换为空, 即删除匹配的字符
算术运算
- 算术表达式shell
echo $((2 + 2)) #会自动忽略内部空格 #支持的算术运算符 + #加法 - #减法 * #乘法 / #除法(整除) % #取余 ** #指数 ++ #自增运算(前缀或后缀) -- #自减运算(前缀或后缀)
- 数值的进制shell
number #没有任何特殊表示法的数字是十进制数(以10为底) 0number #八进制数。 0xnumber #十六进制数。 base#number #base进制的数
- 位运算shell
$((1 << 2)) #位左移运算,把一个数字的所有位向左移动指定的位 $((1 >> 2)) #位右移运算,把一个数字的所有位向右移动指定的位 $((1 & 2)) #位的“与”运算,对两个数字的所有位执行一个AND操作 $((1 | 2)) #位的“或”运算,对两个数字的所有位执行一个OR操作 $((1 ~ 2)) #位的“否”运算,对一个数字的所有位取反 $((1 ^ 2)) #位的异或运算(exclusive or),对两个数字的所有位执行一个异或操作
- 逻辑运算shell
< #小于 > #大于 <= #小于或相等 >= #大于或相等 == #相等 != #不相等 && #逻辑与 || #逻辑或 ! #逻辑否 expr1?expr2:expr3 #三元条件运算符,若表达式expr1的计算结果为非零值(算术真),则执行表达式expr2,否则执行表达式expr3
- 赋值运算shell
$((parameter = value)) #简单赋值。 $((parameter += value)) #等价于$((parameter = parameter + value)) $((parameter -= value)) #等价于$((parameter = parameter – value)) $((parameter *= value)) #等价于$((parameter = parameter * value)) $((parameter /= value)) #等价于$((parameter = parameter / value)) $((parameter %= value)) #等价于$((parameter = parameter % value)) $((parameter <<= value)) #等价于$((parameter = parameter << value)) $((parameter >>= value)) #等价于$((parameter = parameter >> value)) $((parameter &= value)) #等价于$((parameter = parameter & value)) $((parameter |= value)) #等价于$((parameter = parameter | value)) $((parameter ^= value)) #等价于$((parameter = parameter ^ value))
- 求值运算shell
echo $((foo = 1 + 2, 3 * 4)) #在$((...))内部,逗号是求值运算符,执行前后两个表达式,并返回后一个表达式的值
- expr命令shell
expr 3 + 2 #支持算术运算,可以不使用((...))语法
行操作
- 设置当前快捷键库shell
#Bash 内置了 Readline 库, 默认采用 Emacs 快捷键 set -o vi #设置使用vi的快捷键 set editing-mode vi #永久性更改编辑模式,将命令写在~/.inputrc文件,这个文件是 Readline 的配置文件
- 关闭Readlineshell
bash --noediting
- 光标移动(Emacs模式)shell
Ctrl + a #移到行首 Ctrl + b #向行首移动一个字符,与左箭头作用相同 Ctrl + e #移到行尾 Ctrl + f #向行尾移动一个字符,与右箭头作用相同 Alt + f #移动到当前单词的词尾 Alt + b #移动到当前单词的词首
- 清空屏幕shell
Ctrl + l
- 编辑操作shell
Ctrl + d #删除光标位置的字符(delete) Ctrl + w #删除光标前面的单词 Ctrl + t #光标位置的字符与它前面一位的字符交换位置(transpose) Alt + t #光标位置的词与它前面一位的词交换位置(transpose) Alt + l #将光标位置至词尾转为小写(lowercase) Alt + u #将光标位置至词尾转为大写(uppercase) Ctrl + k #剪切光标位置到行尾的文本 Ctrl + u #剪切光标位置到行首的文本 Alt + d #剪切光标位置到词尾的文本 Alt + Backspace #剪切光标位置到词首的文本 Ctrl + y #在光标位置粘贴文本
- 自动补全shell
Tab #完成自动补全 Alt + ? #列出可能的补全,与连按两次 Tab 键作用相同 Alt + / #尝试文件路径补全 Ctrl + x / #先按Ctrl + x,再按/,等同于Alt + ?,列出可能的文件路径补全 Alt + ! #命令补全 Ctrl + x ! #先按Ctrl + x,再按!,等同于Alt + !,命令补全 Alt + ~ #用户名补全 Ctrl + x ~ #先按Ctrl + x,再按~,等同于Alt + ~,用户名补全 Alt + $ #变量名补全 Ctrl + x $ #先按Ctrl + x,再按$,等同于Alt + $,变量名补全 Alt + @ #主机名补全 Ctrl + x @ #先按Ctrl + x,再按@,等同于Alt + @,主机名补全 Alt + * #在命令行一次性插入所有可能的补全 Alt + Tab #尝试用.bash_history里面以前执行命令,进行补全
- 操作历史shell
#退出当前 Shell 的时候,Bash 会将用户在当前 Shell 的操作历史写入~/.bash_history文件,该文件默认储存500个操作 echo $HISTFILE #显示.bash_history完整路径 history #输出.bash_history文件的全部内容 !e #找出操作历史中最近的一条以e开头的命令并执行 export HISTTIMEFORMAT='%F %T ' && history #显示每个操作的时间 export HISTSIZE=0 #设置保存历史的操作数量, 设置0则不保存操作历史
目录堆栈
- 返回上一次的目录shell
cd -
- pushd和popd命令shell
pushd dirname #进入目录dirname,并将当前目录放入堆栈, 再将dirname目录放入堆栈 popd #不带有参数时,会移除堆栈的顶部记录,并进入新的栈顶目录(即原来的第二条目录 #参数如下: -n #只操作堆栈,不改变目录 pushd +3 #将从栈顶算起的3号目录(从0开始)移动到栈顶,同时切换到该目录 popd -3 #将从栈底算起的3号目录(从0开始)移动到栈顶,同时切换到该目录 popd +3 #删除从栈顶算起的3号目录(从0开始),不改变当前目录 popd -3 #删除从栈底算起的3号目录(从0开始),不改变当前目录
- dirs命令shell
dirs #命令可以显示目录堆栈的内容 #参数如下: -c #清空目录栈。 -l #用户主目录不显示波浪号前缀,而打印完整的目录 -p #每行一个条目打印目录栈,默认是打印在一行 -v #每行一个条目,每个条目之前显示位置编号(从0开始) +N #N为整数,表示显示堆顶算起的第 N 个目录,从零开始 -N #N为整数,表示显示堆底算起的第 N 个目录,从零开始
脚本入门
- shebang行(脚本首行)shell
#!/bin/bash #指定脚本解释器:/bin/bash, #!读shebang
- 执行权限和路径shell
$ chmod +x script.sh # 给所有用户执行权限 $ chmod +rx script.sh 或 chmod 755 script.sh # 给所有用户读权限和执行权限 $ chmod u+rx script.sh # 只给脚本拥有者读权限和执行权限 #脚本调用时,一般需要指定脚本的路径(比如path/script.sh)。如果将脚本放在环境变量$PATH指定的目录中,就不需要指定路径了。因为 Bash 会自动到这些目录中,寻找是否存在同名的可执行文件。建议在主目录新建一个~/bin子目录,专门存放可执行脚本,然后把~/bin加入$PATH export PATH=$PATH:~/bin # 将~/bin添加到$PATH的末尾 source ~/.bashrc # 刷新环境变量
- env命令shell
#!/usr/bin/env node #env命令总是指向/usr/bin/env文件,或者说,这个二进制文件总是在目录/usr/bin #让 Shell 查找$PATH环境变量里面第一个匹配的node #参数如下: -i, --ignore-environment #不带环境变量启动 -u, --unset=NAME #从环境变量中删除一个变量 --help #显示帮助 --version #输出版本信息
- 脚本参数shell
script.sh word1 word2 word3 #调用脚本时后面可以带有参数 # 脚本文件内部,可以使用特殊变量,引用这些参数 $0 #脚本文件名,即script.sh。 $1~$9 #对应脚本的第一个参数到第九个参数。脚本的参数多于9个,那么第10个参数可以用${10}的形式引用,以此类推 $# #参数的总数。 $@ #全部的参数,参数之间使用空格分隔。 $* #全部的参数,参数之间使用变量$IFS值的第一个字符分隔,默认为空格,但是可以自定义
- shift命令shell
#shift命令移除脚本参数 shift 3 #指定移除第三个参数, $4向前进一位变成$3
- getopts命令shell
#getopts命令用在脚本内部,可以解析复杂的脚本命令行参数,通常与while循环一起使用,取出脚本所有的带有前置连词线(-)的参数
- 配置项参数终止符shell
cat -- --file #--它的作用是告诉 Bash,在它后面的参数开头的-和--不是配置项,只能当作实体参数解释
- read命令shell
echo -n "输入一些文本 > " #让用户输入文本 read text #将用户输入的内容存入text变量中, 可接收用户输入多个值,用逗号分隔,用多个变量接收 #更多规则: #如果用户的输入项少于read命令给出的变量数目,那么额外的变量值为空。 #如果用户的输入项多于定义的变量,那么多余的输入项会包含到最后一个变量中。 #如果read命令之后没有定义变量名,那么环境变量REPLY会包含所有的输入 filename='/etc/hosts' while read myline do echo "$myline" done < $filename #done命令后面的定向符<,将文件内容导向read命令,每次读取一行,存入变量myline,直到文件读取完毕 #参数如下: -t #指定超时时间,超时将放弃等待,继续向下执行 -p #指定用户输入前的提示信息 -a #把用户的输入赋值给一个数组,从零号位置开始 -n #只读取若干个字符作为变量值,而不是整行读取 -e #允许用户输入的时候,使用readline库提供的快捷键,比如自动补全 -d delimiter #定义字符串delimiter的第一个字符作为用户输入的结束,而不是一个换行符 -r #raw 模式,表示不把用户输入的反斜杠字符解释为转义字符 -s #使得用户的输入不显示在屏幕上,这常常用于输入密码或保密信息 -u fd #使用文件描述符fd作为输入
- IFS变量shell
#可以通过自定义环境变量IFS(内部字段分隔符,Internal Field Separator 的缩写),修改分隔标志 #IFS的赋值命令和read命令写在一行,这样的话,IFS的改变仅对后面的命令生效,该命令执行后IFS会自动恢复原来的值
条件判断
- if判断shell
echo -n "输入一个1到3之间的数字(包含两端)> " read character if [ "$character" = "1" ]; then echo 1 elif [ "$character" = "2" ]; then echo 2 elif [ "$character" = "3" ]; then echo 3 else echo 输入不符合要求 fi #if主判断条件, elif次判断条件, fi结束符, then满足判断条件的主体内容 #示例 # 上一条命令是否不等于0 if [ $? -ne 0 ]; then echo "failed" else echo "succeed" fi # 上一条命令状态是否等于0 if [ $? -eq 0 ]; then echo "succeed" else echo "failed" fi # 建议的代码风格: 如果命令未执行成,就打印执行失败 if ! make mytarget; then echo "Build failed" fi
- case判断shell
case expression in pattern ) commands ;; pattern ) commands ;; * ) commands ;; esac #expression为表达式, pattern匹配表达式的多个结果, commands为满足条件的主体内容, 两个分号;;为结束条件块, *匹配其他未设置条件 #支持通配符如下: a) #匹配a。 a|b) #匹配a或b。 [[:alpha:]]) #匹配单个字母。 ???) #匹配3个字符的单词。 *.txt) #匹配.txt结尾。 *) #匹配任意输入,通过作为case结构的最后一个模式 #Bash 4.0之前,case结构只能匹配一个条件,然后就会退出case结构。Bash 4.0之后,允许匹配多个条件,这时可以用;;&终止每个条件块。 #示例: read -n 1 -p "Type a character > " echo case $REPLY in [[:upper:]]) echo "'$REPLY' is upper case." ;;& [[:lower:]]) echo "'$REPLY' is lower case." ;;& [[:alpha:]]) echo "'$REPLY' is alphabetic." ;;& [[:digit:]]) echo "'$REPLY' is a digit." ;;& [[:graph:]]) echo "'$REPLY' is a visible character." ;;& [[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;& [[:space:]]) echo "'$REPLY' is a whitespace character." ;;& [[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;& esac #输出: Type a character > a 'a' is lower case. 'a' is alphabetic. 'a' is a visible character. 'a' is a hexadecimal digit.
- test命令shell
#if结构的判断条件,一般使用test命令,有三种形式: test expression [ expression ] [[ expression ]] #规则: #三种形式是等价的, 区别:[[]]支持正则, 另外两种不支持 #expression为表达式, 为真则返回0, 为假则返回1 #[]和[[]]两种写法, 与内部的表达式expression之间必须有空格
判断表达式
- 文件判断shell
[ -a file ] #如果 file 存在,则为true。 [ -b file ] #如果 file 存在并且是一个块(设备)文件,则为true。 [ -c file ] #如果 file 存在并且是一个字符(设备)文件,则为true。 [ -d file ] #如果 file 存在并且是一个目录,则为true。 [ -e file ] #如果 file 存在,则为true。 [ -f file ] #如果 file 存在并且是一个普通文件,则为true。 [ -g file ] #如果 file 存在并且设置了组 ID,则为true。 [ -G file ] #如果 file 存在并且属于有效的组 ID,则为true。 [ -h file ] #如果 file 存在并且是符号链接,则为true。 [ -k file ] #如果 file 存在并且设置了它的“sticky bit”,则为true。 [ -L file ] #如果 file 存在并且是一个符号链接,则为true。 [ -N file ] #如果 file 存在并且自上次读取后已被修改,则为true。 [ -O file ] #如果 file 存在并且属于有效的用户 ID,则为true。 [ -p file ] #如果 file 存在并且是一个命名管道,则为true。 [ -r file ] #如果 file 存在并且可读(当前用户有可读权限),则为true。 [ -s file ] #如果 file 存在且其长度大于零,则为true。 [ -S file ] #如果 file 存在且是一个网络 socket,则为true。 [ -t fd ] #如果 fd 是一个文件描述符,并且重定向到终端,则为true。 这可以用来判断是否重定向了标准输入/输出/错误。 [ -u file ] #如果 file 存在并且设置了 setuid 位,则为true。 [ -w file ] #如果 file 存在并且可写(当前用户拥有可写权限),则为true。 [ -x file ] #如果 file 存在并且可执行(有效用户有执行/搜索权限),则为true。 [ FILE1 -nt FILE2 ] #如果 FILE1 比 FILE2 的更新时间更近,或者 FILE1 存在而 FILE2 不存在,则为true。 [ FILE1 -ot FILE2 ] #如果 FILE1 比 FILE2 的更新时间更旧,或者 FILE2 存在而 FILE1 不存在,则为true。 [ FILE1 -ef FILE2 ] #如果 FILE1 和 FILE2 引用相同的设备和 inode 编号,则为true。 #示例 FILE=~/.bashrc if [ -e "$FILE" ]; then if [ -f "$FILE" ]; then echo "$FILE is a regular file." fi if [ -d "$FILE" ]; then echo "$FILE is a directory." fi if [ -r "$FILE" ]; then echo "$FILE is readable." fi if [ -w "$FILE" ]; then echo "$FILE is writable." fi if [ -x "$FILE" ]; then echo "$FILE is executable/searchable." fi else echo "$FILE does not exist" exit 1 fi #$FILE要放在双引号之中,这样可以防止变量$FILE为空,从而出错。因为$FILE如果为空,这时[ -e $FILE ]就变成[ -e ],这会被判断为真。而$FILE放在双引号之中,[ -e "$FILE" ]就变成[ -e "" ],这会被判断为假
- 字符串判断shell
[ string ] #如果string不为空(长度大于0),则判断为真。 [ -n string ] #如果字符串string的长度大于零,则判断为真。 [ -z string ] #如果字符串string的长度为零,则判断为真。 [ string1 = string2 ] #如果string1和string2相同,则判断为真。 [ string1 == string2 ] #等同于[ string1 = string2 ]。 [ string1 != string2 ] #如果string1和string2不相同,则判断为真。 [ string1 '>' string2 ] #如果按照字典顺序string1排列在string2之后,则判断为真。 [ string1 '<' string2 ] #如果按照字典顺序string1排列在string2之前,则判断为真。 #注意,test命令内部的>和<,必须用引号引起来(或者是用反斜杠转义)。否则,它们会被 shell 解释为重定向操作符
- 整数判断shell
[ integer1 -eq integer2 ] #如果integer1等于integer2,则为true。 [ integer1 -ne integer2 ] #如果integer1不等于integer2,则为true。 [ integer1 -le integer2 ] #如果integer1小于或等于integer2,则为true。 [ integer1 -lt integer2 ] #如果integer1小于integer2,则为true。 [ integer1 -ge integer2 ] #如果integer1大于或等于integer2,则为true。 [ integer1 -gt integer2 ] #如果integer1大于integer2,则为true。
- 正则判断shell
[[ string1 =~ regex ]] #=~是正则比较运算符
- test 判断的逻辑运算shell
&& #表示: 与,也可使用参数-a。 || #表示: 或,也可使用参数-o。 ! #表示: 非。 #示例: if [ ! \( $INT -ge $MIN_VAL -a $INT -le $MAX_VAL \) ]; then echo "$INT is outside $MIN_VAL to $MAX_VAL." else echo "$INT is in range." fi #使用否定操作符!时,test命令内部使用的圆括号,必须使用引号或者转义,否则会被 Bash 解释
循环
- while循环shell
while condition; do commands done #condition条件满足, 则会一直循环
- until循环shell
until condition; do commands done #与while想反, condition条件不满足, 则会一直循环
- for...in循环shell
for variable in list; do commands done #list为数组, variable为list的每一项
- for循环shell
for (( i=0; i<5; i=i+1 )); do echo $i done
- breakshell
for number in 1 2 3 4 5 6 do echo "number is $number" if [ "$number" = "3" ]; then break fi done #$number等于3时, 执行break, 会立即结束循环不在执行, 输出: 1 2 3
- continueshell
for number in 1 2 3 4 5 6 do if [ "$number" = "3" ]; then continue fi echo "number is $number" done #$number等于3时, 执行continue, 会立即结束本轮循环, 继续执行后面的循环, 输出: 1 2 4 5 6
- select结构shell
select name in list do commands done #Bash 会对select依次进行下面的处理: # 1. select生成一个菜单,内容是列表list的每一项,并且每一项前面还有一个数字编号。 # 2. Bash 提示用户选择一项,输入它的编号。 # 3. 用户输入以后,Bash 会将该项的内容存在变量name,该项的编号存入环境变量REPLY。如果用户没有输入,就按回车键,Bash 会重新输出菜单,让用户选择。 # 4. 执行命令体commands。 # 5. 执行结束后,回到第一步,重复这个过程
函数
- 创建函数shell
fn() {} 或 function fn() {} #两种方式是等价的
- 删除函数shell
unset -f functionName
- 查看函数shell
declare -f #查看当前 Shell 定义的所有函数 declare -f functionName #查看当前 Shell 定义的指定的函数 declare -F #查看当前 Shell 定义的函数名,不含函数体
- 参数变量shell
#函数的参数变量与脚本参数变量是一致的
- returnshell
#return命令用于从函数返回一个值。函数执行到这条命令,就不再往下执行了,直接返回了
- 全局变量和局部变量 local 命令shell
#函数内直接声明的变量,属于全局变量,整个脚本都可以读取 #local命令声明的变量,只在函数体内有效,函数体外没有定义
数组
- 创建数组shell
#数组可以采用逐个赋值的方法创建, 也可采用一次性赋值 array[0] = 0 array[1] = 1 array[2] = 2 或 array = (0 1 2) array=([2]=c [0]=a [1]=b) #指定下标赋值 mp3s=( *.mp3 ) #可以使用通配符
- 读取数组shell
echo ${array[0]} #读取数组下标为0的值 echo ${foo[@]} #@和*是数组的特殊索引,表示返回数组的所有成员 #在for in循环中一定要用双引号, 否则会出错 for i in "${names[@]}"; do echo $i done #拷贝数组,添加新成员 hobbies=( "${activities[@]}" diving )
- 数组长度shell
${#array[*]} ${#array[@]} #上面命令均可获取数组长度 echo ${#a[100]} #返回数组第100项的字符串的长度
- 提取数组序号shell
${!array[@]} 或 ${!array[*]} #可以返回数组的成员序号,即哪些位置是有值的
- 提取数组成员shell
echo ${food[@]:index:length} #提取指定起始位置和长度的数组成员, index起始下标, length要提取的成员数量
- 追加数组成员shell
foo+=(d e f) #可使用+=运算符直接添加
- 删除数组shell
unset foo[2] #删除数组下标为2的一项 foo[1]='' #设置为空, 可以隐藏该项, 不会删除 unset foo #清空数组
- 关联数组shell
#使用declare -A声明关联数组 declare -A colors colors["red"]="#ff0000" colors["green"]="#00ff00" colors["blue"]="#0000ff" #访问数组的某一项 echo ${colors["blue"]}
set shopt
- setshell
set -u 或 set -o nounset #脚本在头部加上它,遇到不存在的变量就会报错,并停止执行 set -x 或 set -o xtrace #运行结果之前,先输出执行的那一行命令 set +x #关闭命令输出
- Bash 的错误处理shell
command || exit 1 #只要command有非零返回值(命令执行失败 ),脚本就会停止执行 #如果停止执行之前需要完成多个操作, 可采用下面的方式: # 写法一 command || { echo "command failed"; exit 1; } # 写法二 if ! command; then echo "command failed"; exit 1; fi # 写法三 command if [ "$?" -ne 0 ]; then echo "command failed"; exit 1; fi #命令有继承关系, 前一个命令成功才执行后一个命令 command1 && command2 set -e 或 set -o errexit #只要发生错误, 就终止执行 set +e #关闭-e command || true #使得该命令即使执行失败,脚本也不会终止执行 set -o pipefail #只要一个子命令失败,整个管道命令就失败,脚本就会终止执行 set -E #设置了-e会导致函数内错误不会被trap捕获, 使用-E可以纠正这个行为
- 其他参数shell
set -n 或 set -o noexec #运行命令,只检查语法是否正确。 set -f 或 set -o noglob #示不对通配符进行文件名扩展。 set -v 或 set -o verbose #表示打印 Shell 接收到的每一行输入。 set -o noclobber #防止使用重定向运算符>覆盖已经存在的文件 # -f 和 -v 可以分别使用 +f 和 +v 关闭
- set 总结shell
# 写法一 set -Eeuxo pipefail # 写法二 set -Eeux set -o pipefail #上面写法建议放在shell脚本的头部
- shoptshell
shopt #可以查看所有参数,以及它们各自打开和关闭的状态 shopt globstar #指定参数名, 查看该参数的状态 #命令参数如下: -s #用来开启某个参数的状态 -u #用来关闭某个参数的状态 -q #查询某个参数是否打开, 返回状态为0表示打开, 为1表示关闭
脚本除错
- 常见错误shell
[[ -d $dir_name ]] && cd $dir_name && rm * #进入某目录进行文件删除操作, 该避免目录为空, 导致删除了所有文件
- bush -x参数shell
#! /bin/bash -x #在执行脚本内的命令前, 会打印该命令
mktemp trap
- 临时文件的安全问题shell
#直接创建临时文件,尤其在/tmp目录里面,往往会导致安全问题 #首先,/tmp目录是所有人可读写的,任何用户都可以往该目录里面写文件。创建的临时文件也是所有人可读的 #其次,如果攻击者知道临时文件的文件名,他可以创建符号链接,链接到临时文件,可能导致系统运行异常。攻击者也可能向脚本提供一些恶意数据。因此,临时文件最好使用不可预测、每次都不一样的文件名,防止被利用 #最后,临时文件使用完毕,应该删除。但是,脚本意外退出时,往往会忽略清理临时文件 #生成临时文件应该遵循下面的规则: #创建前检查文件是否已经存在。 #确保临时文件已成功创建。 #临时文件必须有权限的限制。 #临时文件要使用不可预测的文件名。 #脚本退出时,要删除临时文件(使用trap命令)。
- mktempshell
#mktemp命令就是为安全创建临时文件而设计的。虽然在创建临时文件之前,它不会检查临时文件是否存在,但是它支持唯一文件名和清除机制,因此可以减轻安全攻击的风险 mktemp #创建一个随机名的临时文件,仅本人可读写 TMPFILE=$(mktemp) || exit 1 #为了确保临时文件创建成功,mktemp命令后面最好使用 OR 运算符(||),保证创建失败时退出脚本 trap 'rm -f "$TMPFILE"' EXIT #为了保证脚本退出时临时文件被删除,可以使用trap命令指定退出时的清除操作 #命令参数如下: -d #创建一个临时目录 -p #指定临时文件所在的目录。默认是使用$TMPDIR环境变量指定的目录,如果这个变量没设置,那么使用/tmp目录 -t #指定临时文件的文件名模板,模板的末尾必须至少包含三个连续的X字符,表示随机字符,建议至少使用六个X。默认的文件名模板是tmp.后接十个随机字符
- trapshell
#trap命令用来在 Bash 脚本中响应系统信号 #注意,trap命令必须放在脚本的开头。否则,它上方的任何命令导致脚本退出,都不会被它捕获 trap -l #列出所有信号 trap [动作] [信号] ... #动作为bash脚本命令 #常用信号如下: HUP #编号1,脚本与所在的终端脱离联系。 INT #编号2,用户按下 Ctrl + C,意图让脚本终止运行。 QUIT #编号3,用户按下 Ctrl + 斜杠,意图退出脚本。 KILL #编号9,该信号用于杀死进程。 TERM #编号15,这是kill命令发出的默认信号。 EXIT #编号0,这不是系统信号,而是 Bash 脚本特有的信号,不管什么情况,只要退出脚本就会产生。
Bash 启动环境
- Sessionshell
#用户每次使用 Shell,都会开启一个与 Shell 的 Session(对话)。 #Session 有两种类型:登录 Session 和非登录 Session,也可以叫做 login shell 和 non-login shell。
- 登录Sessionshell
#登录 Session 是用户登录系统以后,系统为用户开启的原始 Session,通常需要用户输入用户名和密码进行登录 #登录 Session 一般进行整个系统环境的初始化,启动的初始化脚本依次如下: /etc/profile #所有用户的全局配置脚本。 /etc/profile.d #目录里面所有.sh文件 ~/.bash_profile #用户的个人配置脚本。如果该脚本存在,则执行完就不再往下执行。 ~/.bash_login #如果~/.bash_profile没找到,则尝试执行这个脚本(C shell 的初始化脚本)。如果该脚本存在,则执行完就不再往下执行。 ~/.profile #如果~/.bash_profile和~/.bash_login都没找到,则尝试读取这个脚本(Bourne shell 和 Korn shell 的初始化脚本)。 #注意: #Linux 发行版更新的时候,会更新/etc里面的文件,比如/etc/profile,因此不要直接修改这个文件。 #如果想修改所有用户的登陆环境,就在/etc/profile.d目录里面新建.sh脚本。 #如果想修改你个人的登录环境,一般是写在~/.bash_profile里面。下面是一个典型的.bash_profile文件。
- 非登录Sessionshell
#非登录 Session 是用户进入系统以后,手动新建的 Session,这时不会进行环境初始化。 #比如,在命令行执行bash命令,就会新建一个非登录 Session。 #非登录 Session 的初始化脚本依次如下: /etc/bash.bashrc #对全体用户有效。 ~/.bashrc #仅对当前用户有效。 #注意: #对用户来说,~/.bashrc通常是最重要的脚本。非登录 Session 默认会执行它,而登录 Session 一般也会通过调用执行它。每次新建一个 Bash 窗口,就相当于新建一个非登录 Session,所以~/.bashrc每次都会执行。 #执行脚本相当于新建一个非互动的 Bash 环境,但是这种情况不会调用~/.bashrc。 bash --norc #禁止在非登录 Session 执行~/.bashrc脚本 bash --rcfile testrc #指定另一个脚本代替.bashrc
- bash_logoutshell
#~/.bash_logout脚本在每次退出 Session 时执行,通常用来做一些清理工作和记录工作,比如删除临时文件,记录用户在本次 Session 花费的时间。
- 启动选项shell
#为了方便 Debug,有时在启动 Bash 的时候,可以加上启动参数 -n #不运行脚本,只检查是否有语法错误。 -v #输出每一行语句运行结果前,会先输出该行语句。 -x #每一个命令处理之前,先输出该命令,再执行该命令。
- 键盘绑定shell
#Bash 允许用户定义自己的快捷键。 #全局的键盘绑定文件默认为/etc/inputrc #你可以在主目录创建自己的键盘绑定文件.inputrc文件。 #如果定义了这个文件,需要在其中加入下面这行,保证全局绑定不会被遗漏。 $include /etc/inputrc #.inputrc文件里面的快捷键,可以像这样定义,"\C-t":"pwd\n"表示将Ctrl + t绑定为运行pwd命令。
命令提示符
- 环境变量 PS1shell
#命令提示符通常是美元符号$,对于根用户则是井号#。这个符号是环境变量PS1决定的 echo $PS1 #查看当前命令提示符的定义 #Bash 允许用户自定义命令提示符,只要改写这个变量即可。 #改写后的PS1,可以放在用户的 Bash 配置文件.bashrc里面,以后新建 Bash 对话时,新的提示符就会生效。 #要在当前窗口看到修改后的提示符,可以执行下面的命令。 source ~/.bashrc #命令提示符的定义,可以包含特殊的转义字符,表示特定内容: \a #响铃,计算机发出一记声音。 \d #以星期、月、日格式表示当前日期,例如“Mon May 26”。 \h #本机的主机名。 \H #完整的主机名。 \j #运行在当前 Shell 会话的工作数。 \l #当前终端设备名。 \n #一个换行符。 \r #一个回车符。 \s #Shell 的名称。 \t #24小时制的hours:minutes:seconds格式表示当前时间。 \T #12小时制的当前时间。 \@ #12小时制的AM/PM格式表示当前时间。 \A #24小时制的hours:minutes表示当前时间。 \u #当前用户名。 \v #Shell 的版本号。 \V #Shell 的版本号和发布号。 \w #当前的工作路径。 \W #当前目录名。 \! #当前命令在命令历史中的编号。 \# #当前 shell 会话中的命令数。 \$ #普通用户显示为$字符,根用户显示为#字符。 \[ #非打印字符序列的开始标志。 \] #非打印字符序列的结束标志。 #举例来说,[\u@\h \W]\$这个提示符定义,显示出来就是[user@host ~]$(具体的显示内容取决于你的系统): [user@host ~]$ echo $PS1 [\u@\h \W]\$ #改写PS1变量,就可以改变这个命令提示符 $ PS1="\A \h \$ " 17:33 host $ #注意,$后面最好跟一个空格,这样的话,用户的输入与提示符就不会连在一起
- 颜色shell
#默认情况下,命令提示符是显示终端预定义的颜色。Bash 允许自定义提示符颜色 #使用下面的代码,可以设定其后文本的颜色: \033[0;30m #黑色 \033[1;30m #深灰色 \033[0;31m #红色 \033[1;31m #浅红色 \033[0;32m #绿色 \033[1;32m #浅绿色 \033[0;33m #棕色 \033[1;33m #黄色 \033[0;34m #蓝色 \033[1;34m #浅蓝色 \033[0;35m #粉红 \033[1;35m #浅粉色 \033[0;36m #青色 \033[1;36m #浅青色 \033[0;37m #浅灰色 \033[1;37m #白色 #举例来说,如果要将提示符设为红色,可以将PS1设成下面的代码 PS1='\[\033[0;31m\]<\u@\h \W>\$' #但是,上面这样设置以后,用户在提示符后面输入的文本也是红色的。为了解决这个问题, 可以在结尾添加另一个特殊代码\[\033[00m\],表示将其后的文本恢复到默认颜色。 PS1='\[\033[0;31m\]<\u@\h \W>\$\[\033[00m\]' #除了设置前景颜色,Bash 还允许设置背景颜色。 \033[0;40m #蓝色 \033[1;44m #黑色 \033[0;41m #红色 \033[1;45m #粉红 \033[0;42m #绿色 \033[1;46m #青色 \033[0;43m #棕色 \033[1;47m #浅灰色 #下面是一个带有红色背景的提示符: PS1='\[\033[0;41m\]<\u@\h \W>\$\[\033[0m\] '
- 环境变量 PS2,PS3,PS4shell
#环境变量PS2是命令行折行输入时系统的提示符,默认为> #环境变量PS3是使用select命令时,系统输入菜单的提示符 #环境变量PS4默认为+。它是使用 Bash 的-x参数执行脚本时,每一行命令在执行前都会先打印出来,并且在行首出现的那个提示符
CURL命令
- 获取网页源码shell
curl <网页地址> #常用参数如下: -o <filename> #缓存网页源码到指定文件 -i #显示网页头部信息(headers), 包括网页代码 -I #只显示头部信息 -v #显示http通信的整个过程 -X (GET | POST | PUT | DELETE) #默认 HTTP 动词为 GET,使用参数 -X 可以支持其他的动词类型 -H "Content-Type:application/json" #添加头部信息 --user name:password #http需要认证的时候使用 -d #携带参数:key/value,适用POST PUT DELETE -G #携带的参数将放在url后面 -k #允许连接到没有证书(H)的SSL站点 -# #将传输进度显示为进度条