入门shell脚本

在做项目的时候,往往需要shell脚本来运行linux语句,它是用户使用 Linux 的桥梁,今天我们通过这篇文章入门shell脚本。

1 shell介绍

shell就是俗称的壳,用于与用户交互,接收用户命令,调用相关程序。


shell所在位置

可以将shell分为2大类

1.1 图形界面shell (Graphical User Interface shell即GUI shell)

也就是用户使用 GUI 和计算机核交互的 shell,比如 Windows 下使用最广泛的 Windows Explorer(Windows 资源管理器),Linux 下的 X Window,以及各种更强大的 CDE、GNOME、KDE、 XFCE。他们都是GUI shell。

1.2 命令行式 shell(Command Line Interface shell ,即 CLI shell)

也就是通过命令行和计算机交互的 shell。Windows NT 系统下有 cmd.exe(命令提示字符)和近年来微软大力推广的 Windows PowerShell。Linux 下有 bash / sh / ksh / csh/zsh 等 一般情况下,习惯把命令行 shell(CLI shell)直接称做 shell。

2 交互方式

根据交互方式的不一样,命令行式 shell(CLI shell),又分为交互式 shell 和非交互式 shell。

2.1 交互式shell

交互式模式就是 shell 等待你的输入,并且执行你提交的命令,然后马上给你反馈。这种也是我们大多数时候使用的。

2.2 非交互式shell

非交互式 shell,就是把 shell 放在写在一个文件里面,执行的时候,不与用户交互,从前往后依次执行,执行到文件结尾时,shell 也就终止了。

3 shell种类

在 Linux 下 ,各种 shell 百花齐放,种类繁多,不同的 shell,也有不同的优缺点。我们要查看当前系统下支持的 shell,可以读取 / etc/shells 文件。


shell种类

4 shell脚本

4.1 基础

#!/bin/bash
echo "Hello World!"

