linux系统基础知识学习笔记

/  根分区

boot 系统引导程序(内核,GRUB配置文件等)

bin  存放终端二进制命令

etc 系统配置文件(大多数配置文件都是文本文件)

dev  存放设备文件(系统将计算机中的各种设备资源映射为文件形式进行管理)

root  管理员家目录

home  普通用户家目录(多个用户对应自己的家目录/home/lucy

usr  系统资源目录(程序、图片、配置。。。。)系统中75%以上空间被此目录占用

sbin  存放需要管理权限才能运行的二进制命令

var  存放经常变更的文件(例如电子邮件、程序缓存、日志等)

mnt  系统用来挂载设备的目录

media 新系统中用来进行自动挂载的目录

lib 系统程序库文件目录

tmp  临时文件目录

proc  内核进程窗口(内核挂载的特殊类型文件系统)

opt  其他文件

sys  系统内核设备挂载目录

selinux  系统内核级安全功能

Linux系统为多用户,多进程的操作系统。

系统中默认有6个文本终端

分别对应Ctrl+Alt+F1  ~ F6

使用Ctrl+Alt+F7返回图形桌面

终端命令格式

命令  选项   参数1  参数2  。。。

(命令、选项、参数之间必须加空格)

ls 显示文件列表

常用使用方式

ls  显示当前目录下文件

ls  -a   显示当前目录下所有文件(包括隐藏文件,文件名开头字母为“ .”为隐藏文件)

ls  -l   以长格式显示每个文件详细信息

ls  /home  指定要列出的路径(ls  -al  /home)

帮助手册查询命令

man  命令

例如:man  ls

控制键滚动

q快捷键退出

help帮助命令

内部指令帮助命令只提供给shell编程用的内部指令

例如:test  for  until  if

info详细的GNU手册

info  命令

文件类型

- 普通文件

d 目录

l 连接文件

c 字符设备

b 块设备

p 管道文件

s socket套接字文件

系统中有软连接和硬连接。软连接是记录了指定的路径和文件名的文件。硬连接是一个文件的别名。

cd  切换工作路径

cd   /home 切换到指定目录中

cd  ~ 返回当前用户的家目录

cd  返回当前用户家目录

cd  - 返回之前的目录

cd  .. 返回上一层目录

.  当前目录

..  上一层目录

命令行提示符中

第一位显示 当前用户

第二位表示登录的主机

第三位表示当前工作目录

第四位 表示权限 $为普通用户 #为超级用户

显示当前工作目录

pwd

绝对路径和相对路径

绝对路径,是以系统根路径“/”开始指定的目录,写法为"/home/lucy"

相对路径是以当前位置开始指定的目录,写法为"share/gimp"

比较直观的区别在于——是否以"/"开头表示路径

创建目录

mkdir

mkdir  mydvd  在当前位置创建指定名称的目录

mkdir  aa bb cc 同时创建多个目录

mkdir -p  abc/def/ghi 同时创建多层目录

删除目录

rmdir  aa aa必须是空目录)

删除文件和目录

rm 选项  文件或目录名

rm  myfile删除普通文件

rm  -r  mydvd 递归删除目录

rm  -rf  mydvd  递归删除目录,并不需确认

文件或目录复制

cp  选项   文件来源    复制目的地

cp  /proc/cpuinfo  .  cpuinfo文件复制到当前目录下,文件名保持不变

cp  cpuinfo  otherfile  复制文件同时改名

cp cpuinfo /tmp/kkk  复制文件到/tmp目录下(kkk有三种可能,1.不存在,2.是目录,3.是文件)

cp -r mydvd  /tmp mydvd目录及其内容复制到指定位置

tab补全

ctrl+L  清除屏幕

ctrl+c 终止正在执行的终端命令

移动或重命名

mv  原位置和名称   新位置和名称

统计目录大小

du  -sh  统计当前目录占用磁盘空间

du  -sh  /home  统计指定目录占用磁盘空间

查看文本文件内容

cat  不能分页和停止  

more  能分页,但不能回滚(more +5 file  从文件第五行开始显示)

less  可以回滚显示,q退出

head 查看文件头部10

tail  显示文件尾部10

Linux系统权限管理

ls -l可以查看文件信息。其中包括文件权限、所有者、组

除了文件类型位,之后有9位表示文件的权限。分为三组。

第一组是所有者操作权限

第二组是组成员操作权限

第三组是除了所有者、组之外的其他用户权限

rwx     rwx      rwx

user  group  other

r 4 read读取

w 2 write写入

x 1 exec执行

- 0 无

644 rw- r-- r--

rwxr-xr-x  755

字符表示方法和8进制数字表示方法进行转换的方法是

rwx转换为相对应的数字后计算和、每三位得到一个结果。

如果从数字转换到字符,则可以逆向推导,由于和的结果只能由固定数字运算获得,所以不会产生错误。

