您的位置:首页 > 运维架构 > Shell

linux shell详解

2015-09-26 19:09 441 查看
shell工作原理

Linux系统提供给用户的最重要的系统程序是Shell命令语言解释程序。它不属于内核部分,而是在核心之外,以用户态方式运行。其基本功能是解释并执行用户打入的各种命令,实现用户与Linux核心的接口。系统初启后,核心为每个终端用户建立一个进程去执行Shell解释程序。它的执行过程基本上按如下步骤:
(1)读取用户由键盘输入的命令行。
(2)分析命令,以命令名作为文件名,并将其它参数改造为系统调用execve( )内部处理所要求的形式。
(3)终端进程调用fork( )建立一个子进程。
(4)终端进程本身用系统调用wait4( )来等待子进程完成(如果是后台命令,则不等待)。当子进程运行时调用execve( ),子进程根据文件名(即命令名)到目录中查找有关文件(这是命令解释程序构成的文件),将它调入内存,执行这个程序(解释这条命令)。
(5)如果命令末尾有&号(后台命令符号),则终端进程不用系统调用wait4( )等待,立即发提示符,让用户输入下一个命令,转⑴。如果命令末尾没有&号,则终端进程要一直等待,当子进程(即运行命令的进程)完成处理后终止,向父进程(终端进程)报告,此时终端进程醒来,在做必要的判别等工作后,终端进程发提示符,让用户输入新的命令,重复上述处理过程。


shell语法

变量赋值:
name=value
语法注意:=左右千万不能有空格,不然bash会报错,这个和其它语言不一样,比如javascript,php之类的,之前经常容易犯这个错误,写习惯了别的语言。

变量引用:
弱引用: "", 其内部的变量引用会被替换为变量值;
强引用:'',其变量的变量引用会保持原有字符;
命令引用:`COMMAND`, $(COMMAND),引用命令的执行结果;

声明为整型:

declare -i name=3
let name=2

运行脚本
事实上是运行一个bash进程,此进程负责从脚本文件中读取一个执行逻辑,而后由bash进程负责解析并运行此逻辑;

启动脚本:

(1) # bash /PATH/TO/SCRIPT_FILE
(2) 给shell脚本一个执行权限,
# ./PATH/TO/SCRIPT_FILE

条件测试:
test EXPRESSION
[ EXPRESSION ]
语法注意:[的右边和]的左边一定要有空格,和赋值刚好相反

整数测试:隐含着做数值大小比较,所以不要给变量引用加引用;
$A -gt $B:是否大于;是则为“真”,否则为“假”;
$A -ge $B: 是否大于等于;
$A -lt $B:是否小于;
$A -le $B: 是否小于等于;
$A -eq $B: 是否等于;
$A -ne $B:是否不等于;

字符串测试:ASCII数值越大,字符比较时其值越大;
"$A" > "$B":是否大于;
"$A" < "$B":是否小于;
"$A" == "$B":是否等于;
"$A" =~ PATTERN:模式匹配;

"$A" != "$B":是否不等于;
-z "$A":是否为空;空则为“真”,否则为“假”
-n "$A":是否不空;不空则“真”,空则为“假”

注意:引用字符串变量$A和$B比较时,变量$A和$B左右两侧都需要加"",这点有和别的语言不一样,像php判断if ($A == $B)即可,而bash必须为if [ "$A" == "$B" ],这个错误之前经常犯

文件测试:测试文件的存在性以及属性;

-e $file: 是否存在;存在则为“真”,否则为“假”;
-a $file: 同上;
-f $file:文件是否存在且为普通文件;
-d $file:文件是否存在且为目录;
-h $file:是否存在且为符号链接文件;
-L $file: 同上
-b $file:是否存在且为块设备文件;
-c $file:是否存在且为字符设备文件;
-S $file:是否存在且为套接字文件;
-p $file: 是否存在且为管道文件;

-r $file: 当前用户对文件是否拥有读权限;
-w $file:当前用户对文件是否拥有写权限;
-x $file:当前用户对文件是否拥有执行权限;

-u $file:文件是否拥有SUID权限;
-g $file:文件是否拥有SGID权限;
-k $file:文件是否拥有sticky权限;

-O $file: 当前用户是否为指定文件的属主;
-G $file: 当前用户是否为指定文件的属组;

双目操作符:
$file1 -nt $file2: file1是否新于file2, file1的最近一次的修改时间戳是否晚于file2的;
$file1 -ot $file2: file1是否旧于file2, file1的最近一次的修改时间戳是否早于file2的;
$file1 -ef $file2:file1与file2是否指向了同一个inode;测试二者是否为同一个文件的硬链接;

组合测试:

与:[ CONDITION1 -a CONDITION2 ] 例子:[ -n "$str" -a "$str" == "haha200142323" ]
或:[ CONDITION1 -o CONDITION2 ]
非:[ ! CONDITION1 ]

多个语句连环执行:

与:COMMAND1 && COMMAND2 如果COMMAND1执行成功,则执行COMMAND2
或:COMMAND1 || COMMAND2 如果COMMAND1执行不成功,则执行COMMAND2
非: ! COMMEND