(#!)是个特殊的标记,表明用哪个解释器执行。例如这里用到了/bin/bash来执行shell脚本
执行并获取返回结果,有点类似 JavaScript 的 eval 函数。

#!/bin/bash
dt=`date` #反引号内的字符串会当作shell执行 ,并且返回结果。
echo "dt=${dt}"

4.2 Shell变量

shell 的使用比较简单,没有数据类型的概念,所有的变量都可以当成字符串来处理:
使用变量

ABC="tom"
echo $ABC #使用变量前面加$美元符号
echo "ABC=$ABC" #可以直接在字符串里面引用
echo "ABC=${ABC}" #但是建议把变量名字用{}包起来

只读变量

ABC="tom"
echo "ABC=${ABC}"
readOnly ABC #设置只读
ABC="CAT" #会报错,因为设置了只读,不能修改

删除变量

ABC="tom"
echo "ABC=${ABC}"
unset ABC #删除
echo "ABC=$ABC"  
echo "ABC=${ABC}" 

使用一个不存在的变量不会报错,只是当作空字符串处理

4.3 Shell字符串

使用字符串

A=my #你甚至可以不用引号,但是字符串当中不能有空格,这种方式也不推荐
B='my name is ${NAME}' #变量不会被解析
C="my name is ${NAME}" #变量会解析
echo $A
echo $B
echo $C

我们可以发现,这个字符串的单双号和 PHP 的处理非常类似,单引号不解析变量,双引号可以解析变量。
拼接字符串
其实 shell 拼接字符串,大概就是 2 种

  • 直接在双引号内应用变量,类似模板字符串
  • 直接将字符串写在一起
# 使用双引号拼接
echo "hello, "$NAME" !" #直接写在一起,没有字符串连接符
echo "hello, ${NAME} !" #填充模版
# 使用单引号拼接
echo 'hello, '$NAME' !' #直接写在一起,没有字符串连接符
echo 'hello, ${NAME} !' #上面已经提高过,单引号里面的变量是不会解析的

强大的字符串处理 shell 中简单的处理字符串,可以直接使用各种标记,只是比较难记忆,要用的时候,可以查一下。

ABC="my name is tom,his name is cat"
echo "字符串长度=${#ABC}" # 取字符串长度
echo "截取=${ABC:11}" # 截取字符串, 从11开始到结束
echo "截取=${ABC:11:3}" # 截取字符串, 从11开始3个字符串
echo "默认值=${XXX-default}" #如果XXX不存在,默认值是default
echo "默认值=${XXX-$ABC}" #如果XXX不存在,默认值是变量ABC
echo "从开头删除最短匹配=${ABC#my}" # 从开头删除 my 匹配的最短字符串
echo "从开头删除最长匹配=${ABC##my*tom}" # 从开头删除 my 匹配的最长字符串
echo "从结尾删除最短匹配=${ABC%cat}" # 从结尾删除 cat 匹配的最短字符串
echo "从结尾删除最长匹配=${ABC%%,*t}" # 从结尾删除 ,*t 匹配的最长字符串
echo "替换第一个=${ABC/is/are}" #替换第一个is
echo "替换所有=${ABC//is/are}" #替换所有的is

4.4 数组

与大多数语言一样从0下标开始,但是是用空格分隔开来的,就像下面这样

array=("items1","items2","items3")

也可以根据下标定义元素

array[0]="new_item0"
array[1]="new_item1"
array[2]="new_item2"
array[4]="new_item4" #数组下标可以是不连续的

读取数组元素,和变量类似

echo ${array[0]}
echo "array[0]=${array[0]}"

获得所有的数组

echo "数组的元素为: ${array[*]}"
echo "数组的元素为: ${array[@]}"

获得数组的长度

echo "数组的长度为: ${#array[*]}"
echo "数组的长度为: ${#array[@]}"

4.5 输入输出

4.5.1 echo

加入特殊符号,可以用来转义

echo "Hello \nworld!"
echo "\"Hello\""
echo '"Hello"' #当然,也可以这样,单引号不转义,上文提到过
echo `date` #打印执行date的结果,注意,单引号不转义
echo -n "123" #加-n  表示不在末尾输出换行
echo "456"
echo -e "\a处理特殊符号" #-e 处理特殊符号
符号 作用
\a 发出警告声
\b 删除前一个字符
\c 后面不加上换行符
\f 换行但光标仍旧停留在原来的位置
\n 换行且光标移至行首
\r 光标移至行首,但不换行
\t 插入tab

4.5.2 read

有出必有入,下面介绍输入

read name
echo "my name is ${name}"

可以输入-p给定提示

read -p "please input your name:" name
echo "my name is ${name}"

如果没有给定变量,会给环境变量REPLY赋值

read -p "please enter your name:"
echo "your name is ${REPLY}"

指定时间内输入

read -t 3 -p "please enter your name:"
echo "your name is ${REPLY}"

指定输入字符个数

read -n 1 -p "are you sure Y/S"

默读(输入不在监视器显示)

read -s -p "please enter your name:"

4.5.3 printf

类似于C语言的printf,用于字符串模板输出

printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg  
printf "%-10s %-8s %-4.2f\n" 张三 m 123 
printf "%-10s %-8s %-4.2f\n" 李四 f 213 
printf "%-10s %-8s %-4.2f\n" 王五 m 534 

%s %c %d %f 都是格式替代符 %-10s 指一个宽度为 10 个字符(- 表示左对齐,没有则表示右对齐),至少显示 10 字符宽度,如果不足则自动以空格填充,超过不限制。%-4.2f 指格式化为小数,其中. 2 指保留 2 位小数。

4.5.4 重定向

大多数 UNIX 系统命令从你的终端接受输入并将所产生的输出发送回到您的终端

命令 作用
command > file 将输出重定向到 file
command < file 将输入重定向到 file
command >> file 将输出以追加的方式重定向到 file
n > file 将文件描述符为 n 的文件重定向到 file
n >> file 将文件描述符为 n 的文件以追加的方式重定向到 file
n >& m 将输出文件 m 和 n 合并
n <& m 将输入文件 m 和 n 合并
<< tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入

需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
输出文件

echo "test">text.txt #直接输出
echo "test">>text.txt #追加在text.txt后面

4.6 条件判断语句

单分支

if condition
then
    command1 
    command2
    ...
fi

双分支

if condition
then
    command1 
    command2
else
    command3
    ...
fi

多分支

if condition1
then
    command1 
    command2
elif condition2
then 
    command3
else
    command4
    ...
fi

比如

if [ "2" == "2" ]; 
then
    echo "2==2"
else
    echo "2!=2"
fi

特别注意[ "2" == "2" ]两边都有等号,否则会报错
判断文件是否存在

if [ -f "1.sh" ]; then # 判断一个普通文件是否存在
    echo "1.sh 存在"
fi

判断字符串长度=0

a=""
if [ -z $a ]; then
    echo "a为空"
fi

4.7 shell中的括号()、(())、[]、[[]] 和 {}

4.7.1. 单小括号()

  • 命令组括号中的命令会新开一个shell顺序执行,所以括号中的变量不能被另外的括号使用
a="123"
(echo "123";a="456";echo "a=¨E61Ea")
  • 命令替换 发现$(cmd) 结构,便将 $(cmd) 中的 cmd 执行一次,得到其标准输出,再将此输出放到原来命令。
  • 用于初始化数组 如:array=(a b c d)

4.7.2 双小括号(())

  • 运算扩展,比如,你可以
a=$((4+5)) 
echo "a=$a"
  • 做数值运算,重新定义变量
a=5
((a++))
echo "a=$a"
  • 用于算术运算比较
if ((1+1>1));then
  echo "1+1>1"
fi

4.7.3 单中括号[]

  • 用于字符串的比较,运算符只能是==和!=,而且两边都要有空格
if [ "2" == "2" ]; then # "2" 的2边都有空格,不能省略
  echo "2==2"
else
  echo "2!=2"
fi
  • 用于整数比较 需要注意,整数比较,只能用 - eq,-gt 这种形式,不能直接使用大于 (>) 小于 (<) 符号。只能用于整形
if [ "2" == "2" ]; then # "2" 的2边都有空格,不能省略
  echo "2==2"
else
  echo "2!=2"
fi

符号表(只能用于整型)

符号 运算
-eq 等于(equal)
-ne 不等于(not equal)
-gt 大于(great than)
-ge 大于等于(great equal)
-lt 小于(low than)
-le 小于等于(low equal)
  • 多个逻辑组合 -a 表示 and 与运算 -o 表示 or 或运算
if [ "2" == "2" -a "1" == "1" ]; then #注意,在这里,不能是[ "2" == "2" ] -a [ "1" == "1" ] 会报错
  echo "ok"
fi

4.7.4 双中括号[[]]

[[]]表示的是程序中的关键字

  • 字符串匹配支持正则表达式
if [[ "123" == 12* ]]; then #右边是正则不需要引号
  echo "ok"
fi
  • 支持对数字的判断,是支持浮点型的,并且可以直接使用 <、>、==、!= 符号
if [[  2.1 > 1.1 ]]; then
  echo "ok"
fi
  • 多个逻辑判断 可以直接使用 &&、|| 做逻辑运算,并且可以在多个 [[]] 之间进行运算
if [[  1.1 > 1.1 ]] || [[ 1.1 == 1.1 ]]; then
  echo "ok"
fi

4.7.5 大括号{}

创建新的文件

touch new_{1..5}.txt #创建new_1.txt    new_2.txt   new_3.txt   new_4.txt   new_5.txt  5个文件

4.8 循环

4.8.1 for循环

语法格式为

for a in "item1" "item2" "item3"
do
    echo $a
done

输出当前目录为.sh结尾的文件

for a in `ls ./`
do
    if [[ $a == *.sh ]]
  then
        echo $a
  fi
done

4.8.2 while循环

输出1-100的数

int=1;
while(($int<=100))
do
    echo $int
    ((int++))
done

4.8.3 until循环

语法

until condition
do
    command
done

循环中的continue和break与其它语言类似

4.9 case

case 和其他语言 switch 类型,多分支,选择一个匹配。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;,有点类型 Java 的 break。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。

echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
    1)  echo '你选择了 1'
    ;;
    2)  echo '你选择了 2'
    ;;
    3)  echo '你选择了 3'
    ;;
    4)  echo '你选择了 4'
    ;;
    *)  echo '你没有输入 1 到 4 之间的数字'
    ;;
