shell

Shell

Shell脚本入门

 Shell脚本与Windows/Dos下的批处理相似,也就是用各类命令预先放入到一个文件中,方便一次性执行的一个程序文件, Shell就是一个命令行解释器,Shell本身是一个用C语言编写的程序, Shell既是一种命令语言,又是一种程序设计语言(就是你所说的shell脚本)。shell脚本脚本文件通常以.sh作为后缀名,第一行以#!开头指定执行脚本的程序:

#!/usr/bin/bash

#是shell脚本中的行注释符。

这里我们来使用最常用的bash

1.新建文件hello_shell

$ vi hello_shell

2. 输入Shello命令

#! /bin/bash

echo "Hello Shell!"
3、保存

echo是一个输出命令,就是输出一句话

4. 赋权限

我们要让系统知道我们刚才新建的文件是可执行的,所以我们要赋给其可执行的权限

现在我们的文件是不可执行的,只有读写权限:

 

下面是赋权

$ chmod 711 hello_shell

 

5. 执行

./ 表示再当前目录查找命令,如果什么都不加的话,系统默认会在PATH里寻找,而的当前目录通常不在PATH里,所以找不到命令。

通常有三种执行脚本的方式:

1、sh start.sh: 在终端中创建一个sh子进程执行脚本, 执行者需要拥有脚本的读权限。该方式实际上是将脚本路径作为参数传递给了sh命令。

2、source start.sh: 在终端中执行脚本,相当于将脚本中的指令逐条复制到终端执行。脚本中局部变量将保留在终端环境变量中, 脚本的pid和工作目录等环境也与终端一致。

3、./start.sh: 根据HashBang指定的程序,在子进程中执行脚本。

 

变量

1. 变量的声明和定义

Shell里的变量类型:字符串、数值。定义的方式其实是一样的,字符串用单引号或双引号标识。

Shell里变量命名规范:首个字符必须为字母(a-z,A-Z)任何变量都只能由字母(包括大小写)、数字和下划线组成变量中不能有空格,不能使用bash里的关键字(可用help命令查看保留关键字)

 

变量在使用前无需声明,在为变量赋值时=左右不能添加空格。

y_name="yuguiyang"

y_age=24

 

2. 变量的使用

使用一个定义过的变量,只要在变量名前面加美元符号$即可

y_name="yuguiyang"

y_age=24

 

echo "name:$y_name"

echo "age:$y_age"

我们也可以使用 {}将变量括起来,加花括号是为了帮助解释器识别变量的边界

A=a

AB=ab

echo ${A}B

 

可以把命令的输出作为返回值, 如:

PWD=$(pwd)

 

在单引号标识的字符串中$不被作为变量标识符, 而双引号则会将$替换为变量内容。

A="abc"

echo '$A' # $A

echo "$A" # abc

 

字符串拼接不需要任何运算符,只需要将它们写在一起即可:

A="abc"

B="123"

echo "$A+$B"  # abc+123

echo "$A$B"  # abc123

echo "$Adef"  # abcdef

 

3、整型变量

shell仅支持整型计算, declare命令可以声明整型变量,let指令用于算术运算:

declare -i a=1

let a=a+1

echo $a  # 2

let a+=1

echo $a  # 3

let指令支持算术运算符包括:

+:加法

-: 减法

*: 乘法

/: 除法

**: 乘方

%: 取余

let指令也支持算术运算符对应的算术赋值运算符,如+=。

 

4. 只读变量

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变

#!/bin/bash

y_id=1990

readonly y_id

echo "y_id:${y_id}"

 

y_id=2014

echo "y_id:${y_id}"

这里我们尝试修改只读变量y_id,运行时,会报错,提示该变量为只读变量:

 

5. 删除变量

使用 unset 命令可以删除变量,变量被删除后不能再次使用;

unset 命令不能删除只读变量。

#!/bin/bash

 

y_id=1990

y_name="yuguiyang"

echo "y_id:${y_id}"

echo "y_name:${y_name}"

 

readonly y_id

 

unset y_id

unset y_name

echo "y_id:${y_id}"

echo "y_name:${y_name}"


由于y_id 是只读变量,所以不会被删除,y_name被删除后,输出为空

结果:

 

6. 变量的作用范围

运行shell时,会同时存在三种变量:

1) 局部变量

局部变量在脚本或命令中定义,作用域仅限执行脚本的进程,即仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。

 

2) 环境变量(全局变量)

