SHELL 学习第二天记录打卡

SHELL 学习第二天记录打卡

学习转载:http://c.biancheng.net/view/734.html

变量的作用域

Shell 变量的作用域可以分为三种:

  • 有的变量可以在当前 Shell 会话中使用,这叫做全局变量(global variable)
  • 有的变量只能在函数内部使用,这叫做局部变量(local variable)
  • 而有的变量还可以在其它 Shell 中使用,这叫做环境变量(environment variable)

全局变量

在 Shell 中定义的变量,默认就是全局变量。
需要强调的是,全局变量的作用范围是当前的 Shell 会话,而不是当前的 Shell 脚本文件,它们是不同的概念。打开一个 Shell 窗口就创建了一个 Shell 会话,打开多个 Shell 窗口就创建了多个 Shell 会话,每个 Shell 会话都是独立的进程,拥有不同的进程 ID。在一个 Shell 会话中,可以执行多个 Shell 脚本文件,此时全局变量在这些脚本文件中都有效。

例如,现在有两个 Shell 脚本文件,分别是 a.sh 和 b.sh。a.sh 的代码如下:

#!/bin/bash
echo $a
b=200

b.sh 的代码如下:

#!/bin/bash
echo $b

打开一个 Shell 窗口,输入以下命令:

$ a=99
$ . ./a.sh
99
$ . b.sh
200
$

从输出结果可以发现,在 Shell 会话中以命令行的形式定义的变量 a,在 a.sh 中有效;在 a.sh 中定义的变量 b,在 b.sh 中也有效。

局部变量

在 Shell 函数中定义的变量默认也是全局变量,它和在函数外部定义变量拥有一样的效果。请看下面的代码:

#!/bin/bash
#定义函数
function func(){
a=99
}
#调用函数
func
#输出函数内部的变量
echo $a

输出结果:
99
a 是在函数内部定义的,但是在函数外部也可以得到它的值,证明它的作用域是全局的,而不是仅限于函数内部。

要想变量的作用域仅限于函数内部,那么可以在定义时加上local命令,此时该变量就成了局部变量。请看下面的代码:

 #!/bin/bash
 #定义函数
 function func(){
     local a=99
 }
 #调用函数
 func
 #输出函数内部的变量
 echo $a

输出结果为空,表明变量 a 在函数外部无效,是一个局部变量。

Shell 变量的这个特性和 JavaScript 中的变量是类似的。在 JavaScript 函数内部定义的变量,默认也是全局变量,只有加上var关键字,它才会变成局部变量。

环境变量

全局变量只在当前 Shell 会话中有效,如果使用export命令将它导出,那么它就在所有的子 Shell 中也有效了,这称为“环境变量”。

注意,环境变量只能向下传递而不能向上传递,即“传子不传父”。
在一个 Shell 中创建子 Shell 最简单的方式是运行 bash 命令
通过exit命令可以一层一层地退出 Shell。
下面演示一下环境变量的使用:

$ a=22      #定义一个全局变量
$ echo $a    #在当前Shell中输出a,成功
22
$ bash    #进入子Shell
$ echo $a    #在子Shell中输出a,失败

$ exit    #退出子Shell
exit
$ export a    #将a导出为环境变量
$ bash    #重新进入子Shell
$ echo $a    #在子Shell中再次输出a,成功
22
$ exit    #退出子Shell
exit
$ exit    #退出父Shell,结束整个Shell会话

可以发现,默认情况下,a 在子 Shell 中是无效的;使用 export 将 a 导出为环境变量后,在子 Shell 中就可以使用了。
注:用export的方式导入到环境变量是临时的,换一个shell就不能用这个变量了,如果想永久保存,就需要吧环境变量写入系统启动文件中。

位置参数

脚本文件内部可以使用$n的形式来接收,例如,$1 表示第一个参数,$2 表示第二个参数,依次类推。

1) 给脚本文件传递位置参数

请编写下面的代码,并命名为 test.sh:

#!/bin/bash
echo "Language: $1"
echo "URL: $2"

运行 test.sh,并附带参数:
$ . ./a.sh Shell http://c.biancheng.net/shell/
运行结果:
Language: Shell
URL: http://c.biancheng.net/shell/
其中Shell是第一个位置参数,http://c.biancheng.net/shell/是第二个位置参数。

2) 给函数传递位置参数

请编写下面的代码,并命名为 test.sh:

 #!/bin/bash
 #定义函数
 function func(){
     echo "Language: $1"
     echo "URL: $2"
 }
 #调用函数
 func C++ http://c.biancheng.net/cplus/