chmod命令更改文件权限,但首先要有该文件的操作权限(属于所有者,或是root

更改权限方法

chmod  755  install.log rwxr-xr-x

chmod 644 /opt -R  (递归修改制定目录下的所有文件为指定权限)

通过字符指定权限位

u——用户位

g——

o——其他

a——所有

+——增加权限

-——去除权限

=——指定权限

install.log(原来是644  rw-r--r--

改成755 rwxr-xr-x

chmod  a+x  install.log

chmod  g-w install.log

权限位对于目录来讲,

r可以读取目录内容

w可以创建或删除目录里的文件

x表示可以通过cd命令进入目录

t权限位,允许所有用户在此目录中创建和删除自己的文件(T表示未生效)t位是为目录使用的。

chmod 1777 mydir

粘滞位s

如果设置为s位,则执行此命令的用户,是以此命令所有者身份执行。s位是为执行程序使用的。

chmod u+s  myexec

=====================

用户与组管理

系统中用户相当于是职员,组相当于是部门。

一个用户可以属于多个组,每个组可以有多个用户。

ls -l 查看文件的所有者(即用户),组

用户配置文件

/etc/passwd 存放用户信息

/etc/shadow 存放密码相关信息

passwd文件格式

可以使用man手册查询passwd配置文件的格式。

man  5  passwd

passwd文件中,每一行代表一个账号信息。使用:进行分段

第一列——账号(用户名)

第二列——密码,x表示密码在shadow文件里

第三列——UID,用户标识号,唯一

第四列——GID,初始化组标识编号。(用户在创建文件时,会将此GID写入到文件的组权限位置)。

第五列——用户描述信息

第六列——家目录

第七列——用户登录后所使用的shell程序。

shadow文件格式

第一列——登录名(与passwd对应)

第二列——密码

第三列——1970.1.1到最后一次修改密码的天数

第四列——修改密码间隔的天数

第五列——密码必须更改天数

第六列——密码过期前多少天显示警告

第七列——密码过期后多少天无法登录

第八列——指定具体天数禁止登录,从1970.1.1计算

第九列——保留

useradd 增加用户

usermod 修改用户信息

userdel 删除用户

passwd   修改用户密码

创建用户后,必须指定密码,才能使用户登录系统。

useradd  michael

passwd  michael

删除用户时,使用-r选项可以同时删除用户的家目录

否则,只是将用户的账号在passwdshadow中去除。

userdel  michael

userdel -r  michael

用户组group

/etc/group

/etc/gshadow

组配置文件内容

第一列——组名称

第二列——密码,x表示密码在gshadow

第三列——GID,组标识号。唯一

第四列——组内用户

gshadow文件内容

第一列——对应的组名称

第二列——密码,用于组管理员设置密码

第三列——管理员用户列表

第四列——成员用户列表

newgrp切换组临时到其他组中

newgrp   root

gpasswd设置组密码,及指定组管理员和成员

gpasswd -A nicolas -M michael  michael

gpasswd michael  (组管理员设置组密码)

查看当前用户所属组使用groups命令

groupadd

groupmod

groupdel

增加组

groupadd  develop

groupadd -g 1024  manage-g  指定gid

删除组使用groupdel,但此组中必须为空,没有用户才可以删除。

修改组名称,使用groupmod -n

groupmod  -n  devel  develop

将原名为develop 改为devel

useradd

-g   指定初始化组(此用户在创建文件时,写在组权限位置的组名)只能指定一个

-G   可以指定多个组名,使用户属于多个组

-m  在创建用户时,同时创建家目录

-M  不创建家目录

-d   指定家目录位置

-u  指定用户uid

-s  指定登录shell

useradd  tom  -u  1999  -d  /tmp/tom  -g  root  -G  nicolas,michael  -m

usermod -d /home/kate kate

修改kate用户的家目录位置,但系统不会自动将原来目录移动到目的位置。

usermod -g  nicolas  kate

修改kate的初始化组为nicolas

练习:

创建develop

创建manage

创建tom用户,属于develop组,家目录在/tmp/tom目录下,强制创建家目录,其uid1001

创建lucy用户,属于manage组,家目录在/home/lucy目录下,但不创建家目录,辅助组为develop

修改develop组名为devel

指定tommanage组的管理员,lucymanage组成员,并设置密码

PID进程编号

echo $PATH  查看搜索执行程序的路径

在环境变量中记录的路径,可以不使用路径+程序名方式执行,系统会自动在PATH环境变量中遍历每个路径。如果无法找到相应的应用程序,则输出command not found。如果找到,则执行。

执行当前目录下应用程序或脚本,可以使用./方式运行,或绝对路径和相对路径。

./mysh.sh

/root/bin/mysh.sh

ps查看当前正在运行的进程信息列表

ps 显示当前终端下运行的进程

ps ax 显示系统所有正在运行的进程,显示进程的命令

ps aux 显示进程启动的用户

PID 进程编号

TTY 运行在哪个终端中

STAT 进程状态

TIME 运行时间

COMMAND 进程的命令

init进程——永远pid1

进程编号可以循环利用,一般最大进程号为32767

top命令动态更新系统进程列表,并显示启动时间、cpu使用率、内存使用率、交换分区使用率等信息。默认以CPU占用作为排序。默认3秒更新一次。可以使用s快捷键指定更新时间(秒),使用q退出程序。

pstree 使用树状方式显示进程表及进程关系。

kill杀死进程

kill  信号   进程编号PID

kill -9  2349  发送信号给进程,使之退出。

如果信号9无法结束进程,则进程不处理此信号,无法结束。

pkillkillall  使用程序名指定杀死的进程,只要名称相同,可以同时杀死多个进程。

pkill  gnome-terminal

killall  gnome-terminal

xkill 通过鼠标点击窗口,将指定的窗口或应用程序杀死。

将进程在后台运行,如果程序已经运行,则可以使用ctrl+z,将当前进程放到后台。

如果需要在运行时指定直接到后台,则在命令后加&

jobs查询当前终端里后台运行的进程

bg指定进程在后台继续运行,但某些程序只能向终端输出,如果放到后台就只能停止。

fg将后台进程调到前台运行。

通过jobs查看到的jobs编号对进程进行指定。

nice 在运行程序时,指定程序的优先级

renice 程序已经运行,重新指定程序优先级。

nice

-n 指定谦让度,-20最低,19最高。如果用优先级考虑,-20为最高优先级。默认进程nice值为0

nice -n -10  top 

参数指定优先级数,之后是pid

renice +10 2201

======================

文件系统管理

分区fdisk命令

格式化mke2fsmkfs.ext3mkfs.vfatmkswap

挂载mount、卸载umount

配置文件/etc/fstab进行启动自动挂载文件系统

fdisk使用交互模式

fdisk -l   查看所有硬盘分区表

fdisk  /dev/sda   对指定硬盘进行分区操作

帮助菜单

a  分区引导标识开关

删除一个分区

l   列出已知分区类型

6 fat16

7 ntfs

b fat32

82  linux swap

83 linux ext2 or ext3

n  创建一个新分区

o  创建一个空白的dos分区表

p  打印分区表(同 fdisk -l

q  退出不保存

t   修改文件系统类型

u  使用不同的单位显示

v  验证磁盘分区表

w  写入磁盘并退出

x  扩展功能,专家模式

16g分区

第一个分区为主分区类型,6g,文件系统类型ntfs

第二个分区为主分区类型,5g,文件系统类型ext3

逻辑分区第一个,3g,文件系统类型vfat

逻辑分区第二个,2g,文件系统类型vfat

逻辑分区第三个,1g,文件系统类型swap

软件包管理

tar包管理

后缀名为 .tar

文件归档,没有压缩功能,但可以通过选项调用其他压缩程序进行压缩。

x 解压缩

c 创建

v 显示操作的文件

f 指定压缩或解压缩的文件名

z 调用gzip命令压缩成.tar.gz格式

j 调用bzip2命令压缩成.tar.bz2格式

创建tar

tar  cvf  etc.tar   /etc

解压缩tar

tar xf  etc.tar

将归档文件内容解压到当前目录下

创建tar.bz2压缩包

tar jcvf etc.tar.bz2 /etc

创建tar.gz压缩包

tar zcf  etc.tar.gz  /etc

解压缩包

tar  jxf  etc.tar.bz2

tar  zxvf  etc.tar.gz

简单的去掉解压缩选项,tar命令会自动判断文件类型解压

tar  xf  etc.tar.bz2

tar  xf  etc.tar.gz

默认解压将压缩文件内容解压到当前目录,如果需要指定解压路径,可以使用--directory选项或-C选项

tar  xf  etc.tar.bz2  -C  /tmp

tar后缀的压缩包经常用于软代码软件的发布,解压后的内容为一个开源软件的源代码,需要编译后才能在系统中使用。

gzip压缩包

后缀为.gz

使用gzip命令进行压缩,gunzip进行解压缩

gzip命令会对给定的文件进行压缩,产生带.gz后缀的文件命令,并将原始文件删除

gzip  install.log  压缩文件产生install.log.gz

gunzip  install.log.gz  解压缩文件,还原install.log

gzip  -d  install.log.gz  解压缩文件,与 gunzip效果相同

gzip命令在解压文件时,文件后缀必须是gz,否则无法执行解压缩

bzip2 压缩包

后缀为.bz2

使用bzip2进行压缩,使用bunzip2进行解压缩

使用bzip2也可以进行解压缩,-d选项的作用与gzip相同。

RPM软件包管理

红帽包管理器,是红帽公司发布的一种用于安装二进制应用程序的软件包格式。其他linux相同中也有使用debiandeb包管理程序。

RPM安装

rpm  -i   软件包名称

iinstall

显示安装的信息

h  显示安装进度

rpm  -ivh   firefox-3.6.5-i386.rpm

i386 或 i586 i686都是对应pc平台

src表示rpm包内容为源代码

noarch表示不依赖与平台,多数为脚本或资源文件

强制安装,并不检测依赖关系

--force  --nodeps

rpm -ivh --force --nodeps  firefox-1.5.0.el5.centos.i386.rpom

很多软件包在安装后,会在/usr/lib/pkgconfig目录中创建文件,用于记录某些软件包安装信息,便于其他依赖的软件查询相应的版本和位置。

查询软件包

rpm -qquery)用于查询软件包,配合其他选项一起使用

rpm -qi  firefox 显示已经安装软件包的信息

rpm -qa  显示所有已经安装的软件包(rpm -qa | grep  firefox

rpm -ql  firefox  显示软件包内容

rpm -qlp  coreutils-5.97.i386.rpm 显示指定rpm包的内容

rpm  -qf  文件名  显示指定的文件是由哪个软件包安装进来

删除已安装软件包

rpm -e  软件包名

升级软件包

rpm -Uvh  软件包名   指定升级的软件包

========================

使用fdisk分区后,需要使用格式化命令后,才能在分区中存储数据。

不同类型分区使用不同的格式化命令

ext2ext3 使用 mke2fs命令格式化

fat32使用mkfs.vfat

swap  使用mkswap

mkfs.ext2   /dev/sda2

mkfs.ext3  /dev/sdb4

mkfs.vfat  /dev/sdb2

mkswap  /dev/sdb3

对格式化以后的分区可以使用挂载命令,将指定分区设备挂载到某个目录上进行访问。被挂载的目录叫做挂载点。一般情况挂载点目录最好是空目录。

如果挂载点目录中有文件,当挂载后,暂时不可访问原有文件,只能看到被挂载以后的文件内容。

挂载命令mount

直接使用mount命令可以查看当前挂载分区的列表

挂载分区

mount    设备名    挂载点目录

卸载分区

umount  设备名

umount  挂载点目录

注意:被卸载的分区,不能在使用中。工作路径不能在挂载点目录里。

mount命令常用选项

-t  指定要挂载的设备类型

mount  -t  ext3  /dev/sdb1  /mnt

-o  挂载功能选项

mount -o loop /root/centos.iso   /mnt/iso

dd复制命令

dd  if=/dev/zero  of=mydisk  bs=1024k  count=100

if=输入文件

of=输出文件名

bs=block size,一个块大小

count=数量

网络配置

文件共享

远程登录

======================

配置网络

ifconfig用于显示和配置网卡ip地址及子网掩码,对网卡开启和关闭

更改虚拟机网卡连接方式为“bridged adapter”

界面名称为:物理网卡(能够连接互联网络)

ifconfig  显示所有开启的网络设备信息

eth0 代表以太网卡第一个

lo   回环设备(localhost127.0.0.1

wlan0  无线网卡

通过ifconfig命令指定ip地址和子网掩码,但这种方法是临时的。当重新启动网卡时,之前的设置会自动被覆盖成配置文件的设置。

ifconfig  设备名  ip地址   netmask  子网掩码

ifconfig eth0  192.168.5.200 netmask  255.255.255.0

使用ping命令探测主机,使用ctrl+c终止

ping 192.168.5.100

ssh安全远程连接服务

ssh  [email protected]

nmap localhost

查询主机开放端口

如果开启ssh服务的主机,22端口

putty程序,选择ssh类型,指定ip地址后就可以进行远程登录

登录时需要输入用户名和密码,之后就和本地操作一样控制计算机。

=============================

简单设置ip地址的方式可以使用终端的程序setup

setup命令可以设置系统的配置,其中包括网络设置

选择网络设置后,对指定的网卡进行配置。

去掉dhcp的选择,然后指定静态ip地址

设置完成,使用ifdown eth0关闭网卡,

再使用ifup  eth0 开启网卡。系统会读取网卡配置文件,按照设置的ip进行配置。

/etc/sysconfig/network-scripts/ifcfg-eth0

device=设备名

hwaddr=mac地址

onboot=在启动时是否开启网卡

netmask=子网掩码

ipaddr=ip地址

type=网卡类型

gateway=网关设置

===========================

局域网访问互联网

1.设置ip地址,一般和网关在同一个网段

2.设置网关

route  add  default  gw  192.168.5.1

add增加

default默认

gw网关gateway

3.设置dns服务器,linux系统中通过配置文件指定

/etc/resolv.conf

编辑配置文件,使用nameserver指定dns服务器

nameserver 202.96.69.38

============================

在一个网卡设置2ip地址

复制ifcfg-eth0文件为ifcfg-eth0:1

修改ifcfg-eth0:1文件,将其中的device更改为eth0:1,将网段改为其他网段

使用ifup eth0:1启动第二个ip地址

==========================

NFS网路文件系统

服务器开启nfs服务后,客户端只需要通过mount命令即可将远程的共享目录挂载到本地目录上,使用时同自己本机目录和文件一样操作。

 客户端进行访问时,使用挂在命令

mount   -t  nfs   192.168.5.200:/pub  /mnt

mount   指定文件系统   主机地址:共享目录   本地挂载点

nfs服务配置

/etc/exprots文件

每一行可以指定一个共享目录,

第一列为本地共享的目录,

第二列为允许访问的主机或网段

括号中为设置的访问选项。

/pub    192.168.5.0/24(rw, insecure, all_squash)

配置文件修改后需要重新启动服务才能生效

service nfs restart

/etc/rc.d/init.d/nfs  restart

=============

子网掩码

0-255 8位二进制

48位, 32位长

192.168.5.0/24   同于 192.168.5.0   255.255.255.0

192.168.5.1  ~  192.168.5.254 都可以访问

=============

无法正常卸载的挂载点,可以-f选项强制卸载

umount -f  /mnt/usb

======================

1.ip地址保证可以访问。setup

2.修改配置文件/etc/exports

3. 重启服务service nfs restart

4.挂载测试mount -t nfs 192.168.5.200:/pub /mnt

  /pub目录必须存在,如果需要能够写入,需要更改权限位1777

  /mnt挂载点必须存在

====================

telnet远程登录,不像ssh那么安全,但由于协议实现简单,所以大多数设备连接使用telnet完成。

开启linux系统telnet服务,需要修改xinetd服务的配置文件

/etc/xinetd.d目录中两个关于telnet的配置文件

krb5-telnet

telnet

telnet通过xinet服务管理

将配置问文件中disable=yes改为disable=no

之后重新启动xinetd服务,即可开启telnet服务,默认端口为23

service  xinetd  restart

查询端口是否打开

nmap localhost

使用命令访问telnet

telnet 192.168.5.200

使用putty访问telnet

更改连接类型为telnet,填写服务器ip地址,即可连接

登录时同样需要用户名及密码,默认禁止root用户登录,可以先登录普通用户后使用su命令切换身份

tftp简单的文件传输,简化了ftp功能,通常用于已知服务器上文件,将文件下载到本地或开发板用。

配置文件

/etc/xinetd.d/tftp

修改disable=yes 为 disable=no

重启xinetd服务

service xinetd restart

默认tftp配置文件中指定/tftpboot目录为共享目录,将需要客户端下载的文件放在此目录中。

tftp服务不需要验证用户名和密码,只要知道文件名即可传输。

连接方式

tftp 192.168.5.200

下载文件,被下载的文件存储在执行tftp命令的目录里。

get 文件名

quit 退出

查看tftp服务打开的端口

netstat -an | grep udp

69端口为tftp服务开启udp端口

===================================

netstat 网络状态

netstat -an

a——所有

n——以数字的方式显示主机地址,否则显示主机名

established状态——已经建立连接

wait——等待

time out——连接超时

listen——监听(打开的服务)

===================================

文件共享samba

相关配置文件

/etc/samba/smb.conf

/etc/samba/lmhosts

/etc/samba/smbpasswd

主要配置文件smb.conf

如果需要创建公共目录,让所有用户不需要登录,即可以进入共享,并写入内容。需要先修改(默认user,以及server 需要系统的用户名和密码登录)

security = share

增加一个共享目录

[ myshare]   

comment=my share directory

browseable= yes

writeable=yes

guest ok=yes

path=/tftpboot

public=yes

readonly= no

其中

myshare为共享名

comment为共享说明

browseable为共享目录可以被显示

guest ok允许客户写入

path指定共享的目录(如果需要写入,则修改权限o+w

public 指定为共享目录

readonly 是否只读

使linux  smb服务显示在windows网络组中

修改配置文件的

workgroup = WORKGROUP

netbios name = linux-server

因为windows系统默认工作组为workgroup

netbios name指局域网中的主机名

----------------------------------

使用密码方式访问samba服务

需要在安全选项上使用user项(server功能是通过其他验证服务器来验证)

security = user

注释 passdb

增加 smb passwd file = /etc/samba/smbpasswd

在终端中使用smbpasswd命令为可以使用samba的用户增加密码

smbpasswd  -a  nicolas

重启服务器后,需要使用系统用户身份访问共享目录

作业:

设置vsftpd服务,进行文件的上传和下载

vsftpd 安全ftp文件传输服务

需要系统中安装vsftpd软件包

centos系统默认安装vsftpd

配置文件位置/etc/vsftpd/vsftpd.conf

默认支持系统用户登录、支持匿名登录

客户端程序ftp命令用于登录ftp服务器

ftp 192.168.5.200

使用系统用户和密码登录后,可以对用户家目录进行文件操作。

如果使用匿名账户,用户名和密码时ftp

ls 查看文件

cd 更改路径

get  下载文件(mget 支持通配符*

put  上传文件

by   退出

ftp服务端口是21

bin  指定使用二进制传输,用于从服务器下载软件包、视频等非文本内容。如果使用文本模式传输软件包,则可能造成文件损坏。

如果需要查看执行ftp命令的目录里有什么文件(执行系统的命令)使用!

例如:!lswindows中为dir命令)!dir

====================

磁盘限额

quota

系统可以通过磁盘限额,指定用户在系统磁盘中所使用的空间大小。

磁盘限额需要内核支持quota功能

可以通过dmesg命令查看是否有quota信息,有则可以使用磁盘配额功能。

dmesg | grep quota

为分区开启磁盘限额功能,需要在ext3文件系统上实现。

修改/etc/fstab文件,此文件用于系统启动时自动挂载文件系统。

第一列:设备

第二列:挂载点

第三列:文件系统类型

第四列:挂载选项

第五列:崩溃转储,默认0为不使用

第六列:启动检测,默认0为不检查

/dev/sdb1     /mnt/tmp    ext3     defaults   0  0

mount  -a

将所有记录在fstab中,但没有挂载的分区都挂载上。

对分区增加磁盘限额,需要在fstab中对ext3分区增加挂载选项,分别是usrquota,grpquota。对应的是用户限额和组限额。可以指定一个用户在这个分区上能够使用的空间大小。也可以指定一个用户组在这个分区上使用的总大小。

修改第一行为

LABEL=/       /      ext3      defaults,usrquota,grpquota   1  1

使用重新挂载功能,让分区的磁盘配额功能生效

mount  -o  remount  /

检测磁盘限额并在指定分区上创建配置文件

quotacheck  -ugfcbm   /

执行成功后,分区的根目录下产生了

aquota.group

aquota.user

两个文件

使用

quotaon   /

打开指定分区的磁盘限额,到此,磁盘限额打开,可以配置用户或组,来指定限额的空间。

----------------------

指定用户在分区中限额

edquota   用户名

进入类似vi的编辑界面,需要修改的只有softhard下面的数字

blocks指当前用户已经使用的块数量

inodes指当前用户已经使用的i节点数

超过soft限制会被系统警告

到达hard限制将不能再写入数据或创建文件。

关闭指定分区的限额

quotaoff   /

计划任务

Linux系统中有两种计划任务,一种是at单次执行计划任务,一种是循环执行的cron计划任务。

at计划任务

服务器重启

service  atd  restart

服务默认已经自动运行

at  指定计划任务

atq  查询计划任务

atrm   删除指定的计划任务

创建计划任务

at   时间

输入需要执行的指令,计划任务运行在后台,所以执行的命令如果默认输出到终端,则无法看到。

命令输入结束后,使用ctrl+d结束计划输入。最后会显示计划即将运行的具体时间。

atq列出所有即将执行的计划

第一列代表计划的编号

如果需要删除某项计划,使用atrm

atrm  计划编号

atrm   4

/etc/at.allowat.deny用于指定系统中那些用户可以使用at计划任务。管理员一直可用。如果at.allow不存在则去读取at.deny

allow

为允许的用户列表,deny为禁止的用户列表

-------------------------------------

循环计划任务

crontab -e 编辑当前用户的循环计划

或是修改/etc/crontab修改系统计划任务。

编辑crontab计划时,编辑方式同vi编辑器

格式为

第一列——分钟(0-59

第二列——小时(0-23

第三列——日期(1-31

第四列——月份(1-12

第五列——星期(0-7

第六列——执行的命令

每一行为一个计划,中间用空格分开

不需要指定的列,可以使用*代替

时间可以份段或列出(1,5,8,10)(0-4,8-12

使用/表示间隔(0-59/2

0 0 * * 6  shutdown -r now

每周六半夜12点 关机

30 15  1  * *  tar zcf /tmp/etc.tar.gz  /etc

每个月的1号,下午330份 执行系统配置备份

0  9-12,13-17  * * 1-5  wall “one hour”

工作日,每个小时进行提醒

crontabetc中也同样存放用户对计划执行的控制文件

cron.allowcron.deny,内容和使用方式同at.allowat.deny

==================================

bash

脚本内容

第一行开头的#!表示此文件为脚本文件,紧跟的是指定用于执行此脚本的程序绝对路径。

#!/bin/bash

echo用于显示字符串到屏幕,输出信息给执行shell脚本的用户提示。

echo中引号的作用。

双引号表示显示文本字符串,可以使用环境变量。

单引号表示中间的文本内容原样输出,不进行替换

反引号表示中间的文本是终端命令,会执行命令获取结果

执行shell脚本的方式

一种为脚本文件增加x权限,使其可以直接执行

./myshell.sh

另种方式是调用shell命令去执行指定shell脚本

bash  myshell.sh

----------------

系统环境变量的配置

/etc/profile用于设定每个用户的公共系统变量,并读取每个用户家目录中的.bashrc.bash_profile文件,加载自定义变量。

定义变量

一般变量名为大写,通过=赋值

AAA="my name is root"

查看变量内容

echo $AAA

删除变量

unset AAA

在使用变量时,可以使用$AAA方式获得值,或者${AAA}获取。

两种方式区别在于$AAA容易产生混淆,最好使用${}方式取值

export命令使指定的变量可以在其他shell中读取。

shell程序中再运行shell程序,产生shell的嵌套。

声明的变量,不能直接在下层shell中获取

使用export命令可以将变量带到下层shell中。

但下层shell声明的变量以及修改不能带回上层

查看当前shell的变量使用set

查看export的环境变量使用env

练习:

shell脚本

env中的变量内容输出到文本~/myenv.txt

并在终端中进行提示"file export finished."

打印myenv.txt10

----------------------

第一行#!必须顶格

可以有tab进行缩进,一般用于if for 、函数等处

不在第一行使用的#,在每行开头#表示此行注释

------------------

特殊变量

$*   代表shell脚本执行时所提供的所有参数

$#  参数个数

$?  上一个退出的程序或脚本退出的状态 默认为0,错误为大于0

$$  当前shell进程id

$!  后台执行的进程id

$0 脚本名称

$1  $2 $3 代表第一个参数,第二个参数。。。

$_  最后一个指定的命令的选项

$(执行命令)`执行命令`

转义字符将紧跟在后面的特殊字符变成普通符号,将不具备特殊含义

使用$(())进行变量之间的数字晕眩

括号内直接使用变量名,不需要再加$

用于运算的变量的值必须为数字,如果值中有英文或符号,则运算前的转换会出现错误。

支持的运算包括+_*/

echo $((NUM1+NUM2))

==================

shell脚本中比较重要的

系统命令

脚本语法格式

文本处理(awksed

练习:编写脚本

通过脚本给定的一个参数,将指定的目录进行压缩。

/tmp目录下产生文件,文件名如

目录名_当前时间.tar.bz2

1.定义变量,用于存储:路径+文件名

2.执行命令tar,将变量作为文件名,参数1作为tar命令第二个参数。

脚本名为backupdir.sh

运行脚本

./backupdir.sh   /bin

结果产生

/tmp/bin_13:40.tar.bz2

#=====backupdir.sh=========

#!/bin/bash

LOCATION=/tmp

DIR=$1

TIME=`date "+%H:%M"`

FILENAME=${LOCATION}/${DIR}_${TIME}.tar.bz2

#FILENAME="/tmp/${1}_`date "+%H:%M"`.tar.bz2"

tar jcf $FILENAME  $DIR

echo $FILENAME

=======================

练习:

编写脚本,用于设定居于网络上网设置。

设定ip地址

设定网关

设定dns

脚本名称:setnetwork.sh

脚本接受三个参数

第一个参数设置ip地址。ifconfig

第二个参数设置网关。route

第三个参数用于设定dns地址,/etc/resolv.conf(nameserver)

执行方法:

./setnetwork.sh 192.168.100.20  192.168.100.1  202.96.69.38

#--------setnetwork.sh------

#!/bin/bash

ifconfig eth0 $1

route add default gw $2

echo "nameserver $3" > /etc/resolv.conf

#-----------------------------

/opt目录下创建一个目录myroot

在此目录中创建目录,目录名称来自根目录下所有文件名。

mkdir /opt/myroot

cd /opt/myroot

mkdir `ls /`

------------------------

&&  当之前的命令正确执行,才执行后面的命令

|| 当之前的命令执行失败,才执行后面的命令

例如

make  && make install

测试命令test

test可以测试文件属性、类型

可以测试表达式逻辑与或非

test -f backupdir.sh -a -f setnetwork.s  && echo "aaa"

test还可以对数值进行比较

流程控制

if分支语句

语法格式

if 测试语句

then

语句段

fi

---------

if 测试语句

then

语句段1

else

语句段2

fi

-----------

if 测试语句1

then

语句段1

elif  测试语句2

then

语句段2

else

语句段3

fi

================

改造之前backupdir.sh脚本

判断参数个数,必须大于1。否则直接退出。

判断指定目录是否存在,如果不存在,则进行提示后退出。

最后生成文件后,判断生成的文件是否可以读取。如果可以,则显示文件创建成功否则显示文件创建失败

  1 #!/bin/bash

  2 

  3 if [ $# -eq 0 ]

  4 then

  5     echo "需要一个参数"

  6     exit

  7 fi

  8 

  9 if [ ! -e $1 ]

 10 then

 11     echo "指定目录不存在"

 12     exit

 13 fi

 14 

 15 FILENAME=/tmp/${1}_`date "+%H:%M"`.tar.bz2

 16 tar jcf $FILENAME $1

 17 

 18 echo $FILENAME

 19 

 20 if [ -e $FILENAME ]

 21 then

 22     echo "文件创建成功"

 23 else

 24 

 25     echo "文件创建失败"

 26 fi

=======================

编写计算器脚本

脚本名称:ca.sh

执行方式

ca  100  +  50 

ca   99   x  6

ca   45  /   3

ca   7  -   2

通过参数给脚本提供操作数和操作符

通过判断操作符来进行相应计算,最后输出计算结果

ca  100  +  50

result=150

#!/bin/bash

if [ $# -lt 3 ]

then

echo "need 3 argment"

exit 1

fi

if [ "$2" = "x" ]

then

echo "ca  $1 $2 $3"

echo "result=$(($1*$3))"

elif [ "$2" = "/" ] || [ "$2" = "+" ] || [ "$2" = "-" ]

then

echo "ca $1 $2 $3"

echo "result=$(($1$2$3))"

else

echo "operator wrong!"

exit 2

fi

编写脚本实现猜数

由于暂时不使用循环,所以只提供两次猜测机会

使用 expr  $RANDOM %  10

获得一个09之间的数保存在某变量中。

通过两次if判断输入的数和变量值进行对比,提示用户所输入的数字是大于猜数还是小于猜数。

如果两次猜测都失败,则提示未猜到数字,并显示数字

如果猜到数字后,提示成功信息,并显示猜测数字后退出。

编写脚本

在指定目录下查找文件

分支语句case

语法:

case  变量名 in

1)

语句段1

;;

2)

语句段2

;;

*

语句段3

;;

esac

---------------

read ABC

case  $ABC in

start)

service httpd start

;;

stop)

service httpd stop

;;

*)

echo "input start or stop"

;;

esac

--------------

编写脚本,使用case语句实现菜单

1——增加时间

2——显示最后10条记录

3——显示所有时间

4——初始化时间

5——退出

时间是使用date命令显示,增加就是将当前时间追加到一个文本中。显示时间是查看文本内容。初始化时间是删除文本文件。

输入相应的数字则使用指定的菜单项

作业:

完善配置网络脚本

setnet脚本,可以完成指定网络设置功能

具体使用方法

setnet  -n  home

指定使用家庭网络配置

setnet  -n  work

指定使用公司网络配置

setnet  -n home  -ip  192.168.9.100

设定家庭网络配置中ip地址

setnet  -n  home  -gw 192.168.9.1

设定家庭网络配置中route网关

setnet  -n  home  -dns  202.96.69.38

设定家庭网络配置中dns

以上每个值可以保存在独立的配置文件中。例如

home_ip.conf内容

192.168.9.100

set  -n  work   -show

显示工作网络的所有配置项

==============

编写脚本实现用户登录模拟功能

需要输入用户名和密码进行登录。用户名和密码存储在指定的文件中。登录以后,允许用户执行的命令是lscdmkdirexit。其他不允许执行。

for语句控制脚本语句段循环

基本语法格式

for  变量名  in  列表

do

语句段

done

列表是控制for循环的内容和次数。在每次循环中,变量的内容都会被逐个替换成列表中的项。在语句段中可以使用变量名来获取列表内容中一个元素。for语句段循环次数取决于列表元素数量。

编写脚本来

使用for循环实现99乘法表打印

seq命令用于生成序列,可以生成指定数字的列表

例如生成1~100的列表

seq 100

生成10 到 20的列表

seq 10 20 

乘法表中需要对两个变量进行运算,输出乘法结果

两个变量的取值范围都是1~9的数字

由于不只有1x1  2x2 3x3,还有1x2 1x3 ...

所以每个变量都需要单独进行存储,与不同的数字进行运算

使用for循环嵌套方式,例如外层for中的数值作为乘法第一个操作数,里层for循环数值作为第二个操作数。

===========================

统计当前目录下文件数量

自动备份当前目录下所有目录,到/tmp目录中。产生每个目录对应的tar.gz备份文件

--------

ping命令探测主机,使用-c选项指定ping主机的次数

使用-W 指定等待时间

ping -c 1 -W 1 192.168.5.100

编写脚本,使用for循环,通过ping命令探测网络中主机ip

执行脚本

phost.sh   192.168.5

执行脚本后,被探测存在的主机,显示ip地址。最后统计主机数量-

============================

while循环

基本语法

while 命令

do

语句段

done

while循环依赖与其后的命令执行结果。如果命令执行结果为真,则继续执行,否则跳出循环。

cat /etc/hosts | while read LINE

do

echo $LINE

done

用户从文件中读取行,while循环的次数由行数决定。

while内可以通过变量名获取每一行的内容。

read 命令可以有多个参数

如果只有一个参数,则read将所有输入的内容作为这个变量的值

如果有多个参数,read将默认从左边的空格开始分割给变量赋值

============================

作业

编写shell脚本,用于班级点名

有两个功能,一个是显示姓名,之后需要输入y表示已到,或n代表未到,或o其他。

将输入的内容保存在以时间命名的文件中。

第二个功能是查看原有记录,显示的是最后一次点名的状态

Shell编程基础

本文作者:Leal

授权许可:创作共用协议

编辑人员:

校对人员:

适用版本:

文章状态:主要内容转载自文献一,句子组织/文章结构做了较大调整

参考文献:

    *

      文献一 A5D

    *

      文献二 LinuxCommand.org 

快速导航

   1. 还需要shell编程吗?

   2. 脚本的创建

         1. 合理使用注释

         2. 变量赋值和引用

   3. Shell里的命令

         1. Unix 命令

         2. 概念管道重定向和 backtick

   4. Shell里的流程控制

         1. if 语句

         2. && || 操作符

         3. case 语句

         4. select 语句

         5. while/for 循环

   5. Shell里的一些特殊符号

         1. 引号

         2. Here documents

   6. Shell里的函数

   7. 命令行参数

   8. Shell脚本示例

         1. 一般编程步骤

         2. 二进制到十进制的转换

         3. 文件循环拷贝

   9. 脚本调试

1. 还需要shell编程吗?

眼下似乎是GUI的天下了,当然图形用户界面(GUI)的确能完成为数众多的任务,并给用户带来不错的感观享受和易用性,但它并非适合所有任务。Linux 系统里已有各种各样的图形界面工具,但shell仍是一个强大而灵活的工具。Shell不仅集合了大量命令,是这些命令精诚合作、聚沙成塔的舞台,同时也是一门很棒的编程语言。借助shell,我们可以把各种小巧精干的命令粘合在一起,让这些do one thingdo it best的命令/工具完成不可思议的任务,并实现任务的自动化。Shell特别擅长系统管理任务,尤其适合那些注重易用性、维护性而非速度的任务。

接下来,就让我们来感受shell的魅力吧。

2. 脚本的创建

Linux 里有许多种的shell,如bash, ksh, tcshzsh等,通常我们使用bashbourne again shell)进行shell编程,因为bash不仅免费(自由)且易于使用。本文提供的脚本都使用bash(当然大多数情况下,这些脚本同样可以在 bash的前辈bourne shell中运行)。

和其它语言一样,我们可以使用任意一种文字编辑器,比如neditkeditemacsvi等来编写shell脚本,它必须以如下行开始(必须放在文件的第一行):

   #!/bin/sh

   ...

符号#!用来告诉系统执行该脚本的程序,本例使用/bin/sh。编辑结束并保存后,如果要执行该脚本,必须先使其可执行:

   chmod +x filename

此后在该脚本所在目录下,输入 ./filename 即可执行该脚本。

2.1. 合理使用注释

shell 脚本中以开始的行表示注释,直到该行的结束。我们强烈建议你在脚本中进行适当/合理的注释,这样一来,即便你在相当长时间内没有使用该脚本,也能在短时间内就明白它的作用和工作原理。此外,还有一个很重要的原因是,在注释的帮助下,别人可以快速有效的分享你的脚本,并提出自己的意见和改进。

2.2. 变量赋值和引用

Shell编程中,使用变量无需事先声明,同时变量名的命名须遵循如下规则:

   1.

      首个字符必须为字母(a-zA-Z

   2.

      中间不能有空格,可以使用下滑线(_

   3.

      不能使用标点符号

   4.

      不能使用bash里的关键字(可用help命令查看保留关键字)

需要给变量赋值时,可以这么写:

   变量名=

要取用一个变量的值,只需在变量名前面加一个$

   #!/bin/sh

   #对变量赋值:

   a="hello world"

   # 打印变量a的值:

   echo "A is:" $a

挑个自己喜欢的编辑器,输入上述内容,并保存为文件first,然后执行 chmod +x first 使其可执行,最后输入 ./first 执行该脚本。其输出结果如下:

   A is: hello world

有时候变量名可能会和其它文字混淆,比如:

   num=2

   echo "this is the $numnd"

上述脚本并不会输出"this is the 2nd"而是"this is the ";这是由于shell会去搜索变量numnd的值,而实际上这个变量此时并没有值。这时,我们可以用花括号来告诉shell要打印的是num变量:

   num=2

   echo "this is the ${num}nd"

其输出结果为:this is the 2nd

Shell脚本中有许多变量是系统自动设定的,我们将在用到这些变量时再作说明。除了只在脚本内有效的普通shell变量外,还有环境变量,即那些由export关键字处理过的变量。本文不讨论环境变量,因为它们一般只在登录脚本中用到。

3. Shell里的命令

3.1. Unix 命令

shell脚本中可以使用任意unix命令,不过实际上最为常用的一般都是那些文件和文字操作相关的命令。下面介绍一些常用命令的语法和功能:

  echo "some text": 在屏幕上输出信息

  ls: 文件列表

  wc –l file wc -w file wc -c file: 分别计算文件的行数(line)、单词数(word)和字符数(character

  cp sourcefile destfile: 文件拷贝

  mv oldname newname: 重命名文件或移动文件

  rm file: 删除文件

  grep 'pattern' file: 在文件内搜索字符串或和正则表达式匹配的字符串

  cut -b column file: 将指定范围内的文件内容输出到标准输出设备(屏幕)上。比如:输出每行第59个字符 cut -b5-9 file.txt,注意不要和cat命令混淆,这是两个完全不同的命令

  cat file.txt: 输出文件内容到标准输出设备(屏幕)上

  file somefile: 取得文件somefile的文件类型

  read var: 提示用户输入,并将输入内容赋值给变量var

  sort file.txt: file.txt文件所有行进行排序

  uniq: 只输出文件中内容不一致的行,如: sort file.txt | uniq

  expr: 进行数学运算,如要进行2+3的运算,命令为: expr 2 "+" 3

  find: 搜索文件,如根据文件名搜索:find . -name filename -print

  tee: 将数据输出到标准输出设备(屏幕和文件,比如:somecommand | tee outfile

  basename file: 返回不包含路径的文件名,如: basename /bin/tux 会返回 tux

  dirname file: 返回文件所在路径,如:dirname /bin/tux 会返回 /bin

  head file: 打印文本文件开头几行

  tail file : 打印文本文件末尾几行

  sed: 是一个基本的查找替换程序。可以从标准输入(如命令管道)读入文本,并将结果输出到标准输出(屏幕);该命令采用正则表达式进行搜索。不要和 shell中的通配符相混淆。比如将 ubuntu 替换为 Ubuntu cat text.file | sed 's/ubuntu/Ubuntu/' > newtext.file

  awk: 用来提取文本文件中的字段。缺省的字段分割符是空格,可以使用 -F 指定其它分割符。cat file.txt | awk -F, '{print $1 "," $3 }',这里我们使用 作为字段分割符,同时打印第一和第三个字段。如果该文件内容为 Adam Bor, 34, IndiaKerry Miller, 22, USA,则上述命令的输出为:Adam Bor, IndiaKerry Miller, USA

3.2. 概念管道重定向和 backtick

尽管这些都不是系统命令,不过它们扮演着相当重要的角色。

    *

      管道 (|) 将一个命令的输出作为另外一个命令的输入 

   grep "hello" file.txt | wc -l

上述命令会在file.txt中搜索包含有”hello”的行并计算行数,这里grep命令的输出成了wc命令的输入。

    *

      重定向:将命令的结果输出到文件,而不是标准输出(屏幕) 

   >  写入文件并覆盖旧文件

   >> 加到文件的尾部,保留旧文件内容

    *

      反短斜线

反短斜线可以将一个命令的输出作为其它命令的命令行参数。

   find . -mtime -1 -type f -print

上述命令可以查找过去24小时(-mtime –2则表示过去48小时)内修改过的文件。如果你想将上述命令查找到的所有文件打包,则可以使用如下脚本:

   #!/bin/sh

   # The ticks are backticks (`) not normal quotes ('):

   tar -zcvf lastmod.tar.gz `find . -mtime -1 -type f -print`

4. Shell里的流程控制

4.1. if 语句

"if"表达式如果条件为真,则执行then后的部分:

Toggle line numbers

   1    if ....; then

   2      ....

   3    elif ....; then

   4      ....

   5    else

   6      ....

   7    fi

大多数情况下,可以使用测试命令来对条件进行测试,比如可以比较字符串、判断文件是否存在及是否可读等等……通常用" [ ] "来表示条件测试,注意这里的空格很重要,要确保方括号前后的空格。

   [ -f "somefile" ] :判断是否是一个文件

   [ -x "/bin/ls" ] :判断/bin/ls是否存在并有可执行权限

   [ -n "$var" ] :判断$var变量是否有值

   [ "$a" = "$b" ] :判断$a$b是否相等

执行man test可以查看所有测试表达式可以比较和判断的类型。下面是一个简单的if语句:

Toggle line numbers

   1    #!/bin/sh

   2 

   3    if [ "$SHELL" = "/bin/bash" ]; then

   4       echo "your login shell is the bash (bourne again shell)"

   5    else

   6       echo "your login shell is not bash but $SHELL"

   7    fi

变量$SHELL包含有登录shell的名称,我们拿它和/bin/bash进行比较以判断当前使用的shell是否为bash

4.2. && || 操作符

熟悉C语言的朋友可能会喜欢下面的表达式:

   [ -f "/etc/shadow" ] && echo "This computer uses shadow passwords"

这里的 && 就是一个快捷操作符,如果左边的表达式为真则执行右边的语句,你也可以把它看作逻辑运算里的与操作。上述脚本表示如果/etc/shadow文件存在,则打印”This computer uses shadow passwords”。同样shell编程中还可以用或操作(||),例如:

Toggle line numbers

   1    #!/bin/sh

   2 

   3    mailfolder=/var/spool/mail/james

   4    [ -r "$mailfolder" ] || { echo "Can not read $mailfolder" ; exit 1; }

   5       echo "$mailfolder has mail from:"

   6       grep "^From " $mailfolder

该脚本首先判断mailfolder是否可读,如果可读则打印该文件中的"From" 一行。如果不可读则或操作生效,打印错误信息后脚本退出。需要注意的是,这里我们必须使用如下两个命令:

   -打印错误信息

   -退出程序

我们使用花括号以匿名函数的形式将两个命令放到一起作为一个命令使用;普通函数稍后再作说明。即使不用与和或操作符,我们也可以用if表达式完成任何事情,但是使用与或操作符会更便利很多。

4.3. case 语句

case表达式可以用来匹配一个给定的字符串,而不是数字(可别和C语言里的switch...case混淆)。

   case ... in

      ...) do something here ;;

   esac

让我们看一个例子,file命令可以辨别出一个给定文件的文件类型,如:file lf.gz,其输出结果为:

   lf.gz: gzip compressed data, deflated, original filename,

   last modified: Mon Aug 27 23:09:18 2001, os: Unix

我们利用这点写了一个名为smartzip的脚本,该脚本可以自动解压bzip2, gzipzip 类型的压缩文件:

Toggle line numbers

   1    #!/bin/sh

   2 

   3    ftype=`file "$1"`

   4    case "$ftype" in

   5    "$1: Zip archive"*)

   6       unzip "$1" ;;

   7    "$1: gzip compressed"*)

   8       gunzip "$1" ;;

   9    "$1: bzip2 compressed"*)

  10       bunzip2 "$1" ;;

  11    *) error "File $1 can not be uncompressed with smartzip";;

  12    esac

ERROR: EOF in multi-line statement

你可能注意到上面使用了一个特殊变量$1,该变量包含有传递给该脚本的第一个参数值。也就是说,当我们运行:

   smartzip articles.zip

$1 就是字符串 articles.zip

4.4. select 语句

select表达式是bash的一种扩展应用,擅长于交互式场合。用户可以从一组不同的值中进行选择:

Toggle line numbers

   1 select var in ... ; do

   2 break

   3 done

   4 .... now $var can be used ....

下面是一个简单的示例:

Toggle line numbers

   1 #!/bin/sh

   2 

   3 echo "What is your favourite OS?"

   4 select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do

   5 break

   6 done

   7 echo "You have selected $var"

该脚本的运行结果如下:

What is your favourite OS?

1) Linux

2) Gnu Hurd

3) Free BSD

4) Other

#? 1

You have selected Linux

4.5. while/for 循环

shell中,可以使用如下循环:

Toggle line numbers

   1 while ...; do

   2    ....

   3 done

只要测试表达式条件为真,则while循环将一直运行。关键字"break"用来跳出循环,而关键字”continue”则可以跳过一个循环的余下部分,直接跳到下一次循环中。

for循环会查看一个字符串列表(字符串用空格分隔),并将其赋给一个变量:

Toggle line numbers

   1 for var in ....; do

   2    ....

   3 done

下面的示例会把A B C分别打印到屏幕上:

Toggle line numbers

   1 #!/bin/sh

   2 

   3 for var in A B C ; do

   4    echo "var is $var"

   5 done

下面是一个实用的脚本showrpm,其功能是打印一些RPM包的统计信息:

Toggle line numbers

   1 #!/bin/sh

   2 

   3 # list a content summary of a number of RPM packages

   4 # USAGE: showrpm rpmfile1 rpmfile2 ...

   5 # EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm

   6 for rpmpackage in $*; do

   7    if [ -r "$rpmpackage" ];then

   8       echo "=============== $rpmpackage =============="

   9       rpm -qi -p $rpmpackage

  10    else

  11       echo "ERROR: cannot read file $rpmpackage"

  12    fi

  13 done

这里出现了第二个特殊变量$*,该变量包含有输入的所有命令行参数值。如果你运行showrpm openssh.rpm w3m.rpm webgrep.rpm,那么 $* 就包含有 个字符串,即openssh.rpm, w3m.rpm和 webgrep.rpm

5. Shell里的一些特殊符号

5.1. 引号

在向程序传递任何参数之前,程序会扩展通配符和变量。这里所谓的扩展是指程序会把通配符(比如*)替换成适当的文件名,把变量替换成变量值。我们可以使用引号来防止这种扩展,先来看一个例子,假设在当前目录下有两个jpg文件:mail.jpgtux.jpg

Toggle line numbers

   1 #!/bin/sh

   2 

   3 echo *.jpg

运行结果为:

   mail.jpg tux.jpg

引号(单引号和双引号)可以防止通配符*的扩展:

Toggle line numbers

   1 #!/bin/sh

   2 

   3 echo "*.jpg"

   4 echo '*.jpg'

其运行结果为:

   *.jpg

   *.jpg

其中单引号更严格一些,它可以防止任何变量扩展;而双引号可以防止通配符扩展但允许变量扩展:

Toggle line numbers

   1 #!/bin/sh

   2 

   3 echo $SHELL

   4 echo "$SHELL"

   5 echo '$SHELL'

运行结果为:

Toggle line numbers

   1 /bin/bash

   2 /bin/bash

   3 $SHELL

此外还有一种防止这种扩展的方法,即使用转义字符——反斜杆:

   echo *.jpg

   echo $SHELL

输出结果为:

   *.jpg

   $SHELL

5.2. Here documents

当要将几行文字传递给一个命令时,here documents一种不错的方法。对每个脚本写一段帮助性的文字是很有用的,此时如果使用here documents就不必用echo函数一行行输出。Here document以 << 开头,后面接上一个字符串,这个字符串还必须出现在here document的末尾。下面是一个例子,在该例子中,我们对多个文件进行重命名,并且使用here documents打印帮助:

Toggle line numbers

   1 #!/bin/sh

   2 

   3 # we have less than 3 arguments. Print the help text:

   4 if [ $# -lt 3 ] ; then

   5 cat < ren -- renames a number of files using sed regular expressions

   6 

   7 USAGE: ren 'regexp' 'replacement' files...

   8 

   9 EXAMPLE: rename all *.HTM files in *.html:

  10    ren 'HTM$' 'html' *.HTM

  11 

  12 HELP

  13    exit 0

  14 fi

  15 

  16 OLD="$1"

  17 NEW="$2"

  18 # The shift command removes one argument from the list of

  19 # command line arguments.

  20 shift

  21 shift

  22 # $* contains now all the files:

  23 for file in $*; do

  24    if [ -f "$file" ] ; then

  25       newfile=`echo "$file" | sed "s/${OLD}/${NEW}/g"`

  26       if [ -f "$newfile" ]; then

  27        echo "ERROR: $newfile exists already"

  28       else

  29          echo "renaming $file to $newfile ..."

  30          mv "$file" "$newfile"

  31       fi

  32    fi

  33 done

ERROR: EOF in multi-line statement

这个示例有点复杂,我们需要多花点时间来说明一番。第一个if表达式判断输入命令行参数是否小于3个 (特殊变量$# 表示包含参数的个数。如果输入参数小于3个,则将帮助文字传递给cat命令,然后由cat命令将其打印在屏幕上。打印帮助文字后程序退出。如果输入参数等于或大于3个,我们就将第一个参数赋值给变量OLD,第二个参数赋值给变量NEW。下一步,我们使用shift命令将第一个和第二个参数从参数列表中删除,这样原来的第三个参数就成为参数列表$*的第一个参数。然后我们开始循环,命令行参数列表被一个接一个地被赋值给变量$file。接着我们判断该文件是否存在,如果存在则通过sed命令搜索和替换来产生新的文件名。然后将反短斜线内命令结果赋值给newfile。这样我们就达到了目的:得到了旧文件名和新文件名。然后使用 mv命令进行重命名。

6. Shell里的函数

如果你写过比较复杂的脚本,就会发现可能在几个地方使用了相同的代码,这时如果用上函数,会方便很多。函数的大致样子如下:

Toggle line numbers

   1 functionname()

   2 {

   3 # inside the body $1 is the first argument given to the function

   4 # $2 the second ...

   5 body

   6 }

你需要在每个脚本的开始对函数进行声明。

下面是一个名为xtitlebar的脚本,它可以改变终端窗口的名称。这里使用了一个名为help的函数,该函数在脚本中使用了两次:

Toggle line numbers

   1 #!/bin/sh

   2 # vim: set sw=4 ts=4 et:

   3 

   4 help()

   5 {

   6    cat < xtitlebar -- change the name of an xterm, gnome-terminal or kde konsole

   7 

   8 USAGE: xtitlebar [-h] "string_for_titelbar"

   9 

  10 OPTIONS: -h help text

  11 

  12 EXAMPLE: xtitlebar "cvs"

  13 

  14 HELP

  15    exit 0

  16 }

  17 

  18 # in case of error or if -h is given we call the function help:

  19 [ -z "$1" ] && help

  20 [ "$1" = "-h" ] && help

  21 

  22 # send the escape sequence to change the xterm titelbar:

  23 echo -e "33]0;$107"

  24 #

在脚本中提供帮助是一种很好的编程习惯,可以方便其他用户(和自己)使用和理解脚本。

7. 命令行参数

我们已经见过$* 和 $1, $2 ... $9 等特殊变量,这些特殊变量包含了用户从命令行输入的参数。迄今为止,我们仅仅了解了一些简单的命令行语法(比如一些强制性的参数和查看帮助的-h选项)。但是在编写更复杂的程序时,您可能会发现您需要更多的自定义的选项。通常的惯例是在所有可选的参数之前加一个减号,后面再加上参数值 (比如文件名)

有好多方法可以实现对输入参数的分析,但是下面的使用case表达式的例子无疑是一个不错的方法。

Toggle line numbers

   1 #!/bin/sh

   2 

   3 help()

   4 {

   5    cat < This is a generic command line parser demo.

   6    USAGE EXAMPLE: cmdparser -l hello -f -- -somefile1 somefile2

   7    HELP

   8    exit 0

   9 }

  10 

  11 while [ -n "$1" ]; do

  12 case $1 in

  13    -h) help;shift 1;; # function help is called

  14    -f) opt_f=1;shift 1;; # variable opt_f is set

  15    -l) opt_l=$2;shift 2;; # -l takes an argument -> shift by 2

  16    --) shift;break;; # end of options

  17    -*) echo "error: no such option $1. -h for help";exit 1;;

  18    *) break;;

  19 esac

  20 done

  21 

  22 echo "opt_f is $opt_f"

  23 echo "opt_l is $opt_l"

  24 echo "first arg is $1"

  25 echo "2nd arg is $2"

