shell入门

77,635 阅读9分钟
【前言】shell俗称'『壳』,多么中二的名字。脚本就是脚本,它能帮你干很多事情。中二的不是他的名字,是看待名字的人,像『宇宙大将军』这样的称号,现代人又是这样看待的呢?

介绍
其实它也是一门语言,而它的特长就在脚本上。
Shell脚本一般在Linux等类UNIX系统上都可以运行,而在windows上需要安装环境来运行,其实安装GIT这款软件,就能支持部分linux命令
这是常见的shell环境
• Bourne Shell(/usr/bin/sh或/bin/sh)
• Bourne Again Shell(/bin/bash)
• C Shell(/usr/bin/csh)
• K Shell(/usr/bin/ksh)
• Shell for Root(/sbin/sh)
环境的选择这里就不讲,默认以及最常用的就是Bourne Again Shell,也是Linux系统默认的shell
他有两种执行模式:
1,将其更改为执行文件 chmod +x filename.sh
./filename.sh
2,sh filename.sh
---
其实写这些大多都能够在网上查询的到,云大脑确实可以作为解决问题的方案之一,但是我们不能只做云大脑,任何都靠搜索引擎来补充资源。其实单看这一篇文章也不能一下子成为大佬,这是我所不能成就的,更多的还是不够精细,但是见长还是得一步一步慢慢了解。
---
变量
作为一门语言,基础框架是学习 变量,数据类型,运算符,函数,IO,流程控制,循环往深一点就是操作数据库,操作文件,调用三方,通信等更高级一点就是建立模块供其它调用
shell也是大致如此,变量的命名:你可以这样定义
name="you contents"
// 命名规则和其它大差不差
// 使用变量
echo $name
// 或者
echo ${name}
shell也可以传参,案例就是 ./filename.sh 1 2 3
$0 是本文件名
参数处理   说明
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数。
如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
$- 显示Shell使用的当前选项,与set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
shell数组
array_name=(value1 ... valuen)
my_array=(A B "C" D)
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
array_name[3]=value3
> A B C D
使用@ 或 * 可以获取数组中的所有元素,例如:
echo "数组的元素为: ${my_array[*]}"
echo "数组的元素为: ${my_array[@]}"
> 数组的元素为: A B C D
获取数组长度的方法与获取字符串长度的方法相同,例如:
echo "数组元素个数为: ${#my_array[*]}"
echo "数组元素个数为: ${#my_array[@]}"
> 数组元素个数为: 4
shell 运算符
• 算数运算符
• 关系运算符
• 布尔运算符
• 字符串运算符
• 文件测试运算符
前面三个可能比较简单,毕竟大家都是会java或者JavaScript的嘛,怎么不济也会PHP或者python,和这些语言的运算符基本上一样
val=`expr 2 + 2`
echo "两数之和为 : $val"
> 两数之和为 : 4
算术运算符
运算符    说明 举例
+  加法 `expr $a + $b` 结果为 30。
-  减法 `expr $a - $b` 结果为 -10。
*  乘法 `expr $a * $b` 结果为  200。
/  除法 `expr $b / $a` 结果为 2。
%  取余 `expr $b % $a` 结果为 0。
=  赋值 a=$b 将把变量 b 的值赋给 a。
== 相等。用于比较两个数字,相同则返回 true。    [ $a == $b ] 返回 false。
!= 不相等。用于比较两个数字,不相同则返回 true。  [ $a != $b ] 返回 true。
乘号是比较特殊的,需要转义处理
在 MAC 中 shell 的 expr 语法是:$((表达式)),此处表达式中的 "*" 不需要转义符号 "" 。
关系运算符
运算符    说明 举例
-eq    检测两个数是否相等,相等返回 true。   [ $a -eq $b ] 返回 false。
-ne    检测两个数是否不相等,不相等返回 true。 [ $a -ne $b ] 返回 true。
-gt    检测左边的数是否大于右边的,如果是,则返回 true。    [ $a -gt $b ] 返回 false。
-lt    检测左边的数是否小于右边的,如果是,则返回 true。    [ $a -lt $b ] 返回 true。
-ge    检测左边的数是否大于等于右边的,如果是,则返回 true。  [ $a -ge $b ] 返回 false。
-le    检测左边的数是否小于等于右边的,如果是,则返回 true。  [ $a -le $b ] 返回 true。
布尔运算符
运算符    说明 举例
!  非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。
逻辑运算符
运算符    说明 举例
&& 逻辑的 AND    [[ $a -lt 100 && $b -gt 100 ]] 返回 false
|| 逻辑的 OR [[ $a -lt 100 || $b -gt 100 ]] 返回 true
以上运算符写法就是这些,使用的语法也不难,后面会联系分支流程控制语句做出例子
以下就是比较特殊的运算符,不同语言可能会不同