运行 test.sh:
$ . ./test.sh

注意事项
如果参数个数太多,达到或者超过了 10 个,那么就得用${n}的形式来接收了,例如 10 、 {10}、 10{23}。{ }的作用是为了帮助解释器识别参数的边界,这跟使用变量时加{ }是一样的效果。

Shell特殊变量

本节我们继续讲解剩下的几个特殊变量,它们分别是:$#、$*、$@、$?、$$。

变量 含义
$0 当前脚本的文件名。
$n(n≥1) 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是 $1,第二个参数是 $2。
$# 传递给脚本或函数的参数个数。
$* 传递给脚本或函数的所有参数。
$@ 传递给脚本或函数的所有参数。当被双引号" "包含时,$@ 与 $* 稍有不同,我们将在《Shell ∗ 和 *和 @的区别》一节中详细讲解。
$? 上个命令的退出状态,或函数的返回值,我们将在《Shell $?》一节中详细讲解。
$$ 当前 Shell 进程 ID。对于 Shell 脚本,就是这些脚本所在的进程 ID。

下面我们通过两个例子来演示。

给脚本文件传递参数

编写下面的代码,并保存为 test.sh:

#!/bin/bash
echo "Process ID: $$"
echo "File Name: $0"
echo "First Parameter : $1"
echo "Second Parameter : $2"
echo "All parameters 1: $@"
echo "All parameters 2: $*"
echo "Total: $#"

运行 test.sh,并附带参数:

$ chmod +x ./test.sh
$ ./test.sh Shell Linux

运行结果为:
Process ID: 2788
File Name: ./test.sh
First Parameter : Shell
Second Parameter : Linux
All parameters 1: Shell Linux
All parameters 2: Shell Linux
Total: 2

给函数传递参数

编写下面的代码,并保存为 test.sh:

#!/bin/bash
#定义函数
function func(){
    echo "Language: $1"
    echo "URL: $2"
    echo "First Parameter : $1"
    echo "Second Parameter : $2"
    echo "All parameters 1: $@"
    echo "All parameters 2: $*"
    echo "Total: $#"
}
#调用函数
func Java http://c.biancheng.net/java/

运行结果为:
Language: Java
URL: http://c.biancheng.net/java/
First Parameter : Java
Second Parameter : http://c.biancheng.net/java/
All parameters 1: Java http://c.biancheng.net/java/
All parameters 2: Java http://c.biancheng.net/java/
Total: 2

Shell $*和$@的区别

当 $* 和 $@ 不被双引号" "包围时,它们之间没有任何区别,都是将接收到的每个参数看做一份数据,彼此之间以空格来分隔。

但是当它们被双引号" "包含时,就会有区别了:

  • "$*"会将所有的参数从整体上看做一份数据,而不是把每个参数都看做一份数据。
  • "$@"仍然将每个参数都看作一份数据,彼此之间是独立的。

比如传递了 5 个参数,那么对于" ∗ " 来 说 , 这 5 个 参 数 会 合 并 到 一 起 形 成 一 份 数 据 , 它 们 之 间 是 无 法 分 割 的 ; 而 对 于 " *"来说,这 5 个参数会合并到一起形成一份数据,它们之间是无法分割的;而对于" "5"@"来说,这 5 个参数是相互独立的,它们是 5 份数据。

如果使用 echo 直接输出" ∗ " 和 " *"和" ""@"做对比,是看不出区别的;但如果使用 for 循环来逐个输出数据,立即就能看出区别来。

编写下面的代码,并保存为 test.sh:

#!/bin/bash
echo "print each param from \"\$*\""
for var in "$*"
do
    echo "$var"
done
echo "print each param from \"\$@\""
for var in "$@"
do
    echo "$var"
done

运行 test.sh,并附带参数:

 . ./test.sh a b c d

运行结果:
print each param from “ ∗ " a b c d p r i n t e a c h p a r a m f r o m " *" a b c d print each param from " "abcdprinteachparamfrom"@”
a
b
c
d

从运行结果可以发现,对于" ∗ " , 只 循 环 了 1 次 , 因 为 它 只 有 1 分 数 据 ; 对 于 " *",只循环了 1 次,因为它只有 1 分数据;对于" "11"@",循环了 5 次,因为它有 5 份数据。

Shell $?:获取函数返回值或者上一个命令的退出状态

$? 是一个特殊变量,用来获取上一个命令的退出状态,或者上一个函数的返回值。

所谓退出状态,就是上一个命令执行后的返回结果。退出状态是一个数字,一般情况下,大部分命令执行成功会返回 0,失败返回 1,这和C语言的 main() 函数是类似的。