ERROR: EOF in multi-line statement

你可以这样运行该脚本:

   cmdparser -l hello -f -- -somefile1 somefile2

返回结果如下:

   opt_f is 1

   opt_l is hello

   first arg is -somefile1

   2nd arg is somefile2

这个脚本是如何工作的呢?脚本首先在所有输入命令行参数中进行循环,将输入参数与case表达式进行比较,如果匹配则设置一个变量并且移除该参数。根据unix系统的惯例,首先输入的应该是包含减号的参数。

8. Shell脚本示例

8.1. 一般编程步骤

现在我们来讨论编写一个脚本的一般步骤。任何优秀的脚本都应该具有帮助和输入参数。写一个框架脚本(framework.sh),该脚本包含了大多数脚本需要的框架结构,是一个非常不错的主意。这样一来,当我们开始编写新脚本时,可以先执行如下命令:

   cp framework.sh myscript

然后再插入自己的函数。

让我们来看看如下两个示例。

8.2. 二进制到十进制的转换

脚本 b2d 将二进制数 (比如 1101) 转换为相应的十进制数。这也是一个用expr命令进行数学运算的例子:

Toggle line numbers

   1 #!/bin/sh

   2 # vim: set sw=4 ts=4 et:

   3 help()

   4 {

   5    cat < b2h -- convert binary to decimal

   6 

   7 USAGE: b2h [-h] binarynum

   8 

   9 OPTIONS: -h help text

  10 

  11 EXAMPLE: b2h 111010

  12 will return 58

  13 HELP

  14    exit 0

  15 }

  16 

  17 error()

  18 {

  19    # print an error and exit

  20    echo "$1"

  21    exit 1

  22 }

  23 

  24 lastchar()

  25 {

  26    # return the last character of a string in $rval

  27    if [ -z "$1" ]; then

  28       # empty string

  29       rval=""

  30       return

  31    fi

  32    # wc puts some space behind the output this is why we need sed:

  33    numofchar=`echo -n "$1" | wc -c | sed 's/ //g' `

  34    # now cut out the last char

  35    rval=`echo -n "$1" | cut -b $numofchar`

  36 }

  37 

  38 chop()

  39 {

  40    # remove the last character in string and return it in $rval

  41    if [ -z "$1" ]; then

  42       # empty string

  43       rval=""

  44       return

  45    fi

  46    # wc puts some space behind the output this is why we need sed:

  47    numofchar=`echo -n "$1" | wc -c | sed 's/ //g' `

  48    if [ "$numofchar" = "1" ]; then

  49       # only one char in string

  50       rval=""

  51       return

  52    fi

  53    numofcharminus1=`expr $numofchar "-" 1`

  54    # now cut all but the last char:

  55    rval=`echo -n "$1" | cut -b 0-${numofcharminus1}`

  56 }

  57 

  58 while [ -n "$1" ]; do

  59 case $1 in

  60    -h) help;shift 1;; # function help is called

  61    --) shift;break;; # end of options

  62    -*) error "error: no such option $1. -h for help";;

  63    *) break;;

  64 esac

  65 done

  66 

  67 # The main program

  68 sum=0

  69 weight=1

  70 # one arg must be given:

  71 [ -z "$1" ] && help

  72 binnum="$1"

  73 binnumorig="$1"

  74 

  75 while [ -n "$binnum" ]; do

  76    lastchar "$binnum"

  77    if [ "$rval" = "1" ]; then

  78       sum=`expr "$weight" "+" "$sum"`

  79    fi

  80    # remove the last position in $binnum

  81    chop "$binnum"

  82    binnum="$rval"

  83    weight=`expr "$weight" "*" 2`

  84 done

  85 

  86 echo "binary $binnumorig is decimal $sum"

  87 #