所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。子进程可以继承父进程的全局变量。环境变量配置保存于/etc/profile文件中。可以通过export来查看。也可以通过export来设置环境变量:export  xxx=”yyy”

set |grep xxx可以查看某个具体的变量的值

 

3) shell变量

shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行

 

7. 特殊变量

shell中预定义了一些特殊变量,通过这些变量可以获得环境信息:

●$$: 执行当前脚本的进程ID(pid)

●$?: 上一条命令的返回值,如果上一个命令成功执行则$?的值为0,否则为其他非零值

●$!: 上一条后台指令的执行进程的ID

上述变量在交互式终端中同样有效。

还有一些变量可以获得执行脚本时传入的参数:

●$0: 当前脚本的文件名

●$1~$n: 传给脚本的第n个参数

●$#: 传入脚本的参数的个数

●$@: 参数列表

●$*: 单个字符串形式的参数列表

 

 

数组

bash中可以使用圆括号定义数组,元素之间用空格分割,数组下标从0开始:

1. 定义

Shell脚本中支持一维数组,下标从0开始

我们可以这样

y_books[0]="today"

y_books[1]="onepiece"


可以这样:

y_books=("one" "two" "three")


还可以这样:

y_books=(

        "one"

        "two"

        "three")

2. 读取数组内容

我们使用下标来获取数组的信息

echo "book:${y_books[0]}"

echo "book:${y_books[1]}"

 

3. 获取所有的元素

使用*或者@

示例:

echo "books:${y_books[*]}"

echo "books:${y_books[@]}"

 

4. 获取数组长度

echo "数组长度"

echo "length:${#y_books[*]}"

echo "length:${#y_books[@]}"

 

 

 

读取键盘输入

之前我们都是直接将内容输出,那怎样可以交互呢?我们使用read命令,read可以直接使用而不需要在前面声明这个变量。

#!/bin/bash

echo "请输入你的名字:"

read y_name

echo "你好,${y_name}"

 

 

#!/bin/bash

echo ""

echo "姓名:"

read y_name

 

echo "年龄:"

read y_age

 

echo ""

echo "${y_name},今年${y_age}岁。"

 

 

运算符

Linux中有3类运算符:算术运算符、逻辑运算符、比较运算符

1. 算术运算符

在学习运算符之前我们先看个东西,之前没有注意到:

#!/bin/bash

 

y_price=10.9

y_total=${y_price}+3

echo "y_total:${y_total}"


y_total应该输出13.9?不是的,输出值为:10.9+3。因为Shell中默认把变量看作是字符串,所以才会显示成这样。要解决这个问题,我们需要使用let命令

let val="3 + 6"

echo "val:${val}"

 

a=5

b=9

let val="a + b"

echo "val:${val}"

 

let val="${a} + ${b}"

echo "val:${val}"


注意:等号(=)两边没有空格,而加号(+)两边有空格,且所有的运算符两边都要有空格。

 

从上面的例子可以发现:let表达式后,调用变量时可以不使用$符号let也可以使用(())代替

((val="10 + 30"))

echo "val:${val}"


注意:let只可以计算整数,不可以算浮点数

 

2. 逻辑运算符

 

3. 比较运算符

 

#!/bin/bash

a=3

b=4

test ${a} -eq ${b}

echo "$?"

我们这里使用test来判断a和b是否相等,$?可以返回真、假。对于运算符,我们先说到这,我们会在后面的练习中使用

 

 

条件控制语句: if

这里的if语句和其他开发语言中的差不多, Shell脚本中的if语句有3种

1. if ... then ... fi

 

Condition如果为真,则执行then后面的语句,为假则结束。这里需要注意的是:Condition和方括号之间需要有空格,否则就会报错

#!/bin/bash

 

echo "3+3=?"

read y_result

if [ ${y_result} -eq 6 ]

then

        echo "Ha,good."

fi

结果:

 

 

2.  if ... then ... else ... fi

 

这个多了个else,可以对不符合表达式时做些处理

#!/bin/bash

 

echo "3+3=?"

read y_result

if [ ${y_result} -eq 6 ]

then

        echo "Ha,good."

else

        echo "Oh,wrong."

Fi

 

 

3.  if ... elif ... fi

 

有时我们想要在else的时候,再做些判断,可以使用elif

#!/bin/bash

 

echo "3+3=?"

read y_result

if [ ${y_result} -eq 6 ]

then

        echo "Ha,good."