不过,也有一些命令返回其他值,表示不同类型的错误。

1) $? 获取上一个命令的退出状态

我们使用两个脚本文件来演示。

先编写下面的代码,并保存为 a.sh:

#!/bin/bash
if [ $1 == 100 ]
then
   return 0  #参数正确,返回0
else
   return 1  #参数错误,返回1
fi

再编写下面的代码,并保存为 b.sh:

#!/bin/bash
echo $?

先运行 a.sh,传递参数 100,然后再运行 b.sh,结果如下:
$ . ./a.sh 100 $ . ./b.sh 0
如果将传递给 a.sh 的参数改为 89,b.sh 的运行结果就不同了:
$ . ./a.sh 89 $ . ./b.sh 1

2) $? 获取函数的返回值

编写下面的代码,并保存为 test.sh:

#!/bin/bash
#得到两个数相加的和
function add(){
    return `expr $1 + $2`
}
add 23 50  #调用函数
echo $?  #获取函数返回值

运行 test.sh:
$ . ./test.sh 73

Shell命令替换:将命名的输出结果赋值给变量

Shell 中有两种方式可以完成命令替换,一种是反引号,一种是$(),使用方法如下:

  • var_name=`command`
  • var_name=$(command)

其中,var_name 是变量名,command 是要输出的命令。

#!/bin/bash
DATE_01=`date`
DATE_02=$(date)
echo $DATE_01
echo $DATE_02

运行结果:
2018年 10月 31日 星期三 10:15:16 CST
2018年 10月 31日 星期三 10:15:16 CST

如果被替换的命令的输出内容包括多行(也即有换行符),或者含有多个连续的空白符,那么在输出变量时应该将变量用双引号包围,否则系统会使用默认的空白符来填充,这会导致换行无效,以及连续的空白符被压缩成一个。请看下面的代码:

#!/bin/bash
LSL=`ls -l`
echo $LSL  #不使用双引号包围
echo "--------------------------"  #输出分隔符
echo "$LSL"  #使用引号包围

运行结果:

total 8 drwxr-xr-x. 2 root root 21 7月 1 2016 abc -rw-rw-r--. 1 mozhiyan mozhiyan 147 10月 31 10:29 demo.sh -rw-rw-r--. 1 mozhiyan mozhiyan 35 10月 31 10:20 demo.sh~
--------------------------
total 8
drwxr-xr-x. 2 root     root      21 7月   1 2016 abc
-rw-rw-r--. 1 mozhiyan mozhiyan 147 10月 31 10:29 demo.sh
-rw-rw-r--. 1 mozhiyan mozhiyan  35 10月 31 10:20 demo.sh~

所以,为了防止出现格式混乱的情况,我建议在输出变量时加上双引号。

再谈反引号和 $()

原则上讲,上面提到的两种变量替换的形式是等价的,可以随意使用;但是,反引号毕竟看起来像单引号,有时候会对查看代码造成困扰,而使用 $() 就相对清晰,能有效避免这种混乱。而且有些情况必须使用 ( ) : (): ()() 支持嵌套,反引号不行。

下面的例子演示了使用计算 ls 命令列出的第一个文件的行数,这里使用了两层嵌套。

[root@localhost ~]# Fir_File_Lines=$(wc -l $(ls | sed -n '1p'))
[root@localhost ~]# echo "$Fir_File_Lines"
36 anaconda-ks.cfg

要注意的是,$() 仅在 Bash Shell 中有效,而反引号可在多种 Shell 中使用。所以这两种命令替换的方式各有特点,究竟选用哪种方式全看个人需求。

Shell字符串详解

字符串可以由单引号’ '包围,也可以由双引号" "包围,也可以不用引号。它们之间是有区别的,
字符串举例:

str1=c.biancheng.net
str2="shell script"
str3='C语言中文网'

下面我们说一下三种形式的区别:

1)由单引号’ '包围的字符串:

  • 任何字符都会原样输出,在其中使用变量是无效的。
  • 字符串中不能出现单引号,即使对单引号进行转义也不行。

2)由双引号" "包围的字符串:

  • 如果其中包含了某个变量,那么该变量会被解析(得到该变量的值),而不是原样输出。
  • 字符串中可以出现双引号,只要它被转义了就行。

3) 不被引号包围的字符串

  • 不被引号包围的字符串中出现变量时也会被解析,这一点和双引号" "包围的字符串一样。
  • 字符串中不能出现空格,否则空格后边的会作为其他变量或者字符串解析。