esac
case示例

4.10 函数

shell 也可以用户定义函数,然后在 shell 脚本中可以随便调用。注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至 shell 解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。语法格式如下:

[function] funname()
{
    cmd....
    [return int]
}

一个最简单的函数

Line(){
  echo "--------分割线--------"
}
echo "123"
Line
echo "456"

在 Shell 中,调用函数时可以向其传递参数。在函数体内部,通过 n 的形式来获取参数的值,例如,\1表示第一个参数,$2 表示第二个参数… 调用的时候 ,函数名,参数直接用空格分割开。带参数的函数示例:

out(){
    echo "1-->$1"
    echo "2-->$2"[图片上传中...(参数.png-981b9a-1577263405886-0)]

}
out 1 2 #调用的之后
参数

还有一些特殊的符号需要注意

符号 作用
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数
$$ 脚本运行的当前进程 ID 号
$@ 与 $* 相同,但是使用时加引号,并在引号中返回每个参数
$? 显示最后命令的退出状态。0 表示没有错误,其他任何值表明有错误

下面是一个返回所有值的例子

out(){
    echo "全部参数$*"
    for item in $*
    do
        echo "$item"
    done
    return $# #这类返回参数个数,返回值必须是整数
}
out this is perfect
echo "函数返回值:$?"