ERROR: EOF in multi-line statement

该脚本使用的算法是利用十进制和二进制数权值 (1,2,4,8,16,..),比如二进制"10"可以这样转换成十进制:

   0 * 1 + 1 * 2 = 2

为了得到单个的二进制数我们是用了lastchar 函数。该函数使用wc –c计算字符个数,然后使用cut命令取出末尾一个字符。Chop函数的功能则是移除最后一个字符。

8.3. 文件循环拷贝

你可能有这样的需求并一直都这么做:将所有发出邮件保存到一个文件中。但是过了几个月之后,这个文件可能会变得很大以至于该文件的访问速度变慢;下面的脚本 rotatefile 可以解决这个问题。这个脚本可以重命名邮件保存文件(假设为outmail)为outmail.1,而原来的outmail.1就变成了 outmail.2 等等...

Toggle line numbers

   1 #!/bin/sh

   2 # vim: set sw=4 ts=4 et:

   3 

   4 ver="0.1"

   5 help()

   6 {

   7    cat < rotatefile -- rotate the file name

   8    USAGE: rotatefile [-h] filename

   9    OPTIONS: -h help text

  10    EXAMPLE: rotatefile out

  11 

  12    This will e.g rename out.2 to out.3, out.1 to out.2, out to out.1[BR]

  13    and create an empty out-file

  14 

  15    The max number is 10

  16 

  17    version $ver

  18    HELP

  19 

  20    exit 0

  21 }

  22 

  23 error()

  24 {

  25    echo "$1"

  26    exit 1

  27 }

  28 

  29 while [ -n "$1" ]; do

  30    case $1 in

  31       -h) help;shift 1;;

  32       --) break;;

  33       -*) echo "error: no such option $1. -h for help";exit 1;;

  34       *) break;;

  35    esac

  36 done

  37 

  38 # input check:

  39 if [ -z "$1" ] ; then

  40    error "ERROR: you must specify a file, use -h for help"

  41 fi

  42 

  43 filen="$1"

  44 # rename any .1 , .2 etc file:

  45 for n in 9 8 7 6 5 4 3 2 1; do

  46    if [ -f "$filen.$n" ]; then

  47       p=`expr $n + 1`

  48       echo "mv $filen.$n $filen.$p"

  49       mv $filen.$n $filen.$p

  50    fi

  51 done

  52 

  53 # rename the original file:

  54 if [ -f "$filen" ]; then

  55    echo "mv $filen $filen.1"

  56    mv $filen $filen.1

  57 fi

  58 

  59 echo touch $filen

  60 touch $filen

