shell能够在直接在命令行下输入,也能够保存成shell脚本文件运行。当命令简单而且不须要重复使用,在命令行输入直接执行便可,不然就写成脚本。shell脚本默认文件扩展名为.sh
。在shell脚本中,写入的内容,会默认当成一条命令来执行。html
例如:linux
#!/bin/bash echo 'hello world'
将上面的代码存为test.sh,并将可执行权限赋予它chmod +x test.sh
,执行./test.sh
运行脚本。shell
上面的脚本将会输出:编程
hello world数组
这和在命令行或者终端模拟器下输入echo 'hello world'
并按下回车获得的结果是同样的bash
和全部的编程语言同样,shell也有注释,在shell中,#号和它后面的内容来表示一个注释:编程语言
# Print a message echo "I'm a shell script."
echo用于向输出流输出内容,例如:函数
echo "hello world"
read用于输入一条内容:.net
read input echo $input
上面的代码中,read命令从输入流读取一个值并赋予input,而后将input的内容打印出来命令行
变量的命名规则和C语言差很少,支持英文字母和下划线。shell中变量名前不须要声明类型,变量名后面不能有空格,例如:
var1='hello' var2=90
$后接变量名意为读取一个变量的值,例如:
var="hello" echo $var
也能够用${var}
方式访问到变量值,例如:
var="hello" echo ${var}
访问变量的时候
$var
和${var}
是等效的,推荐后者来访问一个变量
没有任何命令修饰的变量是一个全局变量,全局变量在同一个shell会话中都是有效的。
function func(){ a=90 } func echo $a
输出:
90
$ a=90 $ echo ${a} $ bash $ echo ${a}
输出:
90
空值
local命令用于声明一个局部变量
function func(){ local a=90 } func echo $a
输出:
空值
用export命令修饰的变量称为环境变量,在父shell会话中声明一个环境变量,子shell中均可以访问
$ export path="/system/bin" $ bash #建立一个新的shell会话 $ echo ${path}
变量 | 含义 |
---|---|
$0 | 当前脚本的文件名 |
$n(n≥1) | 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是 $1,第二个参数是 $2 |
$# | 传递给脚本或函数的参数个数 |
$* | 传递给脚本或函数的全部参数 |
$@ | 传递给脚本或函数的全部参数 |
$? | 上个命令的退出状态,或函数的返回值 |
$$ | 当前 Shell 进程 ID。对于 Shell 脚本,就是这些脚本所在的进程 ID |
#!/bin/bash for val in "$*" do echo "\$@ : ${val}" done for val in "$@" do echo "\$* : ${val}" done
假如将上面的代码存为test.sh
,运行./test.sh 1 2 3
将输出:
$@ : 1 2 3 $* : 1 $* : 2 $* : 3
` 这个符号,在键盘上的位置是在Esc键的下方
ret=`pwd` echo ${ret}
在 ` 包裹起来的命令中,也能够访问到变量
path='/' ret=`ls -l ${path}` echo ${ret}
ret=$(pwd) echo ${ret}
用$(command)这种方式也能够访问到变量
path='/' ret=$(ls -l ${path}) echo ${ret}
上面的例子中,若是想打印命令结果中的换行符,则:
path='/' ret=$(ls -l ${path}) echo "${ret}"
$(command)方式来执行命令更加直观,可是要注意,$(command) 仅在 Bash Shell 中有效,而反引号可在多种 Shell 中均可使用
shell有三种方式能够表示字符串
str=hello echo ${str}
输出:
hello
这种方式的字符串遇到空格就会被终止
str=hello echo '${str}'
输出:
${str}
单引号里的内容是字符串原始的样子,不存在转义
str=shell echo "${str}:\"hello wolrd\""
输出:
shell:"hello world"
双引号中能够访问变量和转义
str="hello" echo ${#str}
输出:
5
两个变量放在一块儿访问就能够拼接
a='hello' b='world' c=${a}${b} echo ${c}
输出:
helloworld
也能够这样
echo 'hello'"world"
(1) 从左边开始截取字符串,格式:${string: start :length}
,length可省略,省略时,是截取到字符串末尾
msg="hello world" echo ${msg: 6: 5}
输出:
world
(2) 在指定位置截取字符
${string#*chars}
其中,string 表示要截取的字符,chars 是指定的字符(或者子字符串),是通配符的一种,表示任意长度的字符串。chars连起来使用的意思是:忽略左边的全部字符,直到碰见 chars(chars 不会被截取)。
截取最后一次出现chars的位置后面的内容:${string##*chars}
使用 % 截取左边字符
使用%号能够截取指定字符(或者子字符串)左边的全部字符,具体格式以下:
${string%chars*}
请注意 * 的位置,由于要截取 chars 左边的字符,而忽略 chars 右边的字符,因此 * 应该位于chars的右侧。其余方面%和#的用法相同
运算符 | 做用 |
---|---|
+ | 加(须要结合expr命令使用) |
- | 减(须要结合expr命令使用) |
* | 乘(须要结合expr命令使用) |
/ | 除(须要结合expr命令使用) |
% | 求余(须要结合expr命令使用) |
= | 赋值 |
== | 判断数值是否相等,须要结合[] 使用 |
!= | 判断数值是否不相等,须要结合[] 使用 |
a=8 b=4 echo "a=$a,b=$b" var=`expr ${a} + ${b}` echo "加法结果:${var}" var=`expr ${a} - ${b}` echo "减法结果:${var}" # 注意:乘号须要转义 var=`expr ${a} \* ${b}` echo "乘法结果:${var}" var=`expr ${a} / ${b}` echo "除法结果:${var}" var=`expr ${a} % ${b}` echo "求余结果:${var}" var=$[${a} == ${b}] echo "是否相等:${var}" var=$[${a} != ${b}] echo "是否不相等:${var}"
输出:
a=8,b=4 加法结果:12 减法结果:4 乘法结果:32 除法结果:2 求余结果:0 是否相等:0 是否不相等:1
上面的例子中,调用expr命令和使用[]
,获得表达式的值,并将它们输出
请注意表达式两边的空格,shell中表达式两边要有空格
运算符 | 做用 |
---|---|
-eq | 全称:Equal,判断两个数是否相等 |
-ne | 全称:Not equal,判断两个数是否不相等 |
-gt | 全称:Greater than,判断前面那个数是否大于后面那个数 |
-lt | 全称:Less than,判断前面那个数是否小于后面那个数 |
-ge | 全称:Greater equal,判断前面那个数是否大于等于后面那个数 |
-le | 全称:Less than,判断前面那个数是否小于等于后面那个数 |
运算符 | 做用 |
---|---|
! | 非运算 |
-o | 或运算 |
-a | 并运算 |
运算符 | 做用 |
---|---|
&& | 逻辑并 |
|| |
逻辑或 |
&&
链接起来的两个命令,前面的执行失败就不执行后面的命令cd /bin && ls /bin
其实和C语言中的是差很少的,只要前面的条件不知足,后面那个就不用去执行它了
||
链接起来的两个命令,前面的执行失败才会执行后面的命令cd /bin || ls /bin
在这里顺便说一下;
这个运算符,这个运算符用于链接多个语句,使多个语句可以在同一行。用;
链接起来的语句,无论前面的执行成不成功,都会执行后面的
mkdir luoye;cd luoye;pwd
运算符 | 做用 |
---|---|
-e | 判断对象是否存在 |
-d | 判断对象是否存在,而且为目录 |
-f | 判断对象是否存在,而且为常规文件 |
-L | 判断对象是否存在,而且为符号连接 |
-h | 判断对象是否存在,而且为软连接 |
-s | 判断对象是否存在,而且长度不为0 |
-r | 判断对象是否存在,而且可读 |
-w | 判断对象是否存在,而且可写 |
-x | 判断对象是否存在,而且可执行 |
-O | 判断对象是否存在,而且属于当前用户 |
-G | 判断对象是否存在,而且属于当前用户组 |
-nt | 判断file1是否比file2新 |
-ot | 判断file1是否比file2旧 |
(1) if语句
if语句格式以下:
if <condition> then #do something elif <condition> then #do something else #do something fi
若是想把then和if放同一行
if <condition> ; then #do something elif <condition> ; then #do something else #do something fi
其中elif和else能够省略
例子:
read file if [ -f ${file} ] ; then echo 'This is normal file.' elif [ -d ${file} ] ; then echo 'This is dir' elif [ -c ${file} -o -b ${file} ] ; then echo 'This is device file.' else echo 'This is unknown file.' fi
逻辑判断也能够用test命令,它和[]
的做用是同样的
#!/bin/bash a=4 b=4 if test $[a+1] -eq $[b+2] then echo "表达式结果相等" else echo "表达式结果不相等" fi
输出:
表达式结果不相等
(2) for 语句
if语句格式以下:
for <var> in [list] do # do something done
例子:
read input for val in ${input} ; do echo "val:${val}" done
输入:
1 2 3 4 5
输出:
val:1
val:2
val:3
val:4
val:5
(3) while 语句
while <condition> do #do something done
例子:
a=1 sum=0 while [ ${a} -le 100 ] ;do sum=`expr ${sum} + ${a}` a=`expr ${a} + 1` done echo ${sum}
输出:
5050
shell中也有数组,数组的格式是用英文小括号元素包裹起来,元素与元素以前用若干个分割符隔开(空格,制表符,换行符),这个分割符定义在IFS
变量中,咱们能够经过设置IFS变量自定义分隔符。来看看如何定义一个数组:
array=(1 2 3 4 5 'hello')
也能够定义一个空数组
array=()
访问和修改数组元素的格式以下:
array[index]=value
和大多数编程语言同样,shell的数组索引也是从0开始的,假如想要分别修改数组的第一个和第二个元素为88和77:
array[0]=88 array[1]=77
上面的代码,若是array为空,88和77将被添加到数组中。
遍历数组太常见了,若是想对数组每一个元素都进行特定的操做(访问,修改)就须要遍历。
在shell中,有两个方式能够获得数组的所有元素,${array_name[*]}
和${array_name[@]}
,有了这个知识,咱们就遍历数组了
#!/bin/bash array=("My favoriate number is" 65 22 ) idx=0 for elem in ${array[*]} do echo "Array element ${idx} is:${elem}" idx=$(expr $idx + 1) done
输出:
Array element 0 is:My Array element 1 is:favoriate Array element 2 is:number Array element 3 is:is Array element 4 is:65 Array element 5 is:22
在上面的代码中咱们可能觉得本身定义了一个字符串和两个数字在数组中,应该打印出一行字符串和两个数字。可是却不是这样的,只要有空白符,shell会把它们当成数组的分隔符,这些被隔开的部分就会被当成数组的元素。
function foo(){ # do something... }
function foo(){ local name=$1 local age=$2 echo "My name is ${name},I'm ${age} years old." } foo "luoye" 26
输出:
My name is luoye,I'm 26 years old.
重定向能够理解把一个东西传送到另个地方
重定向符 | 做用 |
---|---|
output > file | 将输出流重定向到文件 |
output >> file | 将输出流追加到文件末尾 |
input < file | 将文件的内容重定向到输入流 |
例子:
echo 'hello' > out.txt echo 'world' >> out.txt cat out.txt
输出:
hello world
上面的例子,将hello
从输出流重定向文件,将world
追加到out.txt文件尾,最后用cat命令读取并打印out.txt的文件内容
重定向符还能够配合数字(0,1,2)使用
ls / 1> out.txt cat out.txt
执行上面的命令,会将根目录下的文件名和目录名输出到out.txt
ls /luoye 2> out.txt cat out.txt
执行上面的命令,会将执行ls /luoye
命令时的错误信息输出到out.txt
ls /;ls /luoye 2>&1
执行上面的代码,将错误流重定向到输出流,这种作法在某些场合是颇有用的。
全部重定向到这个文件的内容都会消失,经常同于忽略错误输出。
ls /luoye 2> /dev/null
若是不存在/luoye这个目录或者文件,就没有什么提示
这个文件会不断产出空的数据,该文件常被dd
命令使用
dd if=/dev/zero of=out.txt bs=1 count=16
从/dev/zero
输入,输出到out.txt
,生成一个大小为16字节的空文件
管道是Linux中的一种跨进程通讯的机制,和重定向不一样,管道用作进程与进程之间传送数据。作为Linux中默认的脚本语言,shell中也是可使用管道的,在shell中,管道用|
表示
(1)使用管道进行数据筛选内容中包含root
的行
ls -l /|grep root
这个例子中,ls命令输出的内容传给了grep命令进行筛选
(2)也能够同时用多个管道
使用多个管道把数据筛选并统计
ls -l /|grep root|wc -l
这个例子中,ls命令输出的内容传给了grep命令进行筛选,而后转给wc命令统计行数。
为了更好的理解管道,写两个脚原本体验一下:
in.sh文件
#! /bin/bash read msg echo "Receive :${msg}"
out.sh文件
#! /bin/bash echo 'hello'
在命令行中执行./out.sh |./in.sh
输出:
Receive :hello
符合咱们预期,字符串hello从out.sh传送到了in.sh