Linux_Shell脚本学习第一章-小试牛刀(上)
1. Shell脚本简介
简单的说,当命令或者程序不在命令行执行,而是通过一个程序文件来执行,这个程序就被称为shell脚本。
也就是在shell脚本里内置了多条命令,语句,循环控制,然后将这些命令一次性执行完毕。
2. 在终端中显示输出
2.1 shell脚本的编写及执行
以hello world脚本程序分析
#!/bin/bash
#only is a test shell
echo "hello world"
代码执行方式
a.将脚本名作为命令行参数
[email protected]:/work/project/linux_shell$ bash test.sh
hello world
b.授予脚本执行权限,将其变为可执行文件:
[email protected]:/work/project/linux_shell$ chmod +x ./test.sh
[email protected]:/work/project/linux_shell$ ./test.sh
hello world
代码分析
1. shell脚本都会以#!/bin/bash为起始。/bin/bash是Bash的解释器命令路径。
2. 注释部分以#为起始,一直延续到行尾。注释行通常用于描述代码或是在调试期间禁止执行某行代码。
2.2 echo命令
echo是用于终端打印的最基本命令。
默认情况下,echo在每次调用后会添加一个换行符:
使用echo命令的方式有三种
1. echo "hello world"
2. echo 'hello world'
3. echo hello world
这些方法看起来相似,但各有特定的用途及副作用。双引号允许shell解释字符串中出现的特殊字符。单引号不会对其做任何解释。例如:
echo hello; hello;
bash会将echo hello当作一个命令,将hello当作另一个命令,因为分号在Bash shell中用作命令间的分隔符。
3. 使用变量
#!/bin/bash
#only is a test shell
fruit=appel
count=5
echo "We have $count ${fruit}s"
- 可以使用等号操作符为变量赋值: varName=value
注意:var = value不同于var=value。把var=value写成var = value是一个常见的错误。
两边没有空格的等号是赋值操作符,加上空格的等号表示的是等量关系测试。 - 在变量名之前加上美元符号$ 就可以访问变量的内容。
注意: 使用单引号时,变量不会被扩展(expand),仍依照原样显示。这意味着$ echo 'var。 - 因为shell使用空白字符来分隔单词,所以我们需要加上一对花括号来告诉shell这里的变量名是fruit,而不是fruits。
4.数学运算
4.1 数学运算时不能直接相加
#!/bin/bash
#only is a test shell
no1=5
no2=6
result=$no1+$no2
echo $result
[email protected]:/work/project/linux_shell$ ./test.sh
5+6
4.2 let命令
#!/bin/bash
#only is a test shell
no1=5
no2=6
let result=no1+no2
echo $result
a.let命令可以直接执行基本的算术操作。当使用let时,变量名之前不需要再添加$
let result=no1+no2
b.let命令的其他用法如下:
自加操作: let no1++
自减操作: let no1--
简写形式: let no+=6 let no-=6
c.只支持整数运算
4.3 []操作符
#!/bin/bash
#only is a test shell
no1=5
no2=6
result=$[no1+no2]
echo $result
a.操作符[]的使用方法和let命令一样,但是需要在[]外加上$:
result=$[ no1 + no2 ]
b.在[]中也可以使用$前缀,例如:
result=$[ $no1 + 6 ]
c.只支持整数运算
4.4 ( ( ) )操作符
a.与[]操作符一样,出现在(())中的变量名之前需要加上$:
result=$(( no1 + 50 ))
b. 只支持整数运算
4.5 bc工具
#!/bin/bash
#only is a test shell
echo "4*0.56" | bc
no=54
result=`echo "$no * 1.5" | bc`
echo $result
a. 执行脚本效果如下
[email protected]:/work/project/linux_shell$ ./test.sh
2.24
81.0
b. ``操作符将echo输出的值赋值给result
c. bc工具支持浮点运算
d.设定小数精度。
在下例中参数scale=2将小数位个数设置为2。因此,bc将会输出包含个小数位的数值:
echo "scale=2;22/7" | bc
3.14
e. 进制转换。用bc可以将一种进制系统转换为另一种。
#!/bin/bash
#用途:数字转换
no=100
echo "obase=2;$no" | bc
1100100
no=1100100
echo "obase=10;ibase=2;$no" | bc
100
f.计算平方及平方根
echo "sqrt(100)" | bc
10
echo "5^2" | bc
25
5.文件描述符与重定向
5.1 文件描述符操作
a. 文件描述符是与某个打开的文件或数据流相关联的整数。
文件描述符0、1以及2是系统预留的。
0 —— stdin (标准输入)。
1 —— stdout(标准输出)。
2 —— stderr(标准错误)。
b.使用大于号将文本保存到文件中:
$ echo "This is a sample text 1" > temp.txt
该命令会将输出的文本保存在temp.txt中。如果temp.txt已经存在,大于号会清空该文件中
先前的内容。
c. 使用双大于号将文本追加到文件中:
$ echo "This is sample text 2" >> temp.txt
d.使用cat查看文件内容:
$ cat temp.txt
This is sample text 1
This is sample text 2
5.2 重定向
a. stderr重定向
当命令产生错误信息时,该信息会被输出到stderr流。
$ ls +
ls: cannot access +: No such file or directory
下面的命令会将stderr文本打印到屏幕上,而不是文件中(因为stdout并没有输出,所以
out.txt的内容为空):
$ ls + > out.txt
ls: cannot access +: No such file or directory
使用2>(数字2以及大于号)将stderr重定向到out.txt:
$ ls + 2> out.txt
你可以将stderr和stdout分别重定向到不同的文件中:
$ cmd 2>stderr.txt 1>stdout.txt
重定向stderr和stdout到同一个文件中:
$ cmd 2>&1 alloutput.txt
或者
$ cmd &> output.txt
b.tee命令
tee命令从stdin中读取,然后将输入数据重定向到stdout以及一个或多个文件中。
在下面的代码中,tee命令接收到来自stdin的数据。它将stdout的一份副本写入文件
out.txt,同时将另一份副本作为后续命令的stdin。命令cat -n为从stdin中接收到
的每一行数据前加上行号并将其写入stdout:
由于 | 操作符,使得 cat a*的输出作为标准输入(个人理解)
$ cat a* | tee out.txt | cat -n
cat: a1: Permission denied
1 A2
2 A3
使用cat查看out.txt的内容:
$ cat out.txt
A2
A3
注意,cat: a1: Permission denied 并没有在文件内容中出现,因为
这些信息被发送到了stderr,而tee只能从stdin中读取。
默认情况下,tee命令会将文件覆盖,但它提供了一个-a选项,可用于追加内容。
$ cat a* | tee -a out.txt | cat –n
要发送输入内容的两份副本给stdout,使用-作为命令的文件名参数即可:
$ cmd1 | cmd2 | cmd -
例如:
$ echo who is this | tee -
who is this
who is this
5.3 工作原理
a. 重定向操作符(>和>>)可以将输出发送到文件中,而不是终端。>和>>略有差异。尽管两者都可以将文本重定向到文件,但是前者会先清空文件,然后再写入内容,而后者会将内容追加到现有文件的尾部。
b. 默认情况下,重定向操作针对的是标准输出。如果想使用特定的文件描述符,你必须将描述符编号置于操作符之前。
c. >等同于1>;对于>>来说,情况也类似(即>>等同于1>>)。
d. 处理错误时,来自stderr的输出被倾倒入文件/dev/null中。./dev/null是一个特殊的设备文件,它会丢弃接收到的任何数据。null设备通常也被称为黑洞,因为凡是进入其中的数据都将一去不返。
6.数组与关联数组
6.1 数组的定义和使用
a. 可以在单行中使用数值列表来定义一个数组:
array_var=(test1 test2 test3 test4)
#这些值将会存储在以0为起始索引的连续位置上
另外,还可以将数组定义成一组“索引值”:
array_var[0]="test1"
array_var[1]="test2"
array_var[2]="test3"
array_var[3]="test4"
b.打印出特定索引的数组元素内容:
echo ${array_var[0]}
test1
index=3
echo ${array_var[$index]}
test4
c.以列表形式打印出数组中的所有值:
$ echo ${array_var[*]}
test1 test2 test3 test4
也可以这样使用:
$ echo ${array_var[@]}
test1 test2 test3 test4
d. 打印数组长度(即数组中元素的个数)
$ echo ${#array_var[*]}
4
6.2 关联数组的定义和使用
a.关联数组的定义
在关联数组中,我们可以用任意的文本作为数组索引。首先,需要使用声明语句将一个变量
定义为关联数组:
$ declare -A ass_array
声明之后,可以用下列两种方法将元素添加到关联数组中。
(1) 使用行内“索引.值”列表:
$ ass_array=([index1]=val1 [index2]=val2)
(2) 使用独立的“索引值”进行赋值
$ ass_array[index1]=val1
$ ass_array'index2]=val2
b.关联数组的使用
关联数组通过任意的文本作为数组索引
#!/bin/bash
#only is a test shell
declare -A fruits_array
fruits_array=([apple]='100 dollars' [orange]='150 dollars')
echo "Apple costs ${fruits_array[apple]}
c.列出数组索引
每一个数组元素都有对应的索引。普通数组和关联数组的索引类型不同。我们可以用下面的
方法获取数组的索引列表:
$ echo ${!array_var[*]}
也可以这样
$ echo ${!array_var[@]}
以先前的fruits_value数组为例,运行如下命令:
$ echo ${!fruits_value[*]}
orange apple
对于普通数组,这个方法同样可行。
6.3 别名
a.别名的定义
别名就是一种便捷方式,可以为用户省去输入一长串命令序列的麻烦。下面我们会看到如何
使用alias命令创建别名。
b.别名的使用
(1) 创建别名。
$ alias new_command='command sequence'
下面的命令为apt-get install创建了一个别名:
$ alias myinstall='sudo apt-get install'
定义好别名之后,我们就可以用myinstall来代替sudo apt-get install了。
(2) alias命令的效果只是暂时的。一旦关闭当前终端,所有设置过的别名就失效了。为了
使别名在所有的shell中都可用,可以将其定义放入~/.bashrc文件中。每当一个新的交互式
shell进程生成时,都会执行 ~/.bashrc中的命令。
$ echo 'alias myinstall="sudo apt-get install"' >> ~/.bashrc
(3) 如果需要删除别名,只需将其对应的定义(如果有的话)从~/.bashrc中删除,或者使用
unalias命令。也可以使用alias example=,这会取消别名example。如下:
$ alias myinstall=
或者
$ unalias myinstall
(4) 我们可以创建一个别名rm,它能够删除原始文件,同时在backup目录中保留副本。
alias rm='cp [email protected] ~/backup && rm [email protected]'
#[email protected]表示传入的全部参数
注意:创建别名时,如果已经有同名的别名存在,那么原有的别名设置将被新的
设置取代。
(5) 对别名进行转义
如果身份为特权用户,别名也会造成安全问题。为了避免对系统造成危害,你应该将命令
转义。
创建一个和原生命令同名的别名很容易,你不应该以特权用户的身份运行别名化的命令。我
们可以转义要使用的命令,忽略当前定义的别名:
$ \command
(6) 列举别名
alias命令可以列出当前定义的所有别名:
[email protected]:/tmp$ alias
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
alias egrep='egrep --color=auto'
alias example='myinstall'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -alF'
alias ls='ls --color=auto'
alias myinstall=''
alias rm='cp [email protected] /work/project/linux_shell/bake_up && rm [email protected]'
7.采集终端信息(行数、列数、光标位置、遮盖的密码字段等)
7.1 tput工具
a. 获取终端的行数和列数
tput cols
tput lines
b.打印出当前终端名
tput longname
c. 将光标移动到坐标(100,100)处:
tput cup 100 100
d. 设置终端背景色:
tput setb n
其中,n可以在0到7之间取值。
e.设置终端前景色:
tput setf n
其中,n可以在0到7之间取值。
f.设置文本样式为粗体:
tput bold
g.设置下划线的起止:
#删除下划线
tput smul
#添加下划线
tput rmul
h.删除从当前光标位置到行尾的所有内容:
tput ed
注意:以上的命令的效果都只是暂时的。一旦关闭当前终端,所有设置效果就失效了。为了
使效果所有的shell中都可用,可以将其放入~/.bashrc文件中。每当一个新的交互式
shell进程生成时,都会执行 ~/.bashrc中的命令。
7.2 stty工具
a. 输入密码时,脚本不应该显示输入内容。在下面的例子中,我们将看到如何使用stty来
实现这一需求:
#!/bin/sh
#Filename: password.sh
#echo -e表示**转义字符
echo -e "Enter password: "
# 在读取密码前禁止回显
stty -echo
read password
# 重新允许回显
stty echo
echo Password read.
stty命令的选项-echo禁止将输出发送到终端,而选项echo则允许发送输出。
8.获取并设置日期延时
8.1 日期的读取和设置
a.读取日期:
$ date +%s
1556084972
b. 打印纪元时:
$ date +%s
1290047248
data命令可以将很多不同格式的日期转换成纪元时。这就允许你使用多种日期格式作为
输入。如果要从系统日志中或者其他标准应用程序生成的输出中获取日期信息,就完全
不用烦心日期的格式问题。
将日期转换成纪元时:
$ date --date "Wed mar 15 08:09:16 EDT 2017" +%s
1489579718
选项–date指定了作为输入的日期。我们可以使用任意的日期格式化选项来打印输出。
data命令可以根据指定的日期找出这一天是星期几:
$ date --date “Jan 20 2001” +%A
Saturday
c.用带有前缀+的格式化字符串作为date命令的参数,可以按照你的选择打印出相应格式
的日期。
具体含义可以使用date --h来查看
$ date “+%d %B %Y”
20 May 2010
d. 设置日期和时间:
date -s "21 June 2009 11:01:22"
如果系统已经联网,可以使用ntpdate来设置日期和时间:
/usr/sbin/ntpdate -s time-b.nist.gov
e.要优化代码,首先得先进行测量。date命令可以用于计算一组命令所花费的执行时间:
#!/bin/bash
#文件名: time_take.sh
start=$(date +%s)
commands;
statements;
end=$(date +%s)
difference=$(( end - start))
echo Time taken to execute commands is $difference seconds.
注意:date命令的最小精度是秒。对命令计时的另一种更好的方式是使用time命令:
time commandOrScriptName