字符串运算符
运算符    说明 举例
=  检测两个字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。
!= 检测两个字符串是否相等,不相等返回 true。    [ $a != $b ] 返回 true。
-z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否不为 0,不为 0 返回 true。    [ -n "$a" ] 返回 true。
$  检测字符串是否为空,不为空返回 true。  [ $a ] 返回 true。
shell脚本操作文件是很常有的,接下来介绍的是
文件测试运算符
操作符    说明 举例
-b file    检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-c file    检测文件是否是字符设备文件,如果是,则返回 true。    [ -c $file ] 返回 false。
-d file    检测文件是否是目录,如果是,则返回 true。    [ -d $file ] 返回 false。
-f file    检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。   [ -f $file ] 返回 true。
-g file    检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。
-k file    检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ] 返回 false。
-p file    检测文件是否是有名管道,如果是,则返回 true。  [ -p $file ] 返回 false。
-u file    检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。
-r file    检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
-w file    检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file    检测文件是否可执行,如果是,则返回 true。    [ -x $file ] 返回 true。
-s file    检测文件是否为空(文件大小是否大于0),不为空返回 true。    [ -s $file ] 返回 true。
-e file    检测文件(包括目录)是否存在,如果是,则返回 true。   [ -e $file ] 返回 true。
• -S: 判断某文件是否 socket。
• -L: 检测文件是否存在并且是一个符号链接。
运算符用于计算比较判断等等,比较基础,会的越多,功能就越全
输出语句:echo
1,普通输出
echo "It is a test"
2,转义输出
echo ""It is a test""
3,显示变量
read name
echo "$name It is a test"
这个意思有点类似python的input()函数
4,换行显示
echo -e "OK! \n" # -e 开启转义
echo "It is a test"
> OK!
It is a test
5,显示不换行
echo -e "OK! \c" # -e 开启转义 \c 不换行
echo "It is a test"
> OK! It is a test
6,显示结果定向至文件
echo "It is a test" > myfile
这个我感觉还是挺好玩的,>: 表示写入文件,并覆盖原来的内容 >>: 写入文件,追加原来的内容
7,原样输出字符串,不进行转义或读取变量(用单引号)
echo '$name"'
> $name"
欸!还有点像PHP啊
8,显示命令执行结果
echo `date`
> Thu Jul 24 10:08:46 CST 2014
这个是反引号,也是和PHP有点像,可以执行脚本命令用的
输出 printf命令
这个命令对于学过C语言的再熟悉不过了,格式化输出
• printf 命令模仿 C 程序库(library)里的 printf() 程序。
• printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。
• printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。
printf  format-string  [arguments...]
format-string : 为格式化控制字符串
arguments : 为参数列表
例:
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876

>
姓名     性别   体重kg
郭靖     男      66.12
杨过     男      48.65
郭芙     女      47.99
%s %c %d %f都是格式替代符
%-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐),任何字符都会被显示在10个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
%-4.2f 指格式化为小数,其中.2指保留2位小数。

printf的转义序列
序列 说明
\a 警告字符,通常为ASCII的BEL字符
\b 后退
\c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f 换页(formfeed)
\n 换行
\r 回车(Carriage return)
\t 水平制表符
\v 垂直制表符
\ 一个字面上的反斜杠字符
\ddd   表示1到3位数八进制值的字符。仅在格式字符串中有效
\0ddd  表示1到3位的八进制值字符
test
shell还有一个命令可以测试,就是test
Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。
数值测试
参数 说明
-eq    等于则为真
-ne    不等于则为真
-gt    大于则为真
-ge    大于等于则为真
-lt    小于则为真
-le    小于等于则为真
因为比较少见,所以给个例子
num1=100
num2=100
if test $num1 -eq $num2
then
echo '两个数相等!'
else
echo '两个数不相等!'
fi
> 两个数相等!
代码中的 [] 执行基本的算数运算
a=5
b=6
result=$[a+b] # 注意等号两边不能有空格
echo "result 为: $result"
> result 为: 11

字符串测试
参数 说明
=  等于则为真
!= 不相等则为真
-z 字符串 字符串的长度为零则为真
-n 字符串 字符串的长度不为零则为真
num1="老干妈"
num2="老千妈"
if test $num1 = $num2
then
echo '两个字符串相等!'
else
echo '两个字符串不相等!'
fi