我们通过代码来演示一下三种形式的区别:

#!/bin/bash
n=74
str1=c.biancheng.net$n str2="shell \"script\" $n"
str3='C语言中文网 $n'
echo $str1
echo $str2
echo $str3

运行结果:
c.biancheng.net74 shell "script" 74 C语言中文网 $n

获取字符串长度

在 Shell 中获取字符串长度很简单,具体方法如下:
${#string_name}
string_name 表示字符串名字。

#!/bin/bash
str="http://c.biancheng.net/shell/"
echo ${#str}

运行结果:
29

Shell字符串拼接(连接、合并)

在 Shell 中你不需要使用任何运算符,将两个字符串并排放在一起就能实现拼接,非常简单粗暴。请看下面的例子:

#!/bin/bash
name="Shell"
url="http://c.biancheng.net/shell/"
str1=$name$url  #中间不能有空格
str2="$name $url"  #如果被双引号包围,那么中间可以有空格
str3=$name": "$url  #中间可以出现别的字符串
str4="$name: $url"  #这样写也可以
str5="${name}Script: ${url}index.html"  #这个时候需要给变量名加上大括号
echo $str1
echo $str2
echo $str3
echo $str4
echo $str5

运行结果:
Shellhttp://c.biancheng.net/shell/
Shell http://c.biancheng.net/shell/
Shell: http://c.biancheng.net/shell/
Shell: http://c.biancheng.net/shell/
ShellScript: http://c.biancheng.net/shell/index.html

Shell字符串截取

Shell 截取字符串通常有两种方式:从指定位置开始截取和从指定字符(子字符串)开始截取。

从指定位置开始截取

这种方式需要两个参数:除了指定起始位置,还需要截取长度,才能最终确定要截取的字符串。

既然需要指定起始位置,那么就涉及到计数方向的问题,到底是从字符串左边开始计数,还是从字符串右边开始计数。答案是 Shell 同时支持两种计数方式。

1) 从字符串左边开始计数

如果想从字符串的左边开始计数,那么截取字符串的具体格式如下:

${string: start :length}

其中,string 是要截取的字符串,start 是起始位置(从左边开始,从 0 开始计数),length 是要截取的长度(省略的话表示直到字符串的末尾)。
例如:
url="c.biancheng.net" echo ${url: 2: 9}
结果为biancheng
再如:
url="c.biancheng.net" echo ${url: 2} #省略 length,截取到字符串末尾
结果为biancheng.net

2) 从右边开始计数

如果想从字符串的右边开始计数,那么截取字符串的具体格式如下:
${string: 0-start :length}
同第 1) 种格式相比,第 2) 种格式仅仅多了0-,这是固定的写法,专门用来表示从字符串右边开始计数。
这里需要强调两点:

  • 从左边开始计数时,起始数字是 0(这符合程序员思维);从右边开始计数时,起始数字是 1(这符合常人思维)。计数方向不同,起始数字也不同。
  • 不管从哪边开始计数,截取方向都是从左到右。

例如:
url="c.biancheng.net" echo ${url: 0-13: 9}
结果为biancheng。从右边数,b是第 13 个字符。

再如:
url="c.biancheng.net" echo ${url: 0-13} #省略 length,直接截取到字符串末尾

结果为biancheng.net

从指定字符(子字符串)开始截取

这种截取方式无法指定字符串长度,只能从指定字符(子字符串)截取到字符串末尾。Shell 可以截取指定字符(子字符串)右边的所有字符,也可以截取左边的所有字符。

1) 使用 # 号截取右边字符