elif [ ${y_result} == -1 ]

then

        echo "Hehe,you find me."

Fi

 

<, >等运算符在[]中只能用于字符串的比较, 而在[[]]中<, >可以用于整型和字符串的大小比较, 也可以使用&&和||来书写逻辑表达式。

 

if的条件判断不一定使用[]或[[]]表达式,它可以是任何一个命令。命令的返回值为0则if判断为真, 非0判断为假。

 

[]和[[]]转义表达式也可以像普通指令一样执行,判断为真则返回0,假则返回非0值。

[ 2 -gt 1 -a 3 -lt 4 ] && echo 'ok'

 

判断字符串相等

if [ ${NAME} = 'tmp' ]; then
    echo "name is tmp"
fi

判断文件是否存在

if [ -e tmp ]; then
    echo "tmp exists"
fi 

判断tmp是否存在,tmp可以是目录或文件。

判断是否为普通文件

if [ -f tmp ]; then
    echo "file tmp exists"
fi

判断tmp是否为文件,tmp不能是目录。

判断是否为目录

if [ -d tmp ]; then
    echo "directory tmp exists"
fi

判断是否具有执行权限

if [ -x tmp ]; then
    echo "tmp is executable"
fi

不判断文件是否可执行,只判断是否拥有x权限。 因此,tmp为有x权限的目录时也会判断为真。类似的还有,-w判断是否拥有写入权限, -r判断是否拥有读取权限。

判断是否为空文件

if [ -s tmp ]; then
    echo "file is not empty"
fi

 

 

条件控制语句: case

条件控制语句还有一个case,对于需要多个elif的可以使用case尝试下

 

#!/bin/bash

echo "choose a number from 1 to 4."

read y_num

 

case ${y_num} in

1)

        echo "you select 1."

        ;;

2)

        echo "you select 2."

        ;;

3)

        echo "you select 3."

        ;;

4)

        echo "you select 4."

        ;;

*)

        echo "please choose a number from 1 to 4."

        ;;

esac


*可以代表一种默认情况

;; 与其他语言中的 break 类似,意思是跳到整个 case 语句的最后。

 

 

 

循环控制语句: while

前面我们介绍了条件控制语句,这里我们介绍下循环控制语句while

 

同样的,Condition左右都需要有空格,while循环和Java中的都差不多,这里也不赘述什么了

#!/bin/bash

 

clear

echo "while demo"

y_result="ygy"

 

while [ ${y_result} != "lufei" ]

do

        echo "Who are you ?"

        read y_result

done

 

echo "Haha"


这里我们判断y_result变量的内容是否为“lufei",否则一直循环

 

#!/bin/bash

clear

echo "乘法表"

 

let y=1

 

while [ ${y} -le 9 ]

do

        let x=1

        while [ ${x} -le ${y} ]

        do

                let rs="${x} * ${y}"

                printf "${x} * ${y} = ${rs}  "

                let x="${x} + 1"

        done

        let y="${y} + 1"

        echo ""

done

代码还好,就是个逻辑,相信大家会有更好的方法来实现

 

 

 

循环控制语句:for in

 

for会把wordlist中的值按顺序赋给变量,并执行循环体中的内容

wordlist是一个列表,我们看个例子就知道了

#!/bin/bash

 

echo ""

for loop in 1 2 3 4 5

do

        echo "hello ${loop} ."

done

一些命令的输出也可以作为序列:

for i in $(ls); do

  echo $i

done

 

函数

Shell脚本里面也可以定义函数,函数就像是脚本中的子脚本

1.函数的定义和调用

我们使用 function 来定义一个函数,需要用括号括起来

function show_menu {

        echo ""

        echo "1.显示所有联系人"

        echo "2.退出"

        echo ""

        echo "请选择:"

}

调用的话,直接使用函数名进行调用

 

下面的函数我们显示个界面,根据用户输入进行判断

#!/bin/bash

#显示菜单

function show_menu {

        echo ""

        echo "1.显示所有联系人"

        echo "2.退出"

        echo ""

        echo "请选择:"

}

 

#循环标识,为1时进行循环

let flag=1

while [ ${flag} == 1 ]

do

        #显示菜单信息

        show_menu

       

        #读取输入

        read rs

        case ${rs} in

                1)

                       echo "暂无联系人。"

                       ;;

                2)

                       echo "Bye."

                       let flag=0

                       ;;

                *)

                       echo "请选择1或者2"

                       ;;

        esac