> 两个字符串不相等!
当然shell经常操作文件
文件测试
参数 说明
-e 文件名 如果文件存在则为真
-r 文件名 如果文件存在且可读则为真
-w 文件名 如果文件存在且可写则为真
-x 文件名 如果文件存在且可执行则为真
-s 文件名 如果文件存在且至少有一个字符则为真
-d 文件名 如果文件存在且为目录则为真
-f 文件名 如果文件存在且为普通文件则为真
-c 文件名 如果文件存在且为字符型特殊文件则为真
-b 文件名 如果文件存在且为块特殊文件则为真
cd /bin
if test -e ./bash
then
echo '文件已存在!'
else
echo '文件不存在!'
fi
> 文件已存在!
另外,Shell还提供了与( -a )、或( -o )、非( ! )三个逻辑操作符用于将测试条件连接起来,其优先级为:"!"最高,"-a"次之,"-o"最低。例如:

if test -e ./notFile -o -e ./bash
then
echo '至少有一个文件存在!'
else
echo '两个文件都不存在'
fi
> 至少有一个文件存在!
shell流程控制
shell脚本和其它语言不同的事他的流程控制不可为空,不像python那样还可以使用pass来占个位置,shell脚本如果不需要就不用写
先说最熟悉的if    else
if else
if:
语法格式:

if condition
then
command1
command2
...
commandN
fi一行式:
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
有时候我们会在命令行中使用shell脚本,使用一行式较简便
结尾一般是倒着写的单词,如 if 结尾就用 fi
if else
语法格式:
if condition
then
command1
command2
...
commandN
else
command
fi

if else-if else
语法格式:
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
for