特殊设备:
/dev/null: 空,bit buckets,吞下所有数据,并直接丢弃;
/dev/zero:吐出一堆0;

位置参数变量:
#bash test10.sh($0变量值) 123($1变量值) 333($2变量值)

特殊变量:
$?: 命令的状态结果;
$#: 传递给脚本或函数的参数的个数;
$*和$@: 引用传递给脚本或函数的参数列表;

算术运算:

+, -, *, /, %(求余), **(乘方)

-=, *=, /=, %=,++,--
例子:let count++,let count+=1,let count++

语法:
1.$[$A+$B], 例子:echo $[1+4],输出5
2.$(($A+$B)),例子:echo $((1+4)),输出5,我也经常用该方式运行复杂运算。比如(1+4)*5,用该方式为$(((1+4)*5)),输出25

3.let c=$a+$b

4.$(expr $a + $b) 注意:+左右两边一定都要有空格

流程控制语句

if语句
if [ CONDITION1 ]; then
片段1
elif [CONDITION2]; then
片段2
else
片段3
fi


case语句
case $VARIABLE in
PATTERN1)
语句1
;;
PATTERN2)
语句2
;;
PATTERN3)
语句3
;;
*)
语句4
;;
esac


注意PATTERN1,PATTERN2,PATTERN3可使用glob模式的通配符:
*: 任意长度的任意字符;
?: 任意单个字符;
[]: 指定范围内的任意单个字符;
a|b: 多选1;

for语句

方式一:
for i in $var; do
echo $i
done
方式二:
cnt=100
for (( i=1;i<=$cnt;i++ )); do
echo $i
done


while语句

方式一:
while [ $n -lt 100 ]; do
echo $n
done


方式二:
while read line; then
echo $line
done < /etc/passwd
该方式可以取文件中的每一行($line即为每行的内容)

until语句
i=0
until [ $i -lt 100 ]; do
echo $i
let i++
done


函数
function fname() {
语句块
}
这里我使用其他编程语言通用的方式把

函数返回的内容只可以用$?返回,但貌似只支持0-255的返回值,而且不能用=将函数返回结果赋值给变量,比如:
#!/bin/bash

function sum() {
return $(($1+$2))
}

sum 1 2

echo $?

v=$(sum 1 2)

echo $v
返回结果:
[root@localhost ~]# bash test10.sh
3

[root@localhost ~]#
由此可以看出$?能够返回函数的结果,而复制的$v变量为空。

对于$?只支持0-255返回值,我们再来一个实例
#!/bin/bash

function sum() {
return $(($1+$2))
}

sum 100 200

echo $?
返回结果:

[root@localhost ~]# bash test10.sh
44
[root@localhost ~]#
这个结果一看怎么可能为44.
为解决这个情况,我又将脚本改了下:
#!/bin/bash

declare v
function sum() {
v=$(($1+$2))
}

sum 100 200

echo $v
我们再来看看返回结果:
[root@localhost ~]# bash test10.sh
300
[root@localhost ~]#
结果正确了,我的思路是申请了一个全局变量v,再在函数中将函数结果赋值给v。

吐槽:bash对于这块实在太坑爹了,有没有更好的方法将函数返回值赋值个变量,欢迎大神告诉我一下,哈哈!

数组

申明变量:
传统数组:索引为数字,从0开始编号;
declare -a ARRAY_NAME
关联数组:索引可以自定义,可以使用任意字符串做索引;
declare -A ARRAY_NAME
支持稀疏格式

赋值:
一次只赋值一个元素
array[index]=value
一次赋值全部元素
array=("val1" "val2" ...)
指定索引进行赋值
array=([0]="val1" [3]="val2")
read -a array