done

 

2. 函数的参数

函数同样可以传递参数的,我们可以使用 ${1},${2}..来使用

#!/bin/bash

 

function logon {

        if [ ${1} == "lufei" ] && [ ${2} == "haha" ]

        then

                echo "登录成功!"

        else

                echo "登录失败!"

        fi

}

 

let flag=1

while [ ${flag} -eq 1 ]

do

        echo "请输入用户名:"

        read u_name

 

        echo "请输入密码:"

        read u_password

 

        logon ${u_name} ${u_password}

done


这里是一个简单的登录验证示例,我们将获取的用户名和密码传递给函数logon去验证

 

4. 参数个数验证

在这里调用函数的时候很可能参数不够,导致程序出错;我们可以使用$#来获取参数的个数进行判断

#!/bin/bash

 

function logon {

        #判断参数个数,2个参数就进行信息验证

        if [ $# -eq  2 ]

        then

 

                if [ ${1} == "lufei" ] && [ ${2} == "haha" ]

                then

                       echo "登录成功!"

                else

                       echo "登录失败!"

                fi

        else

                #提示错误信息

                echo "参数个数不符"

        fi

}

 

let flag=1

while [ ${flag} -eq 1 ]

do

        echo "请输入用户名:"

        read u_name

 

        echo "请输入密码:"

        read u_password

       

        #调用时,只传了一个参数

        logon ${u_name}

done

这个程序,暂时还不能退出,可以使用CTRL+C强制退出

 

 

 

5. 函数返回值

函数可以有返回值,return,但是这里的话,返回值必须是0~256之间的一个整数

#!/bin/bash

function test_return {

        echo "please input a number:"

        read  num

 

        return ${num}

}

 

test_return

echo "haha,you input: $?"

 


 

子程序

在实际开发中,我们的程序可能比较大,需要按模块开发,有不同的子程序

每个子程序都是独立的一个 文件,我们可以在一个主程序中调用他们

#!/bin/bash

 

clear

echo ""

echo "1.Add"

echo "2.Delete"

echo "3.Display"

echo "4.Quit"

 

read selection

case ${selection} in

"1")

        ./child_add

        ;;

"2")

        ./child_delete

        ;;

"3")

        ./child_display

        ;;

"4")

        ./child_quit

        ;;

*)

        echo "oh,no."

        ;;

esac


这是我们的一个主程序,我们根据输入,调用不同的程序

 

就子程序的话,先说到这,就是一个简单的例子

 

3. 显示文件中的内容

#!/bin/bash

echo "显示文件中的信息"

echo "f_users.bat"

echo "---------------"

cat f_users.bat

 

 

输出重定向

1. linux下常用的输入输出操作符

标准输入(stdin): < 或者 <<

标准输出(stdout): >或者 >>;

标准正确输出(stderr): 1>或者1>>   >或者 >>  ;

标准错误输出(stderr): 2>或者2>>;

2. 输出重定向

我们使用 > 或者 >>

我们使用 ls命令来显示2个文件,其中file02存在,而file03不存在,这样我们会输出一条错误信息,一条正确信息

1. 我们将正确信息输出到文件中

默认会将正确信息输出,所以这2种写法都可以

输出到文件同样使用echo命令:

1. 写入文件

#!/bin/bash

 

echo "write to file."

 

echo "iput your name:"

read y_name

 

echo "Hello,${y_name}" > f_users.bat


我们这里使用 > 将信息重定向到了f_users.bat这个文件中。如果原来已经有一个同名的文件,使用大于号(>)会覆盖这个文件

 

2. 追加信息

我们使用 >>可以向文件中追加信息

#!/bin/bash

echo "write to file."

echo "iput your name:"

read y_name

echo "Hello,${y_name}" >> f_users.bat

 

 

echo

在我们之前的例子中,我们经常使用echo命令将一些信息输出,这回我们来详细了解下echo这个命令

echo -n :输出后不会自动换行

echo -e :会对一些字符做特殊处理

 

printf

printf同样可以输出信,但printf没有像echo一样自动换行

printf可以对输出进行格式化

 

如果需要限定输出的宽度,格式为%flags width.precision format-specifier,width表示宽度,是整数,默认是右边对齐,如果需要左边对齐,在前面加“-”,

例如"%-20s"表示从左边开始对齐,宽度为20,如果字符串长度少于20,通过空格补齐,长度超过20,则会完全显示。