5 其他实用技巧

shell 脚本,他本身的功能并不强大,强大的是他可以调用其他程序,而在 Linux 下,系统自带的就有非常多的强大工具可以调用。

5.1 后台执行

后台执行一个脚本只需要在后面加上 & 符号即可,我们先用之前学习的,写一个脚本,1s 输出一个数字

#!/bin/bash
i=1
while :
do
    echo $i
    i=$(( $i + 1 ))
    sleep 1s #sleep one second
done

我们执行 sh d.sh & 我们发现,的确会后台输出,但是会输出到当前控制台,我们可以用之前学的重定向,把输出重定向到文件
sh d.sh > out.log 2>&1 &
sh d.sh > out.log 意思是将d.sh输出重定向到out.log,2>&1 &的意思是将错误信号(2表示)也作为输入信号(1表示)重定向。但是我们会发现,如果关闭终端,会产生SIGHUP信号,导致程序退出,所以我们的程序用到nuhup达到真正的后台。
nuhup sh d.sh > out.log 2>&1 &
这样就可以达到真正的后台了。那么,我们如何验证程序在后台运行呢?要怎么结束后台。

5.2 tail

查看文件的后面几行tail out.log,默认输出10行,也可以指定行数,比如tail -n 5 out.log,也可以用tail -f out.log,若有更新则输出结果。

5.3 ps

ps,查询进程,一般查询,就用ps u就行了,显示面向用户格式的后台进程信息。

6 总结

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

推荐阅读更多精彩内容

  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,374评论 0 5
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,726评论 0 10
  • 前言 写下这篇文章,是对自己在学习和使用过程中的总结,文笔不是很好,如果有什么问题欢迎沟通交流 Github地址:...
    hi_dhl阅读 464评论 1 1
  • 在正式开始前,我们可以思考一个问题,学习一门技术难吗?不少人的答案可能是很难。 来看一个案例,也可以说是两个。 2...
    ca202e4d3a56阅读 3,422评论 0 4
  • 一、shell脚本介绍 1.1 开头(环境使用shebang机制) #!/bin/bash 必须写在文件首行 符号...
    优果馥斯阅读 3,278评论 0 1