ERROR: EOF in multi-line statement

这个脚本是如何工作的呢?在检测到用户提供了一个文件名之后,首先进行一个91的循环;文件名.9重命名为文件名.10,文件名.8重命名为文件名. 9……等等。循环结束之后,把原始文件命名为文件名.1,同时创建一个和原始文件同名的空文件(touch $filen)。

9. 脚本调试

最简单的调试方法当然是使用echo命令。你可以在任何怀疑出错的地方用echo打印变量值,这也是大部分shell程序员花费80%的时间用于调试的原因。Shell脚本的好处在于无需重新编译,而插入一个echo命令也不需要多少时间。

shell也有一个真正的调试模式,如果脚本"strangescript"出错,可以使用如下命令进行调试:

   sh -x strangescript

上述命令会执行该脚本,同时显示所有变量的值。

shell还有一个不执行脚本只检查语法的模式,命令如下:

   sh -n your_script

这个命令会返回所有语法错误。

我们希望你现在已经可以开始编写自己的shell脚本了,尽情享受这份乐趣吧! :) 

#cut -d '分隔字符' -f fields

参数

-d: 后面接分隔字符,-f一起使用

-f: 依据-d的分隔字符将一段信息分割成为数据,-f取取第几段的意思

-c: 以字符(characters)的单位取出固定字符的区间

