Shell——变量与脚本运行的方式

变量

其他编程语言中,例如C语言变量是具有多种类型的:整型、浮点型、字符型、逻辑类型等等

但是shell中的变量一切皆为字符串


定义变量的几种方式

shell为变量赋值时,=左右两边不能有空格,例如a=1表示赋值关系,如果写成a = 1则表示等量关系测试

1)无引号

  • 解析特殊符号
  • 无法解析特殊命令
  • 无法输入空格
age=18

2)单引号

  • 不解析一切特殊符号、命令
  • 可以输入空格
  • 所见即所得
name='echo zhang san'

3)双引号

  • 解析特殊符号
  • 无法解析特殊命令
  • 可以输入空格
phone_num="123 456"

4)反引号

  • 解析特殊符号
  • 解析特殊命令
  • 可以输入空格
a='echo hello world!'
b=`${
     
     a}`
echo ${b}

上述代码最终会输出 hello world!,过程分为2步:

  • 解析了特殊符号${a},把a中存储的字符串拿出来
  • 发现字符串是特殊命令,再对该字符串进行解析,最终把打印的结果赋给b

变量的作用域与export和local

变量在当前bash环境中创建,如果不对其进行删除,其作用域是创建位置——>当前bash环境结束,在终端脚本以及函数中都能对其进行访问。变量作用域需要先理解父子shell的关系,该内容在最后一节。

1)export

如果变量a在当前bash环境中创建,那么进入子bash后将无法对其进行识别。

针对这种情况,可以利用export对变量进行修饰,被export修饰过的变量可以在子bash环境中被访问。

例如,在命令端输入如下命令:

[root@learning ~] a=1
[root@learning ~] echo 'echo "${a}"' > print_a.sh
[root@learning ~] bash print_a.sh
                #无法访问变量a,打印出空值
[root@learning ~] export a
[root@learning ~] bash print_a.sh
1               #打印结果为1,说明export定义的变量子bash可以访问

上述代码中:

  • 以bash运行脚本会开辟一个子bash环境,而该环境无法访问父bash中定义的变量
  • 在变量前面加入export进行修饰,可以访问该变量;export可以在变量声明时修饰,也在声明后再修饰

注意:export定义的变量只对子bash有用,子bash对变量进行修改不会影响到父bash,只影响当前bash环境中变量的值

[root@learning ~] export a=1
[root@learning ~] bash  #进入一个子bash环境
[root@learning ~] export a=2
[root@learning ~] bash
[root@learning ~] echo $a
2
[root@learning ~] exit  #退出当前bash环境
exit
[root@learning ~] exit
exit
[root@learning ~] echo $a
1

上述例子很明显:

  • 父bash用export定义的变量子bash可以访问
  • 子bash更改变量的值无法影响到父bash
  • 父子bash环境中变量的关系是变量的临时拷贝,类似C语言中函数的传值调用。

注:export同样对函数有用

2)loacl

在函数中,如果不想变量在函数以外的地方被访问,那么可以用loacl对函数中的变量进行修饰,方法如下:

#函数中不加loacl
$ cat > hello.sh << "EOF"
#!/bin/bash
function hello()
{
   print=hello
 }
hello      #运行函数
EOF
echo $print
hello

#函数中加local
cat > hello.sh << "EOF"
#!/bin/bash
function hello()
{
   local print=hello
}
hello
EOF
echo $print
                    #打印不出来print的值

显然:

  • 函数中正常创建的变量,在函数运行后,终端可以访问其变量的值
  • 函数中用loacl修饰的变量,在函数运行后,终端无法访问其变量的值

变量的提取、修改与删除

1)变量的提取

变量提取的格式如下:

name="变量名"
${name}  #推荐使用该方法
$name

上述方法中,为了减少bug并且增加代码的可读性,推荐使用${}的方法。

2)变量的修改

直接对变量重新赋值即可完成变量的修改

name="zhangsan"
name="lishi"

3)变量的删除

利用unset命令可以指删除指定变量,unset支持一次删除多个变量;如果有一系列特征相同的的变量,可以通过set | grep对变量进行查找,再根据查找的结果进行删除

name1="zhangsan"
name2="lisi"
name3="wangwu"
set | grep "^name"

其中:set表示查找所有变量,包括系统变量,|表示管道,gerp “^name”表示找到以name开头的变量

运行结果如下:

name1=zhangsan
name2=lisi
name3=wangwu

然后即可通过unset对变量进行删除

unset name1 name2 name3

特殊变量

特殊变量也遵循变量提取的基本语法,即${};为了方便书写,下列代码采取简写。

1)位置参数

在linux中,执行一条命令或脚本时,后面可以输入多个参数,这些参数统称位置参数;其中${0}表示执行的命令或脚本,${1}表示第一个位置参数,${2}表示第二个位置参数,依次往后。

编写一个脚本pos_param.sh

#!/bin/bash
echo "执行的脚本或命令是${0}"
echo "第一个位置参数是${1}"
echo "第二个位置参数是${2}"
echo "第三个位置参数是${3}"
echo "第四个位置参数是${4}"
echo "第五个位置参数是${5}"

运行脚本

bash pos_param.sh 1111 2222 3333 4444 5555

结果如下

执行的脚本或命令是pos_param.sh
第一个位置参数是1111
第二个位置参数是2222
第三个位置参数是3333
第四个位置参数是4444
第五个位置参数是5555

