shell简明教程

shell的格式

shell可以在直接在命令行下输入,也可以保存成shell脚本文件运行。当命令简单并且不需要重复使用,在命令行输入直接执行即可,否则就写成脚本。shell脚本默认文件扩展名为.sh。在shell脚本中,写入的内容,会默认当成一条命令来执行。

例如:

#!/bin/bash
echo 'hello world'
  • 第1行 指定shell脚本的解释器
  • 第2行 执行echo命令

将上面的代码存为test.sh,并将可执行权限赋予它chmod +x test.sh ,执行./test.sh运行脚本。

上面的脚本将会输出:

hello world

这和在命令行或者终端模拟器下输入echo 'hello world'并按下回车得到的结果是一样的

注释

和所有的编程语言一样,shell也有注释,在shell中,#号和它后面的内容来表示一个注释:

# Print a message
echo "I'm a shell script."

输出内容

echo用于向输出流输出内容,例如:

echo "hello world"

输入内容

read用于输入一条内容:

read input
echo $input

上面的代码中,read命令从输入流读取一个值并赋予input,然后将input的内容打印出来

1. 变量

1.1 定义变量和赋值

变量的命名规则和C语言差不多,支持英文字母和下划线。shell中变量名前不需要声明类型,变量名后面不能有空格,例如:

var1='hello'
var2=90

1.2 读取变量

$后接变量名意为读取一个变量的值,例如:

var="hello"
echo $var

也可以用${var}方式访问到变量值,例如:

var="hello"
echo ${var}

访问变量的时候$var${var}是等效的,推荐后者来访问一个变量

1.3 变量作用域

1.3.1 全局变量

没有任何命令修饰的变量是一个全局变量,全局变量在同一个shell会话中都是有效的。

function func(){
    a=90
}
func
echo $a

输出:

90

$ a=90
$ echo ${a}
$ bash 
$ echo ${a}

输出:

90

空值

1.3.2 局部变量

local命令用于声明一个局部变量

function func(){
    local a=90
}
func
echo $a

输出:

空值

1.3.3 环境变量

用export命令修饰的变量称为环境变量,在父shell会话中声明一个环境变量,子shell中都可以访问

$ export path="/system/bin"
$ bash #创建一个新的shell会话
$ echo ${path}

1.3.4 特殊变量

变量 含义
$0 当前脚本的文件名
$n(n≥1) 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是 1,第二个参数是2
$# 传递给脚本或函数的参数个数
$* 传递给脚本或函数的所有参数
$@ 传递给脚本或函数的所有参数
$? 上个命令的退出状态,或函数的返回值
$$ 当前 Shell 进程 ID。对于 Shell 脚本,就是这些脚本所在的进程 ID

1.3.5 *和@的区别

  • $*得到所有参数被当成字符串
  • $@得到所有参数都会被当成独立的参数
#!/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

2. 获取一条命令的执行结果

2.1 用 ` 将一条命令包裹起来

` 这个符号,在键盘上的位置是在Esc键的下方

ret=`pwd`
echo ${ret}

