0%

Shell简介与基础编程

Linux.Shell简介


什么是Shell

Shell是核心程序(Kernel)之外的指令解析器,是一个程序,同时也是一种命令语言和程序设计语言。

Shell的类型

目前流行的 Shell 有ash、bash、ksh、csh、zsh等,它们各自都有自己的特点,可以根据自己的需要选择相应的Shell,现在在诸多Linux发行版本中默认使用的shell是 Bash Shell

ash

ash shell 是由Kenneth Almquist编写的,Linux中占用系统资源最少的一个小shell,它只包含24个内部命令,因而使用起来很不方便。

bash

bash 是Linux系统默认使用的shell,它由Brian Fox和Chet Ramey共同完成,是Bourne Again Shell的缩写,内部命令一共有40个(可使用help命令查看)。Linux使用它作为默认的shell是因为它有诸如以下的特色:

  • 可以使用类似DOS下面的doskey的功能,用方向键查阅和快速输入并修改命令。
  • 自动通过查找匹配的方式给出以某字符串开头的命令。
  • 包含了自身的帮助功能,你只要在提示符下面键入help就可以得到相关的帮助。

ksh

ksh 是Korn shell的缩写,由Eric Gisin编写,共有42条内部命令。该shell最大的优点是几乎和商业发行版的ksh完全兼容,这样就可以在不用花钱购买商业版本的情况下尝试商业版本的性能了

csh

csh是Linux比较大的Shell,它由以William Joy为代表的共计47位作者编成,共有52个内部命令。该shell其实是指向/bin/tcsh这样的一个shell,也就是说,csh其实就是tcsh。

zsh

zch 是Linux最大的shell之一,由Paul Falstad完成,共有84个内部命令。如果只是一般的用途,是没有必要安装这样的shell的。

变量和运算符


什么是Shell变量

Shell变量其实就是放在内存中的一定的存储单元,而这个存储单元里存放着这个变量的值,这个值是可以进行改变的。

变量类型

本地变量(局部变量)

本地变量(局部变量)只在创建它们的shell中可用

例:

# lmx=4 #新建本地变量,并将其赋值为4

然后用set命令查看:

# set
...
XDG_RUNTIME_DIR=/run/user/0
XDG_SESSION_ID=8
_=
colors=/root/.dircolors
lmx=4

注:set命令可以显示本地所有的变量

接着将本shell注销重新登录或者新开启一个shell,执行set命令:

# set
...
XDG_RUNTIME_DIR=/run/user/0
XDG_SESSION_ID=10
_=PATH
colors=/root/.dircolors

发现之前设置的lmx变量不见了,因为本地变量(局部变量)只在创建它们的shell中可用。

附:readonly < variable_name > #设置只读变量

# lmx=4
# readonly lmx #创建本地变量lmx,并设置其只读

环境变量

环境变量可以在创建它们的shell及其派生出来的任意子进程中使用。它们通常被称为全局变量,以区别于本地变量(局部变量),通常环境变量应该大写。环境变量是已经用export内置命令导出的变量。

变量被创建时所处的shell被称为父shell。如果父shell又启动了一个shell,这个新的shell被称作子shell。环境变量将传递给从创建它们的shell里启动的任意子进程。它们从父亲传递给儿子再到孙子等,但是不可向其他方向传递。比如,一个子进程可以创建环境变量,但不能将它传回给它的父进程,只能传给它的子进程。有一些环境变量,比如HOME、LOGNAME、PATH和SHELL,在用户登录之前就已经被/bin/login程序设置好了。通常,环境变量定义并保存在用户主目录下的.bash_profile文件中。

如果想设置环境变量,就要在给变量赋值之后或设置变量时使用export命令:

# lmx=4
# export lmx
# export #查看环境变量
...
declare -x TERM="xterm"
declare -x USER="root"
declare -x XDG_RUNTIME_DIR="/run/user/0"
declare -x XDG_SESSION_ID="10"
declare -x lmx="4"

设置环境变量后在父sehll中启动一个子shell,再次使用export查看环境变量:

# /bin/bash #启动一个子shell
# export
...
declare -x TERM="xterm"
declare -x USER="root"
declare -x XDG_RUNTIME_DIR="/run/user/0"
declare -x XDG_SESSION_ID="10"
declare -x lmx="4"

发现lmx变量还在,可见环境变量可以创建它的shell中传递到子shell中。

变量替换

${Variable_name:+value}

如果设置了Variable_name,则显示value,否则为空

${Variable_name:?value}