cat /etc/passwd | cut -d ':' -f 1

cat /etc/passwd | cut -d ':' -f 6

cut -d: -f 1 /etc/passwd > /tmp/users

当然也可以通过cut取得文件中每行中特定的几个字符

cut -c3-5 /etc/passwd

就是输出/etc/passwd文件中每行的第三到第五个字符

语法:

   tr [options] [source-char] [replace-char] < filename

用途:

    转换字符,例如:将大写字符转换成小写字符。选项可以让你指定所要删除的字符,以及将一串重复出现的字符浓缩成一个。

常用选项:

c:   取source-char-list的反义,所有不在source-char-list中的字符。常与-d , -s配合使用。

d:   删除source-char-list中所定义的字符。

s:   浓缩重复的字符。如果标准输入中连续重复出现source-char-list里所列的字符,则将其浓缩成一个。或者将其浓缩成replace-char-list中的字符。

[a-z] a-z内的字符组成的字符串。 

[A-Z] A-Z内的字符组成的字符串。 

[0-9] 数字串。 

 tr -s "\n" < p.txt

tr "[a-z]" "[A-Z]" 或者 tr "[:lower:]" "[:upper:]"

echo "May Day,May Day,Going Down.." | tr "[a-z]" "[A-Z]"  

tr -cs "[a-z][A-Z]" "\n" < diray.txt 