在 ` 包裹起来的命令中,也可以访问到变量

path='/'
ret=`ls -l ${path}`
echo ${ret}

2.2 以$(command)这种方式执行命令

ret=$(pwd)
echo ${ret}

用$(command)这种方式也可以访问到变量

path='/'
ret=$(ls -l ${path})
echo ${ret}

上面的例子中,如果想打印命令结果中的换行符,则:

path='/'
ret=$(ls -l ${path})
echo "${ret}"

(command)方式来执行命令更加直观,但是要注意,(command) 仅在 Bash Shell 中有效,而反引号可在多种 Shell 中都可使用

3. 字符串

shell有三种方式可以表示字符串

3.1 字符串的表示

(1)变量名后直接跟上字符

str=hello
echo ${str}

输出:

hello

这种方式的字符串遇到空格就会被终止

(2)单引号

str=hello
echo '${str}'

输出:

${str}

单引号里的内容是字符串原始的样子,不存在转义

(3)双引号

str=shell
echo "${str}:\"hello wolrd\""

输出:

shell:"hello world"

双引号中可以访问变量和转义

3.2 获取字符串的长度

str="hello"
echo ${#str}

输出:

5

3.3 字符串拼接

两个变量放在一起访问就可以拼接

a='hello'
b='world'
c=${a}${b}
echo ${c}

输出:

helloworld

也可以这样

echo 'hello'"world"

3.4 字符串截取

(1) 从左边开始截取字符串,格式:${string: start :length},length可省略,省略时,是截取到字符串末尾

msg="hello world"
echo ${msg: 6: 5}

输出:

world

(2) 在指定位置截取字符

  • 截取chars后面的字符:${string#*chars}

其中,string 表示要截取的字符,chars 是指定的字符(或者子字符串),是通配符的一种,表示任意长度的字符串。chars连起来使用的意思是:忽略左边的所有字符,直到遇见 chars(chars 不会被截取)。

  • 截取最后一次出现chars的位置后面的内容:${string##*chars}

  • 使用 % 截取左边字符
    使用%号可以截取指定字符(或者子字符串)左边的所有字符,具体格式如下:
    ${string%chars*}

请注意 * 的位置,因为要截取 chars 左边的字符,而忽略 chars 右边的字符,所以 * 应该位于chars的右侧。其他方面%和#的用法相同

4. 运算符和流程控制

4.1 基本运算

运算符 作用
+ 加(需要结合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中表达式两边要有空格

4.2 关系运算

运算符 作用
-eq 全称:Equal,判断两个数是否相等
-ne 全称:Not equal,判断两个数是否不相等
-gt 全称:Greater than,判断前面那个数是否大于后面那个数
-lt 全称:Less than,判断前面那个数是否小于后面那个数
-ge 全称:Greater equal,判断前面那个数是否大于等于后面那个数
-le 全称:Less than,判断前面那个数是否小于等于后面那个数

4.3 布尔运算

运算符 作用
! 非运算
-o 或运算
-a 并运算

4.4 逻辑运算

运算符 作用
&& 逻辑并
` ` 逻辑或
  • &&连接起来的两个命令,前面的执行失败就不执行后面的命令
cd /bin && ls /bin

其实和C语言中的是差不多的,只要前面的条件不满足,后面那个就不用去执行它了

  • ||连接起来的两个命令,前面的执行失败才会执行后面的命令
cd /bin || ls /bin

在这里顺便说一下;这个运算符,这个运算符用于连接多个语句,使多个语句能够在同一行。用;连接起来的语句,不管前面的执行成不成功,都会执行后面的

mkdir luoye;cd luoye;pwd

4.5 文件判断

运算符 作用
-e 判断对象是否存在
-d 判断对象是否存在,并且为目录
-f 判断对象是否存在,并且为常规文件
-L 判断对象是否存在,并且为符号链接
-h 判断对象是否存在,并且为软链接
-s 判断对象是否存在,并且长度不为0
-r 判断对象是否存在,并且可读
-w 判断对象是否存在,并且可写
-x 判断对象是否存在,并且可执行
-O 判断对象是否存在,并且属于当前用户
-G 判断对象是否存在,并且属于当前用户组
-nt 判断file1是否比file2新
-ot 判断file1是否比file2旧

4.6 流程控制语句

(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

5. 数组

5.1 定义和基本用法

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将被添加到数组中。

5.2 遍历

遍历数组太常见了,如果想对数组每个元素都进行特定的操作(访问,修改)就需要遍历。

在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会把它们当成数组的分隔符,这些被隔开的部分就会被当成数组的元素。

6. 函数

  1. 用function关键字来定义一个函数
  2. 直接写一个函数名来调用一个无参数的函数
  3. 函数有参数,调用时,在函数名后面写上参数,多个参数用空格隔开
  4. 调用函数时传递参数,在函数体内部,通过 n的形式来获取参数的值,例如:1表示第1个参数,$2表示第2个参数...

6.1 函数的结构

function foo(){
# do something...
}

6.2 函数的用法示例

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.

7. 重定向

重定向可以理解把一个东西传送到另个地方

重定向符 作用
output > file 将输出流重定向到文件
output >> file 将输出流追加到文件末尾
input < file 将文件的内容重定向到输入流

7.1 输出到文件

例子:

echo 'hello' > out.txt
echo 'world' >> out.txt
cat out.txt

输出:

hello
world

上面的例子,将hello从输出流重定向文件,将world追加到out.txt文件尾,最后用cat命令读取并打印out.txt的文件内容

重定向符还可以配合数字(0,1,2)使用

  • 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

执行上面的代码,将错误流重定向到输出流,这种做法在某些场合是很有用的。

7.2 特殊文件

7.2.1 /dev/null

所有重定向到这个文件的内容都会消失,常常同于忽略错误输出。

ls /luoye 2> /dev/null

如果不存在/luoye这个目录或者文件,就没有什么提示

7.2.2 /dev/zero

这个文件会不断产出空的数据,该文件常被dd命令使用

dd if=/dev/zero of=out.txt bs=1 count=16

/dev/zero输入,输出到out.txt,生成一个大小为16字节的空文件

8. 管道

管道是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

9. 参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,365评论 0 5
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,719评论 0 10
  • shell学习记录 shell是什么 简单来说,shell是一个命令解释器,将用户输入的命令解释给操作系统内核。它...
    土卡拉阅读 784评论 0 0
  • 6月6日 今天听燕子说吸引来粉色的车,而且别牌都是三位同数,哇塞!这个概率真的是很少呀!因为粉色的车本来就少嘛,嘿...
    思言悟语阅读 199评论 0 0
  • 路灯在傍晚时的蓝幕下像绽放的花朵盛放光芒,暖黄色的灯光配上蓝色的天空形成强烈的冷暖对比。抬头的一刹那被那光芒惊艳到...
    唐泽unicorn阅读 470评论 0 3