如果未设置Variable_name,显示用户定义的错误信息value

${Variable_name:-value}

如果未设置Variable_name,则显示其值value,否则显示变量的值

${Variable_name:=value}

如果未设置Variable_name,设置其值为value,并显示,否则显示变量的值

变量清除

unset Variable_name

注:只读变量(readonly)不可以unset

位置变量

$0

表示脚本名称

$1

表示传递给脚本或shell命令的第一个参数

……

$9

表示传递给脚本或shell命令的第九个参数

例如:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
#parm.sh
echo "名字:$0"
echo "第1个参数: $1"
echo "第2个参数: $2"
echo "第3个参数: $3"
echo "第4个参数: $4"
echo "第5个参数: $5"
echo "第6个参数: $6"
echo "第7个参数: $7"
echo "第8个参数: $8"
echo "第9个参数: $9"

运行命令如下

./parm A B C D E F G H I
第1个参数: A
第2个参数: B
第3个参数: C
第4个参数: D
第5个参数: E
第6个参数: F
第7个参数: G
第8个参数: H
第9个参数: I

注:利用 $num 的方式来表示位置变量最多只能到$9,不可以超过9个

标准变量

bash默认建立了一些标准环境变量,可在/etc/profile中定义,例如:HOME、LOGNAME、MAIL、MAILCHECK

特殊变量

  • $# 传递到脚本中的参数个数
  • $* 以一个单字符显示所有向脚本中传递的参数。与位置变量不同,此项参数可超过9个
  • $$ 脚本运行的当前进程ID号
  • $? 显示最后命令的退出状态,0表示没有错误,其他任何值表明有错误
  • $- 显示shell使用的当前选项,与set命令功能相同
  • $! 后台运行的最后一个进程的进程ID号

影响变量的命令

declare

设置或显示变量

-f 只显示函数名

-r 创建只读变量

-i 创建整数变量

export

用于创建传给子shell的变量,即环境变量

-n 把环境变量转换成本地变量(局部变量),换句话说,变量不再传给子shell

-p 显示环境变量列表

shift [n]

用于移动位置变量,调整位置变量,使用$3的值赋予$2

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash
#parm.sh
echo "名字:$0"
echo "第1个参数: $1"
echo "第2个参数: $2"
echo "第3个参数: $3"
echo "第4个参数: $4"
echo "第5个参数: $5"
echo "第6个参数: $6"
echo "第7个参数: $7"
echo "第8个参数: $8"
echo "第9个参数: $9"
shift 1
echo "名字:$0"
echo "第1个参数: $1"
echo "第2个参数: $2"
echo "第3个参数: $3"
echo "第4个参数: $4"
echo "第5个参数: $5"
echo "第6个参数: $6"
echo "第7个参数: $7"
echo "第8个参数: $8"
echo "第9个参数: $9"

运行命令如下:

# ./test.sh A B C D E F G H I
名字:./test.sh
第1个参数: A
第2个参数: B
第3个参数: C
第4个参数: D
第5个参数: E
第6个参数: F
第7个参数: G
第8个参数: H
第9个参数: I
名字:./test.sh
第1个参数: B
第2个参数: C
第3个参数: D
第4个参数: E
第5个参数: F
第6个参数: G
第7个参数: H
第8个参数: I
第9个参数: 

双引号