语法格式:
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
例:
for var in 1 2 3 4 5
do
echo "value is:" $var
done
>
The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5
while
语法格式:
while condition
do
command
done
例:
int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done
>
1
2
3
4
5
let 命令:执行一个或多个表达式
如果你想体验java或者python的哪种无限输入的感觉,shell就能帮你实现
echo '按下 <CTRL-D> 退出'
    echo -n '输入你想点赞的理由: '
    while read FILM
    do
    echo "是的!$FILM 是一个好理由"
    done
    >
    按下 <CTRL-D> 退出
        输入你想点赞的理由:帅
        是的!帅 是一个好理由
        下面教你如何无限循环:无限月读 [無限月読]
        无线循环可不是闹着玩的,小心内存爆掉哦!
        1 ->
        while :
        do
        command
        done
        2 ->
        while true
        do
        command
        done
        3 ->
        for (( ; ; ))
        下面介绍几个可能在别的语言中有的,但是在php中没有的循环
        until循环
        顾名思义就是到……结束
        until 循环执行一系列命令直至条件为 true 时停止。
        是不是和while语句相反呢,欸!人家就是这么特立独行
        语法格式:
        until condition
        do
        command
        done
        例:
        a=0
        until [ ! $a -lt 10 ]
        do
        echo $a
        a=`expr $a + 1`
        done
        >
        0
        1
        2
        3
        4
        5
        6
        7
        8
        9

        case
        语法格式:
        casein
        模式1)
        command1
        command2
        ...
        commandN
        ;;
        模式2)
        command1
        command2
        ...
        commandN
        ;;
        esac
        是不是和switch  case有点像,好吧其实差不多啦
        例:
        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
        >
        输入 1 到 4 之间的数字:
        你输入的数字为:
        3
        你选择了 3
        它是使用‘;;’来结束,其实PHP中你可以使用无数个‘;’反正效果都一样
        *号有点类似于default
        看它的结束符 esac真别扭,哈,其实就是倒过来了。
        跳出循环
        循环只是过程,我们要的是结果,淦
        break

        效果就是你们所知道那样,跳出,终止循环
        continue

        跳出本次循环,不执行后面代码

        shell 函数
        连C语言都有函数,我shell脚本当然也得有,对吧
        用户可以自定义函数
        语法格式:
        [ function ] funname [()]
        {
        action;
        [return int;]
        }
        定义方法有两种:
        1,function func()
        2,func()
        你觉得哪个方便
        和其它语言函数一样可以有返回值,用return就可以了
        例:
        demoFun(){
        echo "这是我的第一个 shell 函数!"
        }
        echo "-----函数开始执行-----"
        demoFun
        echo "-----函数执行完毕-----"

        >
        -----函数开始执行-----
        这是我的第一个 shell 函数!
        -----函数执行完毕-----
        函数也可以有参数,但是shell脚本和其它语言不一样的是,它的参数,不是写在括号中,和命令行传参一样
        例:
        funWithParam(){
        echo "第一个参数为 $1 !"
        echo "第二个参数为 $2 !"
        echo "第十个参数为 $10 !"
        echo "第十个参数为 ${10} !"
        echo "第十一个参数为 ${11} !"
        echo "参数总数有 $# 个!"
        echo "作为一个字符串输出所有参数 $* !"
        }
        funWithParam 1 2 3 4 5 6 7 8 9 34 73
        >
        第一个参数为 1 !
        第二个参数为 2 !
        第十个参数为 10 !
        第十个参数为 34 !
        第十一个参数为 73 !
        参数总数有 11 个!
        作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !
        特殊字符用来处理参数
        参数处理   说明
        $# 传递到脚本或函数的参数个数
        $* 以一个单字符串显示所有向脚本传递的参数
        $$ 脚本运行的当前进程ID号
        $! 后台运行的最后一个进程的ID号
        $@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
        $- 显示Shell使用的当前选项,与set命令功能相同。
        $? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
        shell输入输出重定向
        还记得之前用到过的echo filename > newfile吗,有点类似了
        命令 说明
        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)。
        Here Document
        PHP也有这个,主要是写一些文本文件,或者html什么的
        shell脚本和PHP的用法类似
        例:
        cat << EOF
        欢迎来到
        extend
        shell
        EOF

        >
        欢迎来到
        extend
        shell

        /dev/null 文件
        /dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
        如果希望屏蔽 stdout 和 stderr,可以这样写:
        command > /dev/null 2>&1
        注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
        这里的 2 和 > 之间不可以有空格,2> 是一体的时候才表示错误输出。

        shell文件包含
        和其它语言一样,shell脚本也可以引入外部文件,这听上去是python的特长
        语法格式:
        . filename   # 注意点号(.)和文件名中间有一空格source filename
        类似于php的引入
        举个例子:
        创建一个文件叫lib.sh
        name = "小花"
        password = "9994"
        我们在第二个文件中引用
        controller.sh
        . ./lib.sh  # or source ./lib.sh
        echo "用户名:$name"
        echo "密码:$password"

        运行controller.sh
        chmod +x ccontroller.sh
        ./controller.sh

        >
        用户名:小花
        密码:9994

        旅途很短暂呐!想必痛苦的事情还在后头呢~
        时光不暇,匆匆而逝 谨以此篇,纪念过往





        【声明】本篇是纯个人编辑,创造,知识点及表格等基础知识部分来源于菜鸟教程,整合知识点及讲解全部由本人亲手纂写。
        #/////////////////////////////////////////////////////////////////// ← i am shell annotation
        .扩展
        ==================== 以下为shell脚本学习扩展
        • 使用cd无效问题
        在shell脚本中,如果使用cd ./path,执行shell脚本是不会进入path目录的,原因是shell脚本会创建一个子shell,并在子shell中逐个执行脚本中的指令; 而子shell中从父shell中继承了环境变量,但是执行后不会改变父shell的环境变量;如果想要代码中切换目录的操作生效,只需要通过source 命令执行即可:
        设有一个名叫this.sh的shell脚本,内容如下
        #! /bin/bash
        cd ./path

        > sh this.sh
        ...... 好尴尬,什么都没执行
        解决方法:source ./this.sh 或 . ./this.sh

        运行
        咦~好神奇!
        • base_path=$(cd $(dirname $0); pwd)
        项目中很多地方都用到了这个,通常都是广义上的第一行,解释一下这一条语句的神奇所在
        在linux的shell中经常需要进入脚本当前所在的目录,以其为基准使用相对路径,那么怎样获取到这个路径呢?
        linux的shell中并没有一个变量能直接代表这个路径,需要自己使用命令来构造。
        path=$(cd`dirname $0`;pwd)

        这条简单的命令就可以进入当前的路径,并且将路径值存在变量里。
        - 首先是使用反引号包起来的dirname $0命令。使用反引号代表执行其中的命令并返回命令结果。而$0代表脚本的文件名。这条指令就可以得到脚本的路径。
        - 然后使用dirname通过文件名获取路径,因为$0并不是绝对路径,所以使用dirname得到的也不是绝对路径。这时使用cd进入,然后再使用pwd命令得到当前路径的绝对路径。
        • 解答为什么函数return后如何接收问题
        定义一个函数
        func()
        {
        return 100
        }

        func # 执行函数
        echo $?

        运行结果
        > 100
        • export
        在shell中执行程序时,shell会提供一组环境变量。export可新增,修改或删除环境变量,供后续执行的程序使用。同时,重要的一点是,export的效力仅及于该次登陆操作。注销或者重新开一个窗口,export命令给出的环境变量都不存在了。
        该命令的语法如下:
        export [-fnp][变量名称]=[变量设置值]
        其中:
        ○ -f  代表[变量名称]中为函数名称。
        ○ -n  删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。
        ○ -p  列出所有的shell赋予程序的环境变量。
        你可以只打出export来查看当前的所有环境变量。如果你要在某个环境变量(比如PATH)中加入一些新的路径(如/bin/bash),可以使用如下命令格式
        export PATH=/bin/bash:$PATHls > log 2>&1
        这是什么意思呢
        ls的内容重定向到log中了,2是标准错误,1是标准输出,0是标准输入,0是键盘输入,1和2都是输出,以上意思就是错误不输出到屏幕,重定向到文件

        #!/bin/bash
        a = 'hello world'
        echo $a