注:当位置参数的个数小于10时,{}可以省略,例:$0,$1,$2等;而10以上的位置参数则必须以${}的形式,例:${10}、${11}等


位置参数练习:创建新用户

cat > makeuser.sh << "EOF"
#!bin/bash
useradd ${1}
echo "${2}" | passwd --stdin ${1}
EOF
bash makeuser.sh lisi 1234

注:该方式修改用户密码的方式不适用于ubuntu

运行成果,会报如下提示:

Changing password for user lisi.
passwd: all authentication tokens updated successfully.

2)${#}获取传入参数的个数

echo 'echo "共传入$#个参数"' >> pos_param.sh 

bash pos_param.sh 1111 2222 3333 4444 5555

在1)的基础上,继续在脚本中添加如下命令,再次运行1)中命令执行脚本,会打印如下内容:

共传入5个参数

显然,$#会获取传入脚本中所有参数的个数。

3)${$}获取当前进程ID

继续添加如下命令:

echo 'echo "当前进程ID是$$"' >> pos_param.sh 

bash pos_param.sh 1111 2222 3333 4444 5555

打印内容下:

当前进程ID是14109

14109则是pos_param.sh的进程ID号

4)${*}和${@}接收所有参数

${*}和${@}均为接收所有参数,但二者又略有不同

执行下列代码:

cat >> pos_param.sh << "EOF"
echo "\$*接收的参数为:$*" 
echo "\$@接收的参数为:$@" 

echo "用for循环打印\$*接收的参数为:"
count=0;
for a in "$*"
do 
let count++ 
echo "第${count}个位置参数是:${a}"
done

echo "用for循环打印\$@接收的参数为:"
count=0;
for b in "$@"
do 
let count++ 
echo "第${count}个位置参数是:${b}"
done
EOF

bash pos_param.sh 111 222 333 

运行结果如下:

$*接收的参数为:111 222 333
$@接收的参数为:111 222 333
用for循环打印$*接收的参数为:
第1个位置参数是:111 222 333
用for循环打印$@接收的参数为:
第1个位置参数是:111
第2个位置参数是:222
第3个位置参数是:333

显然:二者都能够接收传入的参数

  • ${*}是将所有参数作为一个字符串进行整体存储,无法分开
  • ${@}是将所有参数进行分开存储

5)${?}获取上一条命令执行结果

在平时写代码中,例如C语言:我们常常利用返回值来判断某个函数、程序的运行结果是否正确,shell脚本中也是如此

shell中如果返回值为0则表示运行正确,非0则表示出错

例如:

[wjj@why1472587 ~]$ ls 777
ls: cannot access 777: No such file or directory
[wjj@why1472587 ~]$ echo $?
2
[wjj@why1472587 ~]$ ls
study
[wjj@why1472587 ~]$ echo $?
0

6)$()获取命令

$()中()中的内容不再视为变量,而是作为命令进行处理。

例如:通过whoami命令可以查看当前用户,可以有如下用法:

echo "当前用户:$(whoami)"

Shell脚本的两种执行方式

首先编写一个打印Hello World!的脚本

cat > test1.sh << 'EOF' 
#!/bin/bash
echo "Hello World!"
EOF

1)添加执行权限运行

chmod u+x test1.sh
./test1.sh

2)无需执行权限,以解释器运行

  • bash运行
  • source运行
  • .运行(需要文件开头有shebang)
#1
bash test1.sh
#2
source test1.sh
#3
. tesh1.sh #文件开头需要有shebang

1.bash、source和 . 运行的区别

  • 使用source.运行时,是在当前bash环境中运行的
  • 使用bash运行时,会额外创建一个子bash环境

以pstree -p命令可打开当前linux系统运行的进程,并且查看PID进程号

1)以source.运行:

在这里插入图片描述

2)以bash运行:

在这里插入图片描述

脚本运行结束后,会退出当前的子bash环境

2.什么是shebang

shebang是一个文本行,放在shell脚本的第一行,告诉计算机当前脚本采用什么解释器运行

#!/bin/bash

其中,#!表示这是一个shebang,/bin/bash为解释器路径。

上述shebang表示该脚本用bash解释器运行;如果是python脚本,则需要改为:

#!/bin/python

shell和python作为解释型语言可以通过加入shebang的方式,直接用解释器运行脚本;而其他编译型语言,例如:C/C++、Java等需要先编译才能运行,并且需要安装编译时所需要的环境。

如果脚本第一行中没有声明shebang,那么系统会默认以当前bash环境运行脚本。


常量

C语言可以通过#define 定义常量,又或者利用const将变量修饰成常变量,使变量具有常量的属性。

但是shell中并没有严格对常量与变量进行区别,一般来说:将符合以下两点的变量当做常量使用:

  • 以纯大写形式对变量命名(约定俗成的定义方式)
  • 通过readonly将变量设置成只读,不能更改

例如:

[wjj@learning ~]$ readonly MAX_READ=100
[wjj@learning ~]$ echo $MAX_READ
100
[wjj@learning ~]$ MAX_READ=200
-bash: MAX_READ: readonly variable

如上:通过readonly将变量设置成只读,对只读的变量再次赋值会报错readonly variable

注意:unset命令无法删除只读变量,bash中没有提供删除可读变量的方法。当退出该bash环境时,所创建的一切变量自动销毁。

猜你喜欢

转载自blog.csdn.net/why1472587/article/details/128511552
今日推荐