获取数组长度:
${#a[@]}或者${#a[*]}

得到整个数组:
${a[@]}或者${a[*]}

读取数组指定位置:
${a[index]}

删除数组指定位置:
unset a[index]

切分数组:
${a[@]:起始位置:长度}

替换数组:
${a[@]/查找字符/替换字符}

循环数组
#!/bin/bash

a=(1 2 30 4 5)

for i in ${a[@]}; do
echo $i
done


小练习:利用bash生成10个随机数值,保存于数组中;要此些数组从大到小降序排列;
#!/bin/bash

declare -a a

declare -i sum=10

for (( i=0;i<$sum;i++ )); do
a[$i]=$RANDOM
done

count=${#a[@]}

for (( i=0;i<$count;i++ )); do
for (( j=i+1;j<$count;j++ )); do
if [ ${a[i]} -lt ${a[j]} ]; then
temp=${a[i]}
a[i]=${a[j]}
a[j]=$temp
fi
done
done

echo ${a[@]}
返回结果:
[root@localhost ~]# bash test10.sh
31870 27033 25714 22587 21793 12488 11529 7180 4051 1751
[root@localhost ~]#


read命令
作用:和用户交互输入数值到一个变量中去
-a:上面已经提到,读一个数组
-p:提示用户输入变量之前输出的字符

-t:用户输入变量的超时时间

实战演练
如果某路径不存在,则将其创建为目录;否则显示其存在,并显示内容类型;

#!/bin/bash

file=$1

if [ -z "$file" ]; then
echo "params error!";
exit 1
fi

if [ ! -e "$file" ]; then
mkdir -p $file
else
echo "$file is exist.."
file $file
fi
2.写一个脚本,完成如下功能;判断给定的两个数值,孰大孰小;给定数值的方法:脚本参数,命令交互;
#!/bin/bash

read -p 'please input two integer value:' a b

if [ -z "$a" ]; then
echo 'please input a value'
exit 1
fi

if [ -z "$b" ]; then
echo 'please input two params'
fi

if [ $a -eq $b ]; then
echo "${a} is equal to ${b}"
elif [ $a -gt $b ]; then
echo "${a} is greater than ${b}"
else
echo "${a} is smaller than ${b}"
fi
3.求100以内所有奇数之和(至少用3种方法)
#/bin/bash

declare -i sum=0

declare -i i=1

while [ $i -lt 100 ]; do
let sum+=$i
let i+=2
done

echo $sum
4.写一个脚本实现如下功能:
(1) 传递两个文本文件路径给脚本;
(2) 显示两个文件中空白行数较多的文件及其空白行的个数;
(3) 显示两个文件中总行数较多的文件及其总行数;
#!/bin/bash

read -p 'please input two file path:' file1 file2

if [ -z "${file1}" ]; then
echo "file1 param is empty.."
exit 1
fi
if [ ! -f "$file1" ]; then
echo "$file1 is not a file"
exit 2
fi

if [ -z "$file2" ]; then
echo 'file2 param is empty..'
exit 3
fi
if [ ! -f "$file2" ]; then
echo "${file2} is not a file"
exit 4
fi

kb_num1=$(grep -E '^$' $file1 | wc -l | cut -d' ' -f1)
kb_num2=$(grep -E '^$' $file2 | wc -l | cut -d' ' -f1)

total_num1=$(wc -l $file1 | cut -d' ' -f1)
total_num2=$(wc -l $file2 | cut -d' ' -f1)

if [ $kb_num1 -gt $kb_num2 ]; then
echo "the more space lines is ${file1}, space lines is ${kb_num1}"
elif [ $kb_num1 -lt $kb_num2 ]; then
echo "the more space lines is ${file2}, space lines is ${kb_num2}"
else
echo "${file1} space lines is equal to ${file2}. and the space lines is ${kb_num1}";
fi

if [ $total_num1 -gt $total_num2 ]; then
echo "the more total lines is ${file1}, total lines is ${total_num1}"
elif [ $kb_num1 -lt $kb_num2 ]; then
echo "the more total lines is ${file2}, total lines is ${total_num2}"
else
echo "${file1} total lines is equal to ${file2}. and the total lines is ${total_num1}";
fi


5.写一个脚本
(1) 提示用户输入一个字符串;
(2) 判断:
如果输入的是quit,则退出脚本;
否则,则显示其输入的字符串内容;
#!/bin/bash

read -p 'please input a string:' str

if [ -z "$str" ]; then
echo 'the string can not be empty'
exit 1
fi

if [ "$str" == "quit" ]; then
exit 0
else
echo $str
fi


6.写一个脚本,打印2^n表;n等于一个用户输入的值
#!/bin/bash

read -p "please input a integer value:" -n1 n

for (( i=0;i<=$n;i++ )); do
let cv=2**$i
echo "2^${i}=${cv}"
done


7.写一个脚本,写这么几个函数:
函数1、实现给定的两个数值的之和;
函数2、取给定两个数值的最大公约数;
函数3、取给定两个数值的最小公倍数;关于函数的选定、两个数值的大小都将通过交互式输入来提供。
#!/bin/bash
declare -i totalvalue
declare -i my
declare -i mb
function add() {
totalvalue=$(($1+$2))
}

#求最大公约数
function maxyin() {
declare -i m
if [ $1 > $2 ]; then
m=$2
else
m=$1
fi
while [ $m -gt 1 ]; do
if [ $(($1 % $m)) -eq 0 ] && [ $(($2 % $m)) -eq 0 ]; then
my=$m
break;
fi
let m--
done
my=1
}
#求最小公倍数
function minbei() {
declare -i n
if [ $1 > $2 ]; then
n=$1
else
n=$2
fi

while true; do
if [ $(($n % $1)) -eq 0 ] && [ $(($n % $2)) -eq 0 ]; then
mb=$n
break;
fi
let n++
done
}

read -p "please input two integer value to calculate the sum:" i1 j1

add $i1 $j1

echo "${i1}+${j1}=${totalvalue}"

read -p "please input two integer value to get max gongyueshu:" i2 j2

maxyin $i2 $j2

echo "${i2} with ${j2} the max gongyueshu is ${my}"

read -p "please input two integer value to get min beishu:" i3 j3

minbei $i3 $j3

echo "${i3} with ${j3} the min beishu is ${mb}"
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux shell