使用#号可以截取指定字符(或者子字符串)右边的所有字符,具体格式如下:
${string#*chars}
其中,string 表示要截取的字符,chars 是指定的字符(或者子字符串),*是通配符的一种,表示任意长度的字符串。*chars连起来使用的意思是:忽略左边的所有字符,直到遇见 chars(chars 不会被截取)。
请看下面的例子:
url="http://c.biancheng.net/index.html" echo ${url#*:}
结果为//c.biancheng.net/index.html

以下写法也可以得到同样的结果:
echo ${url#*p:} echo ${url#*ttp:}

如果不需要忽略 chars 左边的字符,那么也可以不写*,例如:
url="http://c.biancheng.net/index.html" echo ${url#http://}
结果为c.biancheng.net/index.html

注意,以上写法遇到第一个匹配的字符(子字符串)就结束了。例如:
url="http://c.biancheng.net/index.html" echo ${url#*/}
结果为/c.biancheng.net/index.html。url 字符串中有三个/,输出结果表明,Shell 遇到第一个/就匹配结束了。

如果希望直到最后一个指定字符(子字符串)再匹配结束,那么可以使用##,具体格式为:
${string##*chars}
请看下面的例子:

#!/bin/bash
url="http://c.biancheng.net/index.html"
echo ${url#*/}    #结果为 /c.biancheng.net/index.html
echo ${url##*/}   #结果为 index.html
str="---aa+++aa@@@"
echo ${str#*aa}   #结果为 +++aa@@@
echo ${str##*aa}  #结果为 @@@
2) 使用 % 截取左边字符

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

请注意*的位置,因为要截取 chars 左边的字符,而忽略 chars 右边的字符,所以*应该位于 chars 的右侧。其他方面%#的用法相同,这里不再赘述,仅举例说明:

#!/bin/bash
url="http://c.biancheng.net/index.html"
echo ${url%/*}  #结果为 http://c.biancheng.net
echo ${url%%/*}  #结果为 http:
str="---aa+++aa@@@"
echo ${str%aa*}  #结果为 ---aa+++
echo ${str%%aa*}  #结果为 ---

汇总
最后,我们对以上 8 种格式做一个汇总,请看下表:

格式 说明
${string: start :length} 从 string 字符串的左边第 start 个字符开始,向右截取 length 个字符。
${string: start} 从 string 字符串的左边第 start 个字符开始截取,直到最后。
${string: 0-start :length} 从 string 字符串的右边第 start 个字符开始,向右截取 length 个字符。
${string: 0-start} 从 string 字符串的右边第 start 个字符开始截取,直到最后。
${string#*chars} 从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。
${string##*chars} 从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。
${string%*chars} 从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 左边的所有字符。
${string%%*chars} 从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 左边的所有字符。

Shell数组:Shell数组定义以及获取数组元素

Shell 并且没有限制数组的大小,理论上可以存放无限量的数据。和C语言类似,Shell 数组元素的下标也是从 0 开始计数。

获取数组中的元素要使用下标[ ],下标可以是一个整数,也可以是一个结果为整数的表达式;当然,下标必须大于等于 0。

遗憾的是,常用的 Bash Shell 只支持一维数组,不支持多维数组。

Shell 数组的定义

在 Shell 中,用括号( )来表示数组,数组元素之间用空格来分隔。由此,定义数组的一般形式为:
array_name=(ele1 ele2 ele3 ... elen)
注意,赋值号=两边不能有空格,必须紧挨着数组名和数组元素。
下面是一个定义数组的实例:
nums=(29 100 13 8 91 44)
Shell 是弱类型的,它并不要求所有数组元素的类型必须相同,例如:
arr=(20 56 "http://c.biancheng.net/shell/")
第三个元素就是一个“异类”,前面两个元素都是整数,而第三个元素是字符串。

Shell 数组的长度不是固定的,定义之后还可以增加元素。例如,对于上面的 nums 数组,它的长度是 6,使用下面的代码会在最后增加一个元素,使其长度扩展到 7:
nums[6]=88
此外,你也无需逐个元素地给数组赋值,下面的代码就是只给特定元素赋值:
ages=([3]=24 [5]=19 [10]=12)

获取数组元素

获取数组元素的值,一般使用下面的格式:
${array_name[index]}

其中,array_name 是数组名,index 是下标。例如:
n=${nums[2]}
表示输出 nums 数组的第 3 个元素。
使用@*可以获取数组中的所有元素,例如:
${nums[*]} ${nums[@]}
两者都可以得到 nums 数组的所有元素。
完整的演示:

#!/bin/bash
nums=(29 100 13 8 91 44)
echo ${nums[@]}  #输出所有数组元素
nums[10]=66  #给第10个元素赋值(此时会增加数组长度)
echo ${nums[*]}  #输出所有数组元素
echo ${nums[4]}  #输出第4个元素

运行结果:
29 100 13 8 91 44
29 100 13 8 91 44 66
91

Shell获取数组长度

所谓数组长度,就是数组元素的个数。
利用@*,可以将数组扩展成列表,然后使用#来获取数组元素的个数,格式如下:
${#array_name[@]} ${#array_name[*]}
其中 array_name 表示数组名。两种形式是等价的,选择其一即可。
如果某个元素是字符串,还可以通过指定下标的方式获得该元素的长度,如下所示:
${#arr[2]}
获取 arr 数组的第 2 个元素(假设它是字符串)的长度。

回忆字符串长度的获取
回想一下 Shell 是如何获取字符串长度的呢?其实和获取数组长度如出一辙,它的格式如下:
${#string_name}
string_name 是字符串名。

实例演示

下面我们通过实际代码来演示一下如何获取数组长度。

#!/bin/bash
nums=(29 100 13)
echo ${#nums[*]}
#向数组中添加元素
nums[10]="http://c.biancheng.net/shell/"
echo ${#nums[@]}
echo ${#nums[10]}
#删除数组元素
unset nums[1]
echo ${#nums[*]}

Shell数组拼接,Shell数组合并

拼接数组的思路是:先利用@或*,将数组扩展成列表,然后再合并到一起。具体格式如下:
array_new=(${array1[@]} ${array2[@]}) array_new=(${array1[*]} ${array2[*]})
两种方式是等价的,选择其一即可。其中,array1 和 array2 是需要拼接的数组,array_new 是拼接后形成的新数组。
下面是完整的演示代码:

#!/bin/bash
array1=(23 56)
array2=(99 "http://c.biancheng.net/shell/")
array_new=(${array1[@]} ${array2[*]})
echo ${array_new[@]}  #也可以写作 ${array_new[*]}

运行结果:
23 56 99 http://c.biancheng.net/shell/

Shell删除数组元素(也可以删除整个数组)

在 Shell 中,使用 unset 关键字来删除数组元素,具体格式如下:
unset array_name[index]
其中,array_name 表示数组名,index 表示数组下标。

如果不写下标,而是写成下面的形式:
unset array_name
那么就是删除整个数组,所有元素都会消失。

下面我们通过具体的代码来演示:

#!/bin/bash
arr=(23 56 99 "http://c.biancheng.net/shell/")
unset arr[1]
echo ${arr[@]}
unset arr
echo ${arr[*]}

运行结果:
``
23 99 http://c.biancheng.net/shell/

``

Shell内建命令(内置命令)

所谓 Shell 内建命令,就是由 Bash 自身提供的命令,而不是文件系统中的某个可执行文件

可以使用 type 来确定一个命令是否是内建命令:
[root@localhost ~]# type cd cd is a Shell builtin [root@localhost ~]# type ifconfig ifconfig is /sbin/ifconfig
由此可见,cd 是一个 Shell 内建命令,而 ifconfig 是一个外部文件,它的位置是/sbin/ifconfig

$PATH 变量包含的目录中几乎聚集了系统中绝大多数的可执行命令,它们都是外部命令。

通常来说,内建命令会比外部命令执行得更快,执行外部命令时不但会触发磁盘 I/O,还需要 fork 出一个单独的进程来执行,执行完成后再退出。而执行内建命令相当于调用当前 Shell 进程的一个函数。

Shell 的内建命令众多,在 3.2.25 版本的 Bash 中有几十个,如下所示:

bash : . [ alias bg bin
break builtin cd command compgen complete continue
declare dirs disown echo enable eval exec
exit export fc fg getopts hash help
history jobs kill let local logout popd
printf pushd pwd read readonly return set
shift shopt source suspend test times trap
type typeset ulimit umask unalias unset wait

Shell alias:给命令创建别名

alias 用来给命令创建一个别名。若直接输入该命令且不带任何参数,则列出当前 Shell 环境中使用了哪些别名。现在你应该能理解类似ll这样的命令为什么与ls -l的效果是一样的吧。
alisa 是一个 Shell 内建命令。
下面让我们来看一下有哪些命令被默认创建了别名:
[root@localhost ~]# alias alias cp='cp -i' alias l.='ls -d .* --color=tty' alias ll='ls -l --color=tty' alias ls='ls --color=tty' alias mv='mv -i' alias rm='rm -i' alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
你看,为了让我们使用方便,Shell 会给某些命令默认创建别名。

使用 alias 当然也可以自定义别名,比如说一般的关机命令是shutdown-h now,写起来比较长,这时可以重新定义一个关机命令,以后就方便多了。使用 alias 定义的别名命令也是支持 Tab 键补全的,如下所示:
alias myShutdown='shutdown -h now'

alias myShutdown=‘shutdown -h now’
注意,这样定义别名只能在当前 Shell 环境中有效,换句话说,重新登录后这个别名就消失了。为了确保永远生效,可以将该别名手动写入到用户主目录中的.bashrc文件。
.bashrc其实也是一个 Shell 脚本文件,该文件专门用来存放用户自定义的别名和函数。
将别名写入.bashrc文件后的效果如下所示:
# .bashrc # User specific aliases and functions alias myShutdown='shutdown -h now' # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi

unalias:删除别名

使用 unalias 内建命令可以删除当前 Shell 环境中的别名。unalias 有两种使用方法:

  • 第一种用法是在命令后跟上某个命令的别名,用于删除指定的别名。
  • 第二种用法是在命令后接-a参数,删除当前 Shell 环境中所有的别名。

同样,这两种方法都是在当前 Shell 环境中生效的。要想永久删除在.bashrc文件中定义的别名,只能进入该文件手动删除。

# 删除 ll 别名
[root@e-bai ~]# unalias ll
# 再次运行该命令时,报“找不到该命令”的错误,说明该别名被删除了
[root@e-bai ~]# ll
-bash: ll: command not found

Shell echo命令:输出字符串

echo 是一个 Shell 内建命令,用来在终端输出字符串,并在最后默认加上换行符。请看下面的例子:

#!/bin/bash
name="Shell教程"
url="http://c.biancheng.net/shell/"
echo "读者,你好!"  #直接输出字符串
echo $url  #输出变量
echo "${name}的网址是:${url}"  #双引号包围的字符串中可以解析变量
echo '${name}的网址是:${url}'  #单引号包围的字符串中不能解析变量

运行结果:
读者,你好! http://c.biancheng.net/shell/ Shell教程的网址是:http://c.biancheng.net/shell/ ${name}的网址是:${url}

不换行
echo 命令输出结束后默认会换行,如果不希望换行,可以加上-n参数,如下所示:

#!/bin/bash
name="Tom"
age=20
height=175
weight=62
echo -n "${name} is ${age} years old, "
echo -n "${height}cm in height "
echo "and ${weight}kg in weight."
echo "Thank you!"

输出转义字符
默认情况下,echo 不会解析以反斜杠\开头的转义字符。比如,\n表示换行,echo 默认会将它作为普通字符对待。请看下面的例子:
[root@localhost ~]# echo "hello \nworld" hello \nworld

我们可以添加-e参数来让 echo 命令解析转义字符。例如:
[root@localhost ~]# echo -e “hello \nworld”
hello
world

\c 转义字符
有了-e参数,我们也可以使用转义字符\c来强制 echo 命令不换行了。请看下面的例子:

#!/bin/bash
name="Tom"
age=20
height=175
weight=62
echo -e "${name} is ${age} years old, \c"
echo -e "${height}cm in height \c"
echo "and ${weight}kg in weight."
echo "Thank you!"

Shell exit命令:退出Shell

exit 是一个 Shell 内置命令,用来退出当前 Shell:

  • 如果在终端中直接运行 exit 命令,会退出当前登录的 Shell,并关闭终端;
  • 如果在 Shell 脚本中出现 exit 命令,会停止执行后边的所有代码,立即退出 Shell 脚本。

exit 命令可以接受的参数是一个状态值 n,代表退出时的状态。如果不指定,默认状态值是 0。

以下面的 Shell 脚本(命名为 demo.sh)为例:

#!/bin/bash
echo "befor exit"
exit 8
echo "after exit"

运行该脚本:
[root@localhost ~]# bash ./demo.sh befor exit
可以看到,"after exit"并没有输出,这说明遇到 exit 命令后,demo.sh 执行就结束了。

我们可以紧接着使用 $? 来获取 demo.sh 的退出状态:
[root@localhost ~]# echo $? 8

Shell ulimit命令:显示并设置进程资源限度

系统的可用资源是有限的,如果不限制用户和进程对系统资源的使用,则很容易陷入资源耗尽的地步,而使用 ulimit 命令可以控制进程对可用资源的访问(ulimit 是一个 Shell 内置命令)。

默认情况下 Linux 系统的各个资源都做了软硬限制,其中硬限制的作用是控制软限制(换言之,软限制不能高于硬限制)。使用ulimit -a可以查看当前系统的软限制,使用命令ulimit -a –H可查看系统的硬限制。

下面是该命令的运行结果,笔者对每行输出都做了注解。

[root@localhost ~]# ulimit -a
#core文件大小,单位是block,默认为0
core file size          (blocks, -c) 0
#数据段大小,单位是kbyte,默认不做限制
data seg size           (kbytes, -d) unlimited
#调度优先级,默认为0
scheduling priority             (-e) 0
#创建文件的大小,单位是block,默认不做限制
file size               (blocks, -f) unlimited
#挂起的信号数量,默认是8192
pending signals                 (-i) 8192
#最大锁定内存的值,单位是kbyte,默认是32
max locked memory       (kbytes, -l) 32
#最大可用的常驻内存值,单位是kbyte,默认不做限制
max memory size         (kbytes, -m) unlimited
#最大打开的文件数,默认是1024
open files                      (-n) 1024
#管道最大缓冲区的值
pipe size            (512 bytes, -p) 8
#消息队列的最大值,单位是byte
POSIX message queues     (bytes, -q) 819200
#程序的实时性优先级,默认为0
real-time priority              (-r) 0
#栈大小,单位是kbyte
stack size              (kbytes, -s) 10240
#最大cpu占用时间,默认不做限制
cpu time               (seconds, -t) unlimited
#用户最大进程数,默认是8192
max user processes              (-u) 8192
#最大虚拟内存,单位是kbyte,默认不做限制
virtual memory          (kbytes, -v) unlimited
#文件锁,默认不做限制
file locks                      (-x) unlimited

每一行中都包含了相应的改变该项设置的参数,以最大可以打开的文件数为例(open files 默认是 1024),想要增大至 4096 则按照如下命令设置(可参照此方法调整其他参数)。

#设置最大打开的文件数
#该命令会同时设置硬限制和软限制
[root@localhost ~]# ulimit -n 4096
#使用-S参数单独设置软限制
#[root@localhost ~]# ulimit -S -n 4096
#使用-H参数单独设置硬限制
#[root@localhost ~]# ulimit -H -n 4096

使用 ulimit 直接调整参数,只会在当前运行时生效,一旦系统重启,所有调整过的参数就会变回系统默认值。所以建议将所有的改动放在 ulimit 的系统配置文件中。相关配置方法请参考笔者对相关配置文件的注释。

[root@localhost ~]# cat /etc/security/limits.conf
# /etc/security/limits.conf
#该文件是ulimit的配置文件,任何对系统的ulimit的修改都应该写入该文件
#请将所有的设置写到该文件的最后
#Each line describes a limit for a user in the form:
#配置应该写成下面这行的格式,即每个配置占用1行,每行4列
#每列分别是   
#            
#
#其中:
#可以取的值如下:
#       - 一个用户名
#       - 一个组名,组名前面用@符号
#       - 通配符*
#       - 通配符%
#Where:
# can be:
#        - an user name
#        - a group name, with @group syntax
#        - the wildcard *, for default entry
#        - the wildcard %, can be also used with %group syntax,
#                 for maxlogin limit
#
#只有以下两个可用值:
#       - soft用于设置软限制
#       - hard用于设置硬限制
# can have the two values:
#        - "soft" for enforcing the soft limits
#        - "hard" for enforcing hard limits
#
#的值可以是以下任意一种:
#        - core - core文件大小的限制 (KB)
#        - data - 最大数据段限制 (KB)
#        - fsize - 最大文件大小 (KB)
#        - memlock - 最大锁定的内存大小 (KB)
#        - nofile - 最大打开文件数
#        - rss - 最大常驻内存值 (KB)
#        - stack - 最大栈空间大小 (KB)
#        - cpu - 最大CPU使用时间 (MIN)
#        - nproc - 最大进程数
#        - as - 虚拟地址空间
#        - maxlogins - 某用户的最大登录数
#        - maxsyslogins - 系统用户最大登录数
#        - priority - 用户进程的运行优先级
#        - locks – 用户最大可以锁定文件的数量
#        - sigpending - 最大挂起的信号量数
#        - msgqueue - POSIX信号队列使用的最大内存值 (bytes)
#        - nice - 最大nice值
#        - rtprio - 最大实时优先级
#
# can be one of the following:
#        - core - limits the core file size (KB)
#        - data - max data size (KB)
#        - fsize - maximum filesize (KB)
#        - memlock - max locked-in-memory address space (KB)
#        - nofile - max number of open files
#        - rss - max resident set size (KB)
#        - stack - max stack size (KB)
#        - cpu - max CPU time (MIN)
#        - nproc - max number of processes
#        - as - address space limit
#        - maxlogins - max number of logins for this user
#        - maxsyslogins - max number of logins on the system
#        - priority - the priority to run user process with
#        - locks - max number of file locks the user can hold
#        - sigpending - max number of pending signals
#        - msgqueue - max memory used by POSIX message queues (bytes)
#        - nice - max nice priority allowed to raise to
#        - rtprio - max realtime priority
#
#                 
#
#以下是使用样例,请参照配置
#*               soft    core            0
#*               hard    rss             10000
#@student        hard    nproc           20
#@faculty        soft    nproc           20
#@faculty        hard    nproc           50
#ftp             hard    nproc           0
#@student        -       maxlogins       4

你可能感兴趣的:(SHELL,第二天)