precision在浮点值中提供四舍五入。例如%5.6G,长度为5,精度为6。精度是可选的。长度和精度的值可以参数中指定,例如printf "%*.*G/n" 5 6 $myvalue。

长度指显示中占的字符长度,与字符长度的同义。如果长度比实际的少,例如实际字符长度更大或者所要求的精度更大,则显示按实际长度。

后台执行

shell可以执行一行指令后立即返回, 返回后可以通过$?变量获得执行进程的ID:

$ sleep 10 &

[1] 79403

$ echo $!

79403

 

多命令逻辑执行顺序

Linux中可以使用分号“;”、双and号“&&”和双竖线“||”来连接多个命令。单"&"符号也算命令连接符号,只不过它是将其前面的命令放入后台执行,所以可以变相地实现命令并行执行。

1.3.1 分号;

command1 ; command2

命令之间没有逻辑关系。分号连接的命令会按照顺序从前向后依次执行,但分号两端的命令之间没有任何逻辑关系,所有写出来的命令最终都会被执行,即使分号前面的命令出错也不影响后面的命令。

[root@xuexi ~]# ls das;echo "hdakl"

ls: cannot access das: No such file or directory

hdakl

1.3.2 &&

command1  &&  command2

逻辑与。&&连接的命令会按照顺序从前向后执行,但只有当command1正确执行才执行command2,如果command1不正确执行,则不执行command2。在bash中,通过预定义变量“$?”来判断命令是否正确执行,如果"$?"的值为0则表示前一条命令正确执行,其他任意值都表示不正确执行。

[root@xuexi ~]# echo "hdakl" && ls ds

hdakl

ls: cannot access ds: No such file or directory

[root@xuexi ~]# ls das && echo "hdakl"

ls: cannot access das: No such file or directory

1.3.3 ||

command1 || command2

逻辑或。||连接的命令会按照顺序从前向后执行,但只有当command1不正确执行才执行command2,command1正确执行则不会执行command2。||和&&都是短路符号,符号左右的命令之间具有逻辑关系。

[root@xuexi ~]# ls das || echo "hdakl"

ls: cannot access das: No such file or directory

hdakl

[root@xuexi ~]# echo "hdakl" || ls ds  

hdakl

一般要联合使用&&和||的时候,基本上都会先逻辑与再逻辑或:command1 && command2 || command3。因为在实际中,command2和command3应该都是想要执行的命令。如果command1正确执行,$?就等于0,执行command2,再看情况执行command3,如果command1错误执行,$?就不等于0,所以不执行command2,根据$?为非0值,判断了 || 右边的命令应该被执行。

通俗点的理解方法是根据语义判断。“如果...就...否则...就...”的语句使用“cmd1 && cmd2 || cmd3”,“如果不...就...否则...就...”使用“!cmd1 && cmd2 || cmd3”。

例如,如果用户user1存在,就显示用户已经存在,否则,就添加此用户。

[root@xuexi tmp]# id user1 && echo "user1 exists" || useradd user1

如果用户user2不存在,则添加此用户,否则显示用户已存在。

[root@xuexi tmp]# !id user2 && useradd user2 || echo "user2 exists"

如果用户user3不存在,则添加此用户,并设定其密码为用户名本身,否则显示用户已存在。

[root@xuexi tmp]# !id user3 && useradd user3 && echo "user3" | passwd --stdin user3 || echo "user3 exists"

1.3.4 &

command1 &

command1 & command2

&表示将其前面的命令放入后台执行,放入后台后会立即返回到bash环境让用户可以继续和bash交互。如果&符号连接了两个命令,则其前面的命令被放入后台,立即执行后面的命令,所以可以简单地认为这两个命令是并行执行的,两端的命令之间也没有任何逻辑关系。

需要注意的一点是,在终端的bash环境下,子shell中的后台的进程不受终端控制,在终端被关闭时它会挂靠在init/systemd进程下,因此退出终端或脚本shell环境,无法中断这些后台进程。例如:

[root@xuexi ~]# (sleep 10 &)     # 终端1上执行,立即关闭该终端

 

[root@xuexi ~]# ps aux | grep slee[p]     # 终端2上捕捉sleep进程

root 5732 0.0 0.0 107892 624 ? S 00:28 0:00 sleep 10

注意ps结果中的"?",它表示非终端进程,即脱离了终端。

 

转载于:https://www.cnblogs.com/tester-l/p/11398779.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值