01 - 介绍
查看操作系统当前支持的 Shell
cat /etc/shells
显示以下结果:
/bin/bash
/bin/csh
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh
/bin/sh 是最早的 Shell ,仍然在 unix 和 unix-like 的操作系统上使用。 其中 /bin/bash 就是我们要学习的,它是 /bin/sh 的升级版本,直观且灵活,目前是标准的 GNU Shell ,大部分的 Shell 可运行在 Linux 和 Mac 操作系统上运行。
看看 bash 在哪
which bash
返回:
/bin/bash
创建第一个 Shell Script
执行以下命令,会在当前目录下创建一个文件 01-introduction.sh ,这个文件的后缀是 .sh 其实没有这个后缀我们的脚本也能正常运行,但是为了规范并方便 IDE 识别,一般都会使用该后缀。
touch 01-introduction.sh
我们修改这个文件为以下内容:
#! /bin/bash
echo "hello,world"
第一行: #! /bin/bash 其实就是我们刚才查看的bash位置,让解释器知道这是什么脚本;
第二行: echo "hello,world" 在控制台输出。如果输出的字符是变量名使用单引号可原样输出,使用 echo -e "hello,world\n" 会开启转义模式,会正确识别 \n 。
第一行其实也可以输入别的开头,具体可见 What is the difference between “#!/usr/bin/env bash” and “#!/usr/bin/bash”?
在当前目录下执行这个脚本:
./01-introduction.sh
提示 zsh: permission denied: ./01-introduction.sh 原因是通过 touch 创建的文件只能读写,没有可执行权限。我们 chmod +x 01-introduction.sh 增加执行权限后,再次执行 ./01-introduction.sh 发现成功出现了 hello,world 。
其实这里也可以直接使用 sh 01-introduction.sh 来执行,不会出现权限的问题。
02 - 使用变量和注释
上次我们学习了如何创建一个最简单的脚本,这节来学习下如何使用变量和注释。还是以上次的脚本为例。
先来看注释
#! /bin/bash
# 我是单行注释
<< COMMENT
我是多行注释,注意:使用相同的开头和结尾
COMMENT
echo "hello,world"
<< COMMENT2
你好啊
COMMENT2
echo "hello,world"
执行这段脚本后,输出
hello,world
hello,world
可以看到,单行注释使用 # ,多行注释使用 << 后面在带一个字符,只要前后一致闭合,之间包含的内容 Shell 就认为是注释。
内置变量和自定义
为了方便说明,以下的输出全部以注释的形式展示:
echo $BASH #/bin/sh
echo $BASH_VERSION #3.2.57(1)-release
echo $HOME #/Users/pleuvoir
echo $PWD #/Users/pleuvoir/dev/space/shell-tutorial
name="pleuvoir" #注意:变量名和值=之间不要有空格,否则无法识别
echo my name is $name #my name is pleuvoir
1age=18 #特别注意:变量名称不能以数字开头,否则无法识别
echo $1age #age 预期18,但是这里输出的是age
03 - 接收用户输入
换行输入
# 输入框会换行
echo "Enter your username:"
read username
echo "your name is $username"
输入框会换行,一次可以输入多个
echo "Enter your usernames:"
read username1 username2 username3
echo "username1=$username1,username2=$username2,username3=$username3"
输入框不换行
read -p "enter your new name:" newname_val
echo "newname=$newname_val"
安静输入,不显示输入内容
read -sp "enter your pwd :" newpwd_val
echo "newpwd_val=$newpwd_val"
同样也可以在一行中接收多个内容
echo "enter names:"
read -a infos_val
echo "name1=${infos_val[0]},name2=${infos_val[1]},name3=${infos_val[2]}"
没有定义变量名,则可以使用内置的REPLY
read -p "hello:"
echo $REPLY
04 - 解析命令行参数
# 原生的,第一个参数是脚本文件名
echo $0 $1 $2 '$0 $1 $2' #输出 01-introduction.sh 1 2 $0 $1 $2
# 使用数组(推荐使用)
args=($@)
echo "${args[0]},${args[1]},${args[2]}" #1,2,3
# 输出所有值 sh 01-introduction.sh 1 2 3 则输出 1 2 3
echo $@ #1 2 3
echo $# #传入参数的个数,一个也没有为0
05 - 条件语句
基本语法
if [[ condition ]]; then
#statements
elif [[ condition ]]; then
#statements
fi
运算符
Integer 类型
-eq 等于
-ne 不等于
-gt 大于
-ge 大于等于
-lt 小于
<
<=
>
>=
字符串类型
=
== 推荐使用
!=
< 比较 ASCII
> 比较 ASCII
-z 长度为0返回true
-n 字符串不为空(长度大于0)返回true
示例
#判断字符串为空
empty=
if [[ -z $empty ]]; then
echo "empty is null"
fi
empty2=""
if [[ -z $empty2 ]]; then
echo "empty2 is null"
fi
empty2=" " #注意这里加了空格就不是空了
if [[ ! -z $empty2 ]]; then
echo "empty2 is not null"
fi
# 判断字符串的情况
inputnum="b"
if [[ $inputnum == "a" ]]; then
echo "inputnum is a"
elif [[ $inputnum == "b" ]]; then
echo "inputnum is b"
else
echo "inputnum is else number"
fi
# 判断数字的情况
word=10
if [[ $word -eq 10 ]]; then
echo "word is 10"
fi
需求:字符串如果为空则报错,可以通过 ! -n 或者 -z 。
条件判断表达式
# 与
uage=29
if [[ $uage -gt 15 && $uage -lt 23 ]]; then
echo "valid age is $uage"
else
echo "oh my god."
fi
#或
if [[ $uage -gt 50 || $uage -lt 35 ]]; then
echo "OR valid age is $uage"
else
echo "OR oh my god."
fi
#分开写也是可以的
if [[ $uage -gt 50 ]] || [[ $uage -lt 35 ]]; then
echo "OR valid age is $uage"
else
echo "OR oh my god."
fi
#case
vehicle=bike1
case $vehicle in
"car" )
echo "car";;
"bike" )
echo "bike";;
"ship" )
echo "ship";;
* )
echo "unknown"
esac
06 - 文件操作
检查文件是否存在
echo "current pwd is $PWD"
read -p "Enter filename:" filename
if [[ -e $filename ]]; then #注意这个-e是文件运算当文件存在返回true,还有很多其他的可以参考运算符大全
echo "this file $filename exist"
else
echo "this file $filename not exist"
fi
这里其实没什么好说的,就是判断文件存不存在。关键点就在 -e 这个操作上,而这只是运算符中的一种,大家需要什么命令可以先去检索一下。
追加写文件
echo "current pwd is $PWD"
read -p "Enter filename:" filename
if [[ -e $filename ]]; then
echo "this file $filename exist"
if [[ -w $filename ]]; then
echo "type something text, quit ctrl+d"
cat >> $filename #>>是追加写文件,在最后一个字符后追加,不会换行;>是全覆盖写
else
echo "no write permission" #可以通过执行 chmod -w filename 把写权限去掉
fi
else
echo "this file $filename not exist"
fi
07 - 数学运算
a=10
b=2
echo "a+b=$((a+b))"
echo "a-b=$((a-b))"
echo "a*b=$((a*b))"
echo "a/b=$((a/b))"
echo "a%b=$((a%b))"
08 - 数组
test=('windows' 'ubuntu' 'linux')
test[3]="mac"
unset test[2] #移除
echo "${test[@]}" #全部
echo "${!test[@]}" #索引
echo "${test[0]}" #windows
#也可以把字符串当做数组
strings="hwjklrnjw"
echo "${strings[@]}"
echo "${strings[0]}" #整个就是0
09 - 循环
args=0
while [[ $args -lt 10 ]]; do
echo "$args"
(( ++args ))
sleep 2
done
# 类似do while
until [[ $args -gt 5 ]]; do
echo "$args"
(( ++args ))
sleep 2
done
#fori
for (( i = 0; i < 10; i++ )); do
if [[ $i%2 -eq 0 ]]; then
echo "mod 2 zero"
continue #可以continue也可以break
fi
echo "$i"
done
#打印当前目录下所有内容
for item in *; do
echo $i
done
# 列表循环
for i in {1,5,10}; do
echo "$i"
done
10 - 函数
name=pleuvoir
function print(){
echo "out_name is $name"
local name=$1 #注意这里要加上local 否则会被认为是全局变量
echo "local_name is $name"
}
echo "out_name is $name"
print "hehe" #传递参数进去
echo "out_name is $name"
#判断文件是否存在的示例
usage(){
echo "输入文件位置"
echo "$0 filename"
}
is_file_exits(){
local file=$1
echo "file is $file"
[[ -f $file ]] && return 0 || return 1 #三目运算符
}
[[ $# -eq 0 ]] && usage #当没有传递参数时调用帮助方法
if ( is_file_exits $1 ); then
echo "$1 存在"
else
echo "$1 不存在"
fi
11 - 监听Signal
echo "PID is $"
# 可以监听多个信号
trap "echo hehe;exit 0" 15 2
for (( i = 0; i < 10; i++ )); do
echo $i
sleep 10
done
exit 0