Shell类型
shell种类特别的多,一般在Linux下默认都是bash,我们可以通过cat /etc/passwd查看每个用户默认的shell。同时我们在工作中可能会用到zsh,其实zsh是兼容bash的。
一般在shell下执行的命令有两种:
- 内建命令
- 非内建命令
内建命令在执行时一般不会fork出子shell,内建命令有cd、alias、exit,一般我们通过which cmd可以查出命令是不是内建命令。不管是内建命令,还是非内建命令,我们都可以通过$?来获取命令的返回码(返回吗一般是0表示成功,非0表示失败)。
Shell如何执行命令
执行命令我们一般是分单条执行还是多条执行。
单条执行
其实就是我们常用的方式,只不过是如果是内建命令就不会fork而已,非内建命令会fork/exec/wait。
多条执行
其实就是我们说写的脚本(批处理),把多条命令放到一个文件中,然后一块执行。
比如有如下一个脚本,文件名为:script.sh:
#! /bin/sh
cd ..
ls
复制代码
一般有两种执行方式:
- 以一种可执行文件的方式来执行。
chmod +x script.sh然后./script.sh就可以执行了。这种执行方式的大概流程为:先fork出一个子shell,在子shell执行通过exec来执行该脚本中第一行所指定的/bin/sh的解释器(相当于将sh的代码段加载到这个子进程中来),而当前script.sh是作为一个命令行参数来传递给sh的。当执行到cd ..,由于cd是内建命令,相当于sh内部调用一个函数来改变当前子shell的工作路径;然后再执行ls,由于ls不是内建命令,所以会再fork出一个子进程执行ls,子shell调用wait等待ls进程执行完,当ls的进程执行完之后,子shell的wait会解除,由于脚本中的代码已经执行完了,所以主shell中wait子shell也解除,因此我们就能看到shell提示符了。
- 直接通过解释器来执行
也就是这种sh ./script.sh方式。我们会发现,其实两种方式的本质还是一样的。
我们应该能发现在脚本中有一句cd ..,但是脚本执行完之后,我们的工作目录本没有任何改变,这是为啥?这是因为修改的是子shell的工作目录,当前主shell的目录并没有修改呀。
- 执行脚本时能不能先不要fork出子shell
答案肯定是可以的。比如我们执行以下的方式source ./script.sh或者. ./script.sh,这种方式就不会先fork出子shell来执行,但是到单个命令时还是会fork。这里可以联想到我们平时在定义环境变量时脚本,我们执行该脚本时都用source script.sh的原因。因为执行不会fork,所以环境变量就是针对当前的shell进程的;如果执行之前的方式执行,仅仅修改的子shell的环境变量,主shell环境变量并没有任何变化。
另外要注意,通过(cd ..; ls -l)这样也是会fork的。
Shell基本语法
变量
在shell中变量分成本地变量、环境变量。本地变量只能在当前的shell进程中使用,而环境变量可以在当前的shell进程的子进程中使用。
- 本地变量
本地变量的定义方式:
VALNAME=value
复制代码
等号两边不能有空格。获取获取变量的值呢:
echo ${VALNAME}
复制代码
在shell中所有变量的类型都是字符串类型,如果变量没有定义就是空字符串。
- 环境变量
将本地变量导出就是环境变量,可以通过命令export VALNAME。也可以一边定义一边导出:
export AAA=value
复制代码
查看环境变量,可以通过env或者printenv
- 如何使用变量
使用变量推荐使用:
echo ${SHELL}
复制代码
也就是大括号的方式。这样的好处就是后边跟一些字符串,变量展开以后也是可以拼接上的。比如echo ${SHELL}abc,那么结果就是/bin/zshabc
文件名代换globbing
其实就是一些通配符。
| 通配符 | 作用 |
|---|---|
| * | 匹配0个或者多个任何字符 |
| ? | 匹配1个任意字符 |
| [] | 匹配方括号中其中一个字符 |
比如我们执行ls ch1[0-2].doc,那么shell其实会先展开通配符,比如能找到ch10.doc、ch11.doc,然后将这两个文件传递给ls。也就是说ls并不会不处理这些通配符,是由shell先展开再传递给ls。 |
命令行代换``、$()
通过``或者$()括起来的也是一条命令,shell会先执行这条命令,将执行结果放到当前所在的命令行中。
DATE=`date`
echo $DATE
或者
DATE=$(date)
echo $DATE
复制代码
算术代换:$(())
shell中变量默认都是字符串,所以可以涉及到整形变量+、-、*、/可以这样处理:
val=10
echo $(($val + 10))
复制代码
$(())只能用于整形运算
转义字符\
有一些特殊的字符如果我们想使用这些特殊字符的字面量时,就可以使用转义。
echo \$SHELL结果就不打印SHELL变量了,而是打印$SHELL。
另外\也可以表示续行的意思。
单引号、双引号
单引号表示字符的字面值。双引号一般情况下都表示字符的字面值,但是在遇到$变量名则会展开变量值;在遇到``则会命令替换。
bash启动脚本
就是bash启动时执行的脚本,这里边的规则还是挺复杂的,但是只需要记住一条,我们可以将环境变量、alias、mask定义到.bashrc文件中即可。这样在bash启动时会自动source启动脚本,这样我们预先定义的变量就自动生效了。
shell脚本语法
条件测试
条件测试语句在shell中有两种表达[、test。比如
VAR=2
test $VAR -gt 1$
[ $VAR -gt 3 ]
复制代码
不管用那种方式,最终表示条件是真还是假,是通过$?来判断的,0表示true,1表示false。
对于与或非
判断语句
在shell编程中是可以使用if语句的,但是跟我们的c语言的if也不太一样。
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
复制代码
其实涉及三条语句,if [ -f ~/.bashrc ] 如果测试语句$?为0则表示为true,否则为false。then . ~/.bashrc在shell一般一行只有一条语句,如果要有多条语句的话,就用;分离。fi 表示if的结束标记。
特殊if语句,也就是if的条件永远为true。:是空语句,执行结果$?是0。
if :; then echo "always true"; fi
复制代码
常见的if用法为:
num=1
if [ $(($num)) -gt 10 ]; then
echo 'a'
elif [ ${num} -eq 10 ]; then
echo 'b'
else
echo 'c'
fi
复制代码
&&、||用法,其实就是利用短路特性。
test "$(whoami)" != 'root' && (echo you are using a non-privileged account; exit 1)
复制代码
case语句
case语句就是c语言中的switch case。大概的用法是这样:
case $1 in
start)
...
;;
stop)
...
;;
reload | force-reload)
...
;;
restart)
...
;;
*)
log_success_msg "Usage: /etc/init.d/apache2 {start|stop|restart|reload|force-reload|start-htcacheclean|stop-htcacheclean}"
exit 1
;;
esac
复制代码
以case开头,在in里边进行匹配,支持通配符,一旦找到某一个条件就执行对应的语句最终语句是以;;结束。整个语句是以esac结束。
for/do/done
for循环。比如
for FRUIT in apple banana pear; do
echo "I like $FRUIT"
done
复制代码
如果想将某一个目录下的文件改名,可以这样:
files=`ls test`
echo ${files}
for f in ${files}; do
mv "test/${f}" "test/${f}.tmp"
done
复制代码
while/do/done
COUNTER=1
while [ "$COUNTER" -lt 10 ]; do
echo "Here we go again"
COUNTER=$(($COUNTER+1))
done
复制代码
这个应该很好理解。
位置参数与特殊变量
可以参考如下列表:
其中位置参数可以用shift命令左移。比如shift 3表示原来的
1,原来的
2等等,原来的
2、
0不移动。不带参数的shift命令相当于shift 1。shift可以使用场景就是在脚本内部可能会调用其他的脚本,但是参数又不想用那么多,这个时候就可以使用shift进行丢弃。
函数
shell中的函数不用写参数列表跟返回值类型的。是可以传递参数跟返回值的,只不过不需要声明而已。
is_directory()
{
DIR_NAME=$1
if [ ! -d $DIR_NAME ]; then
return 1
else
return 0
fi
}
for DIR in "$@"; do
if is_directory "$DIR"
then :
else
echo "$DIR doesn't exist. Creating it now..."
mkdir $DIR > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Cannot create directory $DIR"
exit 1
fi
fi
done
复制代码
在shell脚本中如果要表示返回true,一般用return 0,这个跟if判断是有关系的。另外在shell空语句用:来表达。




近期评论