使用双引号引用除字符$、`、\ 外的任意字符或字符串

例如:

# hostname="$HOSTNAME"
# echo $hostname
server1.example.com

单引号

会忽略任何引用值。换句话说,如果屏蔽了其特殊含义,会将引号里的所有字符,包括引号都作为一个字符串

例如:

# hostname='$HOSTNAME'
# echo $hostname
$HOSTNAME

反引号

将反引号中的内容作为一个系统命令,并执行其内容

例如:

# echo `date`
Thu Jul 30 20:52:47 CST 2015

反斜杠

屏蔽有特殊含义的特殊字符

$[ ]

表示形式告诉shell对方括号中的表达式求值,例如:

# echo $[2+8]
10

$[base#n]

base进制的数n,例如:

# echo $[10#8+1]
9

Shell的输入与输出


echo

显示文本行或变量或者把字符串输入到文件中

echo [option] string

-e 解析转义字符

-n 回车不换行,Linux系统默认回车换行

read

从键盘或文件的某一行文本中读入信息并将其赋给一个变量

read variable1 variable2

例如:

1
2
3
4
5
6
7
#!/bin/bash
#readname
echo -n "first name:"
read firstname
echo -n "last name:"
read lastname
echo -e "your name is :\n$firstname $lastname"

运行命令如下:

./test.sh 
first name:koen
last name:li
your name is :
koen li

标准输入、标准输出与标准错误

在 Shell 执行命令时,每个进程都和三个打开的文件相联系,并使用文件描述符来引用这些文件。由于文件描述符不易记忆,Shell同时也给出了相应的文件名:

文件名 文件描述符
输入文件-标准输入 0(缺省是屏幕,也可以是文件)
输出文件-标组输出 1(缺省是键盘,也可以是文件或其他命令的输出)
错误输出文件-标准错误 2(缺省是屏幕,也可以是文件)

系统实际上有12个文件描述符,可以任意使用文件描述符3到9

文件重定向

  • command 1> filename #把标准输出重定向到一个新文件中

  • command > filename 2>&1 #把标准输出和标准错误都重定向到filename中

  • command >> filename 2>&1 #把标准输出和标准错误都重定向追加到filename中

  • command << delimiter #从标准输入读入数据,直到delimiter遇到delimiter结束

  • command < &m #文件描述符m作为标准输入

  • command > &m #把标准输出重定向到文件描述符m中

  • command < &- #关闭标准输入

exec

可用来代替当前的Shell,换句话说,并没有启动子Shell,使用这一命令时任何现有环境都将会被清除,并重新启动一个Shell

exec command

其中command通常是一个Shell脚本

控制流结构


if语句

语法格式一:

if 条件1;then
    命令1
fi

语法格式二:

if 条件1;then
    命令1
else
    命令2
fi

语法格式三:

if 条件1;then
    命令1
elif 条件2;then
    命令2
else
    命令3
fi

例如:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
#if
age=18
if [ $age -ge 18 ];then
echo "you are adult!"
elif [ $age -ge 13 ];then
echo "you are teenager!"
else
echo "you are child!"
fi

运行命令如下:

# ./test.sh 
you are adult!

case语句

语法格式:

case 变量 in
模式1)
    命令1
    ;;
模式2)
    命令2
    ;;
*)
    命令3
    ;;
esax

例如:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
#case
case $1 in
dog)
echo "you are dog!"
;;
cat)
echo "you are cat!"
;;
*)
echo "I don't konw what you are!"
esac

运行命令如下:

# ./test.sh dog
you are dog!
# ./test.sh cat
you are cat!
# ./test.sh
I don't konw what you are!

for循环

语法格式:

for 变量名 in 列表
do
    命令1
    命令2
done

例如:

1
2
3
4
5
6
#!/bin/bash
#for
for loop in 1 2 3 4 5
do
echo $loop
done

运行命令如下:

# ./test.sh 
1
2
3
4
5

until循环

语法格式:

until 条件
do
    命令1
    命令2
done

注:条件可为任意测试条件,测试发生在循环末尾,因此循环至少执行一次

例如:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
#until
part=/mnt/yum
LOOK_OUT=`df |grep $part |awk '{print $5}' |sed 's/%//g'`
echo $LOOK_OUT
until [ "$LOOK_OUT" -gt "90" ]
do
echo "filesystem is nearly full" | mail root
LOOK_OUT=`df |grep $part |awk '{print $5}' |sed 's/%//g'`
sleep 36000
done

运行命令如下:

# ./test.sh 
100

While循环

语法格式:

while 条件/命令
do
    命令1
    命令2
done

注:在while和do之间虽然通常只使用一条命令,但可以放几条命令,命令通常用作测试条件

例如:

1
2
3
4
5
6
7
8
#!/bin/bash
#while
while :
do
date
sleep 3
done
echo "sum is $sum"

运行命令如下:

# ./test.sh 
Thu Jul 30 20:21:20 CST 2015
Thu Jul 30 20:21:23 CST 2015
...

break [n]

退出循环,如果是在一个嵌套循环中,可以通过n来指定跳出的循环个数

continue

跳出循环步

文本过滤


正则表达式

一种用来描述文本模式的特殊语法

正则表达式语言由两种基本字符类型组成:原义(正常)文本字符和元字符。元字符使正则表达式具有处理能力。所谓元字符就是指那些在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。

字符 含义 字符 含义
^ 只匹配行首 $ 只匹配行尾
* 匹配0个或多个在*之前的字符 [ ] 只匹配[]内字符,可以是一个单字符,也可以是符号序列,使用-表示[ ]内字符序列范围,如用[1-5]代替[12345]
\ 只用来屏蔽一个元字符的特殊含义 . 只匹配任意单字符
pattem\ {n\ } 只用来匹配前面pattem出现次数,n为次数 pattem\ {n,\ } 含义同上,但次数最少为n
pattem\ {n,m\ } 含义同上,但pattem出现次数在n和m之间

Find命令

一般形式:

find pathname -option [-print -exec -ok]

常用选项

-print 将匹配的文件输出到标准输出

-exec 对匹配的文件执行该参数所给出的shell命令,相应命令的形式为command {} \;

-ok 与-exec的作用相同,只不过以一种更安全的模式来执行该参数所给出的shell命令,在执行一个命令之前,都会给出提示,让用户来确定是否执行

更多帮助:man find

Xargs

在使用find命令的-exec选项处理匹配到的文件时,find命令将所有匹配到的文件一起传递给exec。不幸的是,有些系统对能够传递给exec的命令长度有限制,这样在find命令运行几分钟之后,就会出现溢出错误。错误信息通常是“参数列太长”或“参数列溢出”。这就是Xargs命令的用处所在,特别是与find命令一起使用。exec会发起多个进程,而Xargs不会多个,只有一个。

# find / -perm -7 -print | xargs chmod o-w

Grep命令

对文本进行模式查找

一般格式:

grep [选项] 基本正则表达式 [文件]

三种变形

  • Grep 标准grep命令
  • Egrep 扩展grep,支持基本及扩展的正则表达式
  • Fgrep 快速grep

常用选项

-c 只输出匹配行的计数
-i 不区分大小写(只使用于单字符)
-h 查询多文件时,不显示文件名
-H 显示文件名
-l 查询多文件时,只输出包含匹配字符的文件名
-n 显示匹配的行及行号
-s 不显示不存在或无匹配文本的错误信息
-v 显示不包含匹配文本的所有行

Grep命令类名

grep命令类名 等价正则表达式或含义 grep命令类名 等价正则表达式或含义
[[:upper:]] [A-Z] [[:space:]] 空格或tab键
[[:alnum:]] [0-9a-zA-Z] [[:digit:]] [0-9]
[[:lower:]] [a-z] [[:alpha:]] [a-zA-Z]

匹配IPv4地址:[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}

Awk命令

可从文件或字符串中基于指定规则浏览和抽取信息,是一种自解释的编程语言

三种调用方式

  • 命令行方式:awk [-F filed-spearator] 'command' input_files
  • awk脚本
  • awk命令插入一个单独文件 awk -f awk-script-file input-files

模式和动作

Awk脚本由各种动作和模式组成:

模式部分决定动作语句何时触发及触发事件(BEGIN、END)
动作对数据进行处理,放在大括号{ }内指明(print)

分割符、域和记录

awk执行时,其浏览域标记为$1、$2、…$n,这种方法称为域标识,$0为所有域

例如:

# awk -F : '{print $0"\t"$4}' /etc/passwd

# awk 'BEGIN{print "IP\n------"} {print $1"\t"$4} END{print "IP\n------"}' score.txt

awk中的特殊字符

  • + 匹配任意字符
  • ? 匹配单个字符

awk中的匹配操作符

  • ~ 表示匹配
  • !~ 表示不匹配

例如:

# cat score.txt | awk '$0~/218.79.131.96/'

# awk '{if($1=="218.79.131.96") print $0}' score.txt

更过帮助:man awkinfo awk

Sed命令

Sed不与初始化文件打交道,它操作的只是一个拷贝,然后所有的改动如果没有重定向到一个文件中,将输出到屏幕

三种调用方式

  • 命令行格式:sed [选项] sed_command input_files
  • sed脚本文件 sed [选项] -f sed_script_file input_files
  • sed脚本文件 [选项] 输入文件

Sed在文件中查询文本的方式

  • 使用行号,可以是一个简单数字,或是一个行号范围
  • 使用正则表达式

x x为一行号

x,y 表示行号范围x到y

/pattern/ 查询包含模式的行

/pattern/pattern/ 查询包含两个模式的行

pattern/,x 在给定行号上查询包含模式的行

x,/pattern/ 通过行号和模式查询匹配行

x,y! 查询不包含指定行号x和y的行

基本Sed编辑命令

p 打印匹配行

= 显示文件行号

a\ 在定位行号后附加新文本信息

i\ 在定位行号前插入新文本信息

d 删除定位行

c\ 用新文本替换定位文本

s 使用替换模式替代相应模式

r 从另一个文本中读文本

w 写文本到一个文件

选项

-n 不打印未匹配的

例如:

sed '2p' score.txt

sed -n '2p' score.txt #打印第2行

sed -n '4,/los/p' score.txt #打印第4行到第一次匹配los的行

sed -n '/^$/=' score.txt #显示空行行号

sed -n -e '/^$/p' -e '/^$/=' myfile.txt

sed -n '/chinaitlab/a\shenzhen' myfile.txt #匹配到chinaitlab后在其后面添加文本

sed -n '/chinaitlab/& hello/p' myfile.txt #匹配到后在后面加上hello

Sort命令

常用选项

-c 测试文件是否已经分类(排序)

-m 合并两个分类(排序)文件

-u 删除所有重复行

-o 存储sort结果的输出文件名

-t 域分隔符

-r 比较求逆

- +n n为域号,使用此域号开始分类

-n 指定分类是域上的数字分类项

Uniq命令

从一个文本文件中去除或禁止重复行

常用选项

-u 只显示不重复行

-d 只显示有重复数据行,每种重复行只显示其中一行

-c 打印每一重复行出现次数

-f [n] n为数字,前n个域被忽略

Split命令

用来将大文件分割成小文件

###一般格式:###

split -output_file_size input_filename output_filename

常用选项

-b n 每个分割文件的大小为n(k,m)

-C n 每个分割文件一行最多n字节数

-l n 每个分割文件的行数

-n 同-l n

例如:

# split -10 ls_out.txt splita

Shell 函数


什么是函数

Shell允许将一组命令集或语句形成一个可用块,这些块称为Shell函数

函数定义与调用

定义方法一:

函数名()
{
    命令1
    ...
}

定义方法二:

function 函数名()
{
    命令1
    ...
}

函数可以放在同一个文件中作为一段代码,也可以放在只包含函数的单独文件中

例如:

1
2
3
4
5
6
7
8
9
#/bin/bash
#func
function hello()
{
echo "Hello,today is `date`"
}
echo "now going to the function hello"
hello
echo "back from the function"

运行命令如下:

# ./test.sh 
now going to the function hello
Hello,today is Thu Jul 30 20:33:04 CST 2015
back from the function

向函数传递参数就像在脚本中使用位置变量\$1、\$2…\$9

例如:

1
2
3
4
5
6
7
8
9
#/bin/bash
#func
function hello()
{
echo "Hello,$1 today is `date`"
}
echo "now going to the function hello"
hello shenzhen
echo "back from the function"

运行命令如下:

# ./test.sh 
now going to the function hello
Hello,shenzhen today is Thu Jul 30 20:35:55 CST 2015
back from the function

函数文件

例如:

# vim hellofun.sh
1
2
3
4
5
6
7
#/bin/bash
#hello fun
function hello()
{
echo "Hello,$1 today is `date`"
return 1
}
# vim test.sh
1
2
3
4
5
6
7
#/bin/bash
#func
# Source function
. hellofun.sh
echo "now going to the function hello"
hello shenzhen
echo "back from the function"

运行命令如下:

# ./test.sh 
now going to the function hello
Hello,shenzhen today is Thu Jul 30 20:42:31 CST 2015
back from the function

检查载入函数和删除函数

检查载入函数:set

例如:

1
2
3
4
5
6
7
8
#/bin/bash
#func
# Source function
. hellofun.sh
set
echo "now going to the function hello"
hello shenzhen
echo "back from the function"

运行命令如下:

# ./test.sh
...
XDG_RUNTIME_DIR=/run/user/0
XDG_SESSION_ID=10
_=hellofun.sh
lmx=4
hello () 
{ 
    echo "Hello,$1 today is `date`";
    return 1
}
now going to the function hello
Hello,shenzhen today is Thu Jul 30 20:45:23 CST 2015
back from the function

删除函数:unset 函数名

例如:

1
2
3
4
5
6
7
8
9
10
#/bin/bash
#func
# Source function
. hellofun.sh
set
echo "now going to the function hello"
hello shenzhen
unset hello
set
echo "back from the function"

运行命令如下:

# ./test.sh
...
XDG_RUNTIME_DIR=/run/user/0
XDG_SESSION_ID=10
_=hellofun.sh
lmx=4
hello () 
{ 
    echo "Hello,$1 today is `date`";
    return 1
}
now going to the function hello
Hello,shenzhen today is Thu Jul 30 20:45:23 CST 2015
...
XDG_RUNTIME_DIR=/run/user/0
XDG_SESSION_ID=10
_=hello
lmx=4
back from the function

- - - - - - - - - 本文结束啦感谢您阅读 - - - - - - - - -