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.shb.sha.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:.[aliasbgbin
breakbuiltincdcommandcompgencompletecontinue
declaredirsdisownechoenableevalexec
exitexportfcfggetoptshashhelp
historyjobskillletlocallogoutpopd
printfpushdpwdreadreadonlyreturnset
shiftshoptsourcesuspendtesttimestrap
typetypesetulimitumaskunaliasunsetwait

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列
#每列分别是<domain> <type> <item> <value>
#<domain>        <type>  <item>  <value>
#
#其中:
#<domain>可以取的值如下:
#       - 一个用户名
#       - 一个组名,组名前面用@符号
#       - 通配符*
#       - 通配符%
#Where:
#<domain> 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
#
#<type>只有以下两个可用值:
#       - soft用于设置软限制
#       - hard用于设置硬限制
#<type> can have the two values:
#        - "soft" for enforcing the soft limits
#        - "hard" for enforcing hard limits
#
#<item>的值可以是以下任意一种:
#        - 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 - 最大实时优先级
#
#<item> 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
#
#<domain>      <type>  <item>         <value>
#
#以下是使用样例,请参照配置
#*               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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值