sort

-r 逆向排序

-c 测试文件是否已经分类。

-m 合并两个分类文件。

-u 删除所有复制行。

-o 存储s o r t结果的输出文件名。- o选项保存分类结果,然而也可以使用重定向方法保存

sort -c file.txt

split

用途

切割文件。

参数

-<行数>-l<行数 指定每多少行就要切成一个小文件。

-b<字节 指定每多少字就要切成一个小文件。<字节>后再加上单位,总共有3种类型:

           b    512字节

           k    1024字节(1KB

           m    1048576字节(1MB

           如-b 3b表示以15363*512)字节为单位来进行切割。

-C<字节 与-b参数类似,但切割时尽量维持每行的完整性。

--help  显示帮助。

--version  显示版本信息。

[输出文件名 设置切割后文件的前置文件名,split会自动在前置文件名后再加上编号。例子

将 largefile 100行为单位切割成小文件,切割后的文件的前置文件名为small: 

$ split -l 100 largefile small

split不加参数,默认以1000行一个文件分割,文件名以xaaxabxac....

-l参数,以1500行分割文件

[root@localhost ~]# split -l 1500 aaa.sql

[root@localhost ~]# wc -l xa*

   1500 xaa

   1500 xab

    532 xac

   3532 总计

以文件大小来分割-b参数,1M来分割文件

[root@localhost ~]# split -b 1m aaa.sql 

[root@localhost ~]# ll xa*

-rw-r--r-- 1 root root 1048576 12-28 04:48 xaa

-rw-r--r-- 1 root root 1048576 12-28 04:48 xab

-rw-r--r-- 1 root root  577934 12-28 04:48 xac

wc命令的功能为统计指定文件中的字节数、字数、行数并将统计结果显示输出。 

语法:wc [选项文件… 

说明:该命令统计给定文件中的字节数、字数、行数。如果没有给出文件名,则从标准输入读取。wc同时也给出所有指定文件的总统计数。字是由空格字符区分开的最大字符串。 

该命令各选项含义如下: 

- c 统计字节数。 

- l 统计行数。 

- w 统计字数。 

1.编写程序weeklist,使用for循环输出每周7天的英文缩写

2.编写程序autohello,每1秒钟向屏幕输出一个linux字符串,形式如:

linux linux linux linux linux ....。一共输出10个字串以后提示输出完毕并自动退出。

3.编写程序square20,使用until循环,输出1-20之间各数的平方,要求一共分4行输出,各值以制表位分割

4. 编写输入几个数值求和程序mysum,利用while循环、位置变量和shift,如输入:

#./mysum  1  2  3  4   能正确计算4个数的和。

5.编写程序hellotime,使用if选择结构,判断当前系统时间和当前用户;如果是上午,输出"Good Morning!! Kenthy!!"的消息,如果是下午输出"Good Afternoon!! Kenthy!!",其它时间输出"Good Evening!! Kenthy!!"Kenthy为执行该程序的用户名。

6.编写程序pwadduser,使用系统的newusers,pwunconv,chpasswd,pwconv批量增加用户的步骤,实现批量增加50个用户,用户名如thiz101....thiz150,配置用户的初始密码为ilovelinux

7.编写程序fladduser,要求直接修改passwdshadowgroupgshadow配置文件,创建各用户的主目录并赋予权限,实现批量增加50个用户,用户名如linux101....linux150,配置用户的初始密码为空。

8.编写程序dlusers,批量删除刚才建立的50个用户及其主目录。

9.编写程序mybackup,实现简单的交互式的备份与恢复,可以让用户选择备份、恢复及退出程序操作,备份或恢复的目录由用户输入路径指定。

10.编写命令程序calculator,要求实现简单的整数四则运算(+ - * /),如

执行#calculator  11  +  12 后显示结果:11 + 12 = 23

执行#calculator  21  \/  3 后显示结果:21 / 3  = 7

 



你可能感兴趣的:(Ubuntu)