zhufeng518 发表于 2018-8-21 06:25:02

bash shell的基本概述与操作

  Bash shell的基本操作与概念
  特殊的shell变量
  除了用户自定义的变量以外,在linux系统和bash shell环境中还有一些特殊的变量-环境变量,位置变量,预定义变量。下面分别进行介绍
  环境变量
  环境变量指的是出于运行需要而由linux系统提前创建的一类变量,主要用于设置用户的工作环境,包括用户的宿主目录,命令查找路径,用户当前目录,登录终端等。环境变量的值由linux系统自动维护,会随着用户状态的改变而改变。
  使用env命令可以查看到当前工作环境下的环境变量,对于一些常见的环境变量应了解其各自的用途。例如变量USER表示用户名称,HOME表示用户宿主目录,LANG表示语言和字符集,PWD表示当前所在的工作目录,PATH表示命令搜索路径。
  # env
  HOSTNAME=Centos01.test.com
  TERM=xterm
  SHELL=/bin/bash
  HISTSIZE=1000
  SSH_CLIENT=192.168.1.100 4053 22
  QTDIR=/usr/lib64/qt-3.3
  QTINC=/usr/lib64/qt-3.3/include
  SSH_TTY=/dev/pts/1
  USER=root
  PATH变量用于设置可执行程序的默认搜索路径,当仅指定文件名称来执行命令程序时,linux系统将PATH变量指定的目录搜索范围查找对应的可执行文件,如果找不到则会提示command not found.
  # mkdir /root/scripts
  # cd /root/scripts
  # vi first.sh
  # chmod +x first.sh
  # ls -lh /root/scripts/first.sh
  -rwxr-xr-x 1 root root 44 Jun4 23:16 /root/scripts/first.sh
  # echo $PATH
  /usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
  # cd
  # first.sh
  -bash: first.sh: command not found
  # PATH="$PATH:/root/scripts"
  # echo $PATH
  /usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/root/scripts
  # first.sh
  #
  # /etc/fstab
  # Created by anaconda on Thu Jun2 19:35:06 2016
  #
  # Accessible filesystems, by reference, are maintained under '/dev/disk'
  # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
  #
  /dev/mapper/vg_centos01-lv_root /                     ext4    defaults      1 1
  UUID=3bb70b22-a906-43ea-97d1-ca28485afec7 /boot                   ext4    defaults      1 2
  /dev/mapper/vg_centos01-lv_home /home                   ext4    defaults      1 2
  /dev/mapper/vg_centos01-lv_swap swap                  swap    defaults      0 0
  tmpfs                   /dev/shm                tmpfs   defaults      0 0
  devpts                  /dev/pts                devptsgid=5,mode=6200 0
  sysfs                   /sys                  sysfs   defaults      0 0
  proc                  /proc                   proc    defaults      0 0
  16 /etc/fstab
  #
  在linux系统中,环境变量的全局配置文件为/etc/profile,在此文件中定义的变量作用域所有用户。除此之外,每个用户还有自己的独立配置文件(./bash_profile)。若要长期变更或设置某个环境变量,应在上述文件中进行设置。例如,执行以下操作可以将记录的历史命令条数改为200条,默认为1000条,只针对root用户
  # vi /root/.bash_profile
  export HISTSIZE=200
  上述修改只有当root用户下次登录时才会生效,若希望立即生效,应手动修改环境变量或者加载配置文件执行。
  # source /root/.bash_profile
  位置变量
  为了在使用shell脚本程序时,方便通过命令行为程序提供操作参数,bash引入了位置变量的概念。当执行命令行操作时,第一个字段表示命令名或脚本程序名,其余的字符串参数按照从左到右的顺序依次赋值给位置变量。
  位置变量也成为位置参数,使用$1,$2,$3,$4.....表示,例如当执行命令行“ls -ls /boot/”时,其中第一个位置变量为“-lh”以$1表示,第二个位置变量为/boot/ ,使用$2表示,命令或脚本本身的名称使用$0表示。虽然$0与位置变量的格式,但是$0属于预定义变量而不是位置变量。
  为了说明位置变量的作用,下面编写一个加法运算的脚本adder2num.sh,用来计算两个整数的合,需要计算的两个整数在执行脚本时以位置变量的形式提供。
  # vi adder2num.sh
  #!/bin/bash
  sum=`expr $1 + $2`
  echo "$1 + $2 = $sum"
  预定义变量是有bash程序预先定义好的一类特殊变量,用户只能使用预定义变量,而不能创建新的预定义变量,也不能直接为预定义变量赋值。预定义变量使用$符号和另一个符号组合表示,较常用的预定义变量的含义如下:
  $#:表示命令行中位置参数的个数
  $*: 表示所有位置参数的内容
  $?: 表示前一条命令执行后的返回状态,返回值为0表示执行正确,返回任何非0值均表示执行出现异常。
  $0: 表示当前执行的脚本或程序的名称
  GNU的date提供+%s(小写s), 能打印出自1970-01-01 00:00:00到当前时间的秒数.
  编写一个备份操作的脚本,用来打包命令行指定的多个文件或目录,并输出相关信息,其中,新建的压缩包文件名称中嵌入秒数。
  #!/bin/bash
  tarfile=beifen-`date +%s`.tgz
  tar zcf $tarfile $* &> /dev/null
  echo "已执行 $0 脚本"
  echo "共完成 $# 个对象的备份"
  echo "具体内容包括: $* "
  执行结果是:
  # ./mybak.sh /etc/passwd /etc/shadow
  已执行 ./mybak.sh 脚本
  共完成 2 个对象的备份
  具体内容包括: /etc/passwd /etc/shadow
  shell条件测试
  如果一个表达式自身不足以做条件测试的话,可以使用 test [测试表达式] 或者 [ 测试表达式 ]
  I/O重定向
  标准输出重定向
  > :覆盖重定向,目标文件中的原有内容会被消除
  >> :追加重定向,新内容会追加至目标文件尾部
  set -C :禁止将内容覆盖输出到已有的文件中
  若要强制覆盖可以使用 >| 例如 cat /etc/fstab >| /tmp/aa.txt
  set +C:关闭以上功能
  # ls /var > /tmp/etc.out
  # ls /var/log/ >> /tmp/etc.out
  标准错误重定向
  # cat /etc/issuee 2> /tmp/issue.txt
  # cat /tmp/issue.txt
  cat: /etc/issuee: No such file or directory
  # cat /etc/issue 2> /tmp/issue.txt

  CentOS>  Kernel \r on an \m
  # cat /tmp/issue.txt 发现文件是空的,因为没有错误输出,所以2>只能重定向错误输出
  2> :覆盖从定向错误输出
  2>> :追加重定向错误输出
  将标准输出和错误输出各自定向到不同位置:
  # tail -100 /etc/rc.d/rc.sysinit > /tmp/sysinit.out 2> /tmp/sysinit.err
  # cat /tmp/sysinit.err 这个文件是空的
  # cat /tmp/sysinit.out 这个文件是标准输出
  # tail -100 /etc/rc.d/rc.sysinidddt > /tmp/sysinit.out 2> /tmp/sysinit.err
  # cat /tmp/sysinit.err
  tail: cannot open `/etc/rc.d/rc.sysinidddt' for reading: No such file or directory
  可以合并标准输出和错误输出为同一个数据流进行重定向
  &> 覆盖重定向
  &>> 追加重定向
  # echo $PATH
  /usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
  You have new mail in /var/spool/mail/root
  # echo $PATH &> /tmp/path.out
  # cat /tmp/path.out
  /usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
  #
  # echoooo $PATH &> /tmp/path.out
  # cat /tmp/path.out
  -bash: echoooo: command not found
  特殊写法:将标准输出和错误输出写到同一个文件可以使用2>&1
  # echo "$PATH" > /tmp/path.out 2>&1
  # echoddd "$PATH" >> /tmp/path.out 2>&1
  # cat /tmp/path.out
  /usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
  -bash: echoddd: command not found
  输入重定向:将原本需要有键盘输入的数据,同过文件来读入。 <
  # useradd jerry
  # vi pass.txt
  # passwd --stdin jerry < pass.txt
  # cat aa.txt
  test stdin
  使用cat命令在键盘上输入一些数据,然后写入catfile文件中
  # cat > catfile
  testing
  cat file tset
  输入crtl+d结束
  此时就会有catfile文件产生,而且该文件的内容就是刚刚输入的内容,那么是否可以使用其他文件来替换键盘输入呢
  # cat > catfile < aa.txt
  # cat catfile
  test stdin
  #
  我们可以用cat直接将输入的消息输出到catfile中,并且当输入EOF时,该次输入就结束,可以这样做
  # cat > catfileok now stop
  > EOF
  You have new mail in /var/spool/mail/root
  # cat catfile
  this is a test testing
  ok now stop
  #
  # tr abc ABC < /etc/fstab
  #
  # /etC/fstAB
  # CreAted By AnACondA on Thu Jun2 19:35:06 2016
  #
  # ACCessiBle filesystems, By referenCe, Are mAintAined under '/dev/disk'
  # See mAn pAges fstAB(5), findfs(8), mount(8) And/or Blkid(8) for more info
  #
  /dev/mApper/vg_Centos01-lv_root /                     ext4    defAults      1 1
  UUID=3BB70B22-A906-43eA-97d1-CA28485AfeC7 /Boot                   ext4    defAults      1 2
  /dev/mApper/vg_Centos01-lv_home /home                   ext4    defAults      1 2
  /dev/mApper/vg_Centos01-lv_swAp swAp                  swAp    defAults      0 0
  tmpfs                   /dev/shm                tmpfs   defAults      0 0
  devpts                  /dev/pts                devptsgid=5,mode=6200 0
  sysfs                   /sys                  sysfs   defAults      0 0
  proC                  /proC                   proC    defAults      0 0
  # tr 'a-z' 'A-Z' < /etc/fstab
  #
  # /ETC/FSTAB
  # CREATED BY ANACONDA ON THU JUN2 19:35:06 2016
  #
  # ACCESSIBLE FILESYSTEMS, BY REFERENCE, ARE MAINTAINED UNDER '/DEV/DISK'
  # SEE MAN PAGES FSTAB(5), FINDFS(8), MOUNT(8) AND/OR BLKID(8) FOR MORE INFO
  #
  /DEV/MAPPER/VG_CENTOS01-LV_ROOT /                     EXT4    DEFAULTS      1 1
  UUID=3BB70B22-A906-43EA-97D1-CA28485AFEC7 /BOOT                   EXT4    DEFAULTS      1 2
  /DEV/MAPPER/VG_CENTOS01-LV_HOME /HOME                   EXT4    DEFAULTS      1 2
  /DEV/MAPPER/VG_CENTOS01-LV_SWAP SWAP                  SWAP    DEFAULTS      0 0
  TMPFS                   /DEV/SHM                TMPFS   DEFAULTS      0 0
  DEVPTS                  /DEV/PTS                DEVPTSGID=5,MODE=6200 0
  SYSFS                   /SYS                  SYSFS   DEFAULTS      0 0
  PROC                  /PROC                   PROC    DEFAULTS      0 0
  # tr -d abc
  alpha
  lph
  amnbak
  mnk
  此处生成文档
  # cathow are you?
  > how old are you?
  > EOF
  how are you?
  how old are you?
  # cat >> /tmp/test.outhow are you?
  > how old are you?
  > EOF
  # cat /tmp/test.out
  how are you?
  how old are you?
  利用 /tmp/who.out
  将/etc/passwd/文件中的前5行内容转换为大写后保存至/tmp/passwd.out文件中
  # cat /etc/passwd | head -n 5 | tr 'a-z' 'A-Z' > /tmp/passwd.out
  # cat /tmp/passwd.out
  ROOT:X:0:0:ROOT:/ROOT:/BIN/BASH
  BIN:X:1:1:BIN:/BIN:/SBIN/NOLOGIN
  DAEMON:X:2:2:DAEMON:/SBIN:/SBIN/NOLOGIN
  ADM:X:3:4:ADM:/VAR/ADM:/SBIN/NOLOGIN
  LP:X:4:7:LP:/VAR/SPOOL/LPD:/SBIN/NOLOGIN
  脚本示例
  1. 创建一个组newgroup,id号为4000
  2. 创建一个用户test,id号为3001,附加组为newgroup
  3. 创建目录/tmp/hellod
  4. 复制/etc/fstab至上面的目录中
  5. 改变目录及内部文件的属主和属组为test
  6. 让目录及内部文件的其他用户没有任何权限
  # cat aa.sh
  #!/bin/bash
  groupadd -g 4000 newgroup
  useradd -u 3001 -G newgroup test
  mkdir /tmp/hellod
  cp /etc/fstab /tmp/hellod
  chown -R test:test /tmp/hellod
  chmod -R o= /tmp/hellod
  或者
  #!/bin/bash
  myGroup="newgroup"
  myUser="test"
  myDir="/tmp/hellod"
  myID=3001
  groupadd -g 4000 $myGroup
  useradd -u $myID -G $myGroup $myUser
  mkdir $myDir
  cp /etc/fstab $myDir
  chown -R $myUser:$myUser $myUser
  chmod -R o= $myDir
  变量的命名要求
  只能使用数字、字母、下划线组成
  不能以数字开头
  不能使用程序中关键字
  要做到见名之意
  awk可以处理后续文件,也可以读取来自前一个命令的标准输出。awk主要是处理“每一行字段内的数据”,而默认的字段分隔符为空格键或tab键,举例来说,用last可以将登录者的数据取出来,结果如图所示:
  若想取出账号与登录者的IP地址,且账号与IP地址之间以tab键隔开,则会变成这样:
  # last | awk '{print $1 "\t" $3}'
  root192.168.1.100
  root192.168.1.100
  rebootboot
  root:0.0
  root:0
  awk是以行尾处理单位,而以字段为最小的处理单位
  awk举例:
  # ifconfig eth0 | grep "HWaddr"
  eth0      Link encap:EthernetHWaddr 00:0C:29:1A:E3:AA
  # ifconfig eth0 | grep "HWaddr" | awk '{print $5}'
  00:0C:29:1A:E3:AA
  # hwaddr=$(ifconfig eth0 | grep "HWaddr" | awk '{print $5}')
  # echo hwaddr
  hwaddr
  # echo $hwaddr
  00:0C:29:1A:E3:AA
  # grep "bash$" /etc/passwd
  root:x:0:0:root:/root:/bin/bash
  mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash
  cacti:x:500:500::/home/cacti:/bin/bash
  # grep "bash$" /etc/passwd | awk -F: '{print $1,$7}'
  root /bin/bash
  mysql /bin/bash
  cacti /bin/bash
  #
  特殊变量:
  $0;脚本名称自身
  $?: 上一条命令的执行状态,状态用数字来表示:0-255 0表示成功,1-255表示失败
  命令执行成功了,成功的标记叫状态返回值,而正常的命令执行后的结果叫返回值。
  环境变量:用来定义bash的工作特性,用于保存当前会话的属性信息。
  显示所有环境变量:export,env,printenv
  env是environment(环境)的缩写。
  执行env命令即可获得当前shell环境下所有环境变量及其内容
  # env
  定义环境变量:
  export Var_Name="Value"
  例如:
  # echo $PATH
  /usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
  # mkdir /root/sbin
  # export PATH=/root/sbin/
  # cat /etc/fstab
  -bash: cat: command not found
  重新登录该终端就又可以访问了
  # cat /etc/fstab
  #
  # /etc/fstab
  # Created by anaconda on Thu Jun2 21:38:10 2016
  #
  # Accessible filesystems, by reference, are maintained under '/dev/disk'
  # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
  #
  /dev/mapper/vg_centos6-lv_root /                     ext4    defaults      1 1
  UUID=f8f486f4-8f58-4619-92f9-0fdde943166c /boot                   ext4    defaults      1 2
  /dev/mapper/vg_centos6-lv_home /home                   ext4    defaults      1 2
  /dev/mapper/vg_centos6-lv_swap swap                  swap    defaults      0 0
  tmpfs                   /dev/shm                tmpfs   defaults      0 0
  devpts                  /dev/pts                devptsgid=5,mode=6200 0
  sysfs                   /sys                  sysfs   defaults      0 0
  proc                  /proc                   proc    defaults      0 0
  不重新登录该终端也可以,# export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
  若要在PATH环境变量中增加/root/sbin
  # echo $PATH
  /bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
  # export PATH=$PATH:/root/sbin
  # echo $PATH
  /bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/root/sbin
  # export PATH=/root/sbin:$PATH
  # echo $PATH
  /root/sbin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/root/sbin
  改过来
  # export PATH=$PATH:/root/sbin
  bash的配置文件:持久保存用户配置
  profile:为交互式登录用户提供配置
  /etc/profile --->全局的,对所有用户有效
  /etc/profile.d/*.sh--->全局的,对所有用户有效
  每个用户家目录下的~/.bash_profile   --->个人的,仅对当前用户有效,
  profile的功能:
  设定环境变量
  运行命令或脚本时
  # cat .bash_profile 由于只配置了root用户的.bash_profile,只对root用户生效。
  # .bash_profile
  # Get the aliases and functions
  if [ -f ~/.bashrc ]; then
  . ~/.bashrc
  fi
  # User specific environment and startup programs
  PATH=$PATH:$HOME/bin
  export PATH
  echo "welcome here...."--->添加此行
  重新登录的时候就会显示
  Last login: Tue Jun 14 16:21:44 2016 from 192.168.1.100
  welcome here....
  bashrc:为非交互式登录用户提供配置
  .bashrc功能:
  可以设定本地变量,让脚本运行时有变量可以使用
  可以定义命令别名
  # cat .bashrc
  # .bashrc
  # User specific aliases and functions
  alias rm='rm -i'
  alias cp='cp -i'
  alias mv='mv -i'
  # Source global definitions
  if [ -f /etc/bashrc ]; then
  . /etc/bashrc
  fi
  登录类型:
  交互式登录:
  直接通过终端输入用户名密码登录系统
  其实su - 用户名 或者 su -l 用户名方式登录也算是交互式登录
  对于交互式用户来讲是读取配置文件的顺序
  首先会读取/etc/profile,/etc/profile.d/*.sh,接着读~/.bash_profile,~/.bashrc,在读/etc/bashrc
  /etc/profile--->/etc/profile.d/*.sh--->~/.bash_profile--->~/.bashrc--->/etc/bashrc 如有冲突,最后被source的生效
  非交互式:
  su 用户名
  图形界面的终端也是非交互式登录
  执行脚本的时候需要使用解释器来解释执行,解释器在解释脚本时会读取配置文件先设置解释环境的
  /etc/bashrc:全局
  ~/.bashrc:个人配置
  对于非交互式用户来讲是读取配置文件的顺序
  ~/.bashrc---> /etc/bashrc--->/etc/profile.d/*.sh
  通知shell重读配置文件的命令:source .bashrc或者 . .bashrc
  bash的编程
  程序有3中执行流程:
  1. 顺序执行:如果有很多命令的时候,自上而下逐条被执行,
  2. 选择执行: 选择执行肯定会有n个分支,事先有个条件判断,符合条件的分支将被执行,不符合分支的条件将不被执行
  3. 循环执行:对于计算机而言拉磨式的执行。即对同一段代码反复执行有限次,所以必须要有退出条件,否则会进入无限循环
  若想实现以上流程的定义需要用到所谓的编程控制语句,此前声明变量,为变量赋值的我们称之为赋值语句
  所以一个程序代码是由语句和表达式组成的。为了实现定义以上的流程就会用到控制语句
  控制语句:
  bash的循环控制语句:for while until
  for 循环是一种事先就知道循环次数的,遍历指定列表的方式来实现的循环。
  for 变量名 in 列表; do
  语句1
  语句2
  done
  for 语句中变量名是用来变量赋值而非引用变量,不能使用$,切记
  使用for循环添加三个用户
  分析:使用useradd添加用户,执行3遍,这三遍当中如果那个用户,每一遍都使用同一个变量来引用,而每一遍执行的时候都把变量的值改一下就可以实现了
  for usreName in shuguang liming huanghun;do
  useradd $userName
  done
  当程序执行这段代码的时候,首先会把shuguang的这个字符串赋值给userName这个变量,而后执行useradd $userName这条语句,执行完就done结束了,然后进入下一次循环,将liming这个字符串赋值给userName这个变量,而后执行useradd $userName这个语句,执行完就done结束了,然后进入下一次循环,直到遍历完整个列表。所以列表中存储的元素的个数决定循环的次数。
  # cat autouseradd.sh
  #!/bin/bash
  for userName in shuguang liming huanghun;do
  useradd $userName
  done
  检测脚本是否有语法错误
  # bash -n autouseradd.sh
  执行脚本
  # bash autouseradd.sh
  验证三个用户是否添加成功
  # tail -3 /etc/passwd
  shuguang:x:500:500::/home/shuguang:/bin/bash
  liming:x:501:501::/home/liming:/bin/bash
  huanghun:x:502:502::/home/huanghun:/bin/bash
  使用for循环添加10个用户,分别为user101-user110
  分析:for循环的列表中有10个用户,写起来也很繁琐
  列表生成的方法:只要能够实现在命令行中展开的功能都可以生成列表
  生成数字序列:
  可以使用花括号 {start..end} {1..10}
  可以使用seq start end
  # seq 1 10
  1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  #
  很显然seq命令既然执行后生成序列,我们做个命令的引用就可以生成列表
  # seq 1 2 10其中2为步长
  1
  3
  5
  7
  9
  # for userName in $(seq 101 110);do echo $userName;done
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  # for userName in $(seq 101 110);do echo user$userName;done
  user101
  user102
  user103
  user104
  user105
  user106
  user107
  user108
  user109
  user110
  直接把echo换成useradd就循环添加10个user101-user110的用户了
  # for userName in $(seq 101 110);do useradd user$userName;done
  # tail -10 /etc/passwd
  user101:x:503:503::/home/user101:/bin/bash
  user102:x:504:504::/home/user102:/bin/bash
  user103:x:505:505::/home/user103:/bin/bash
  user104:x:506:506::/home/user104:/bin/bash
  user105:x:507:507::/home/user105:/bin/bash
  user106:x:508:508::/home/user106:/bin/bash
  user107:x:509:509::/home/user107:/bin/bash
  user108:x:510:510::/home/user108:/bin/bash
  user109:x:511:511::/home/user109:/bin/bash
  user110:x:512:512::/home/user110:/bin/bash
  或者
  # cat useradd.sh
  #!/bin/bash
  for userName in `seq 101 110`;do
  useradd user$userName
  echo "Add user$userName successfully."
  done
  文本处理类命令:wc
  # wc /etc/issue
  39 47 /etc/issue3行,9个单词,47个字节
  wc ...
  -l:统计行数
  -c:统计字节数
  -w:统计单词数
  写一个脚本,用for循环实现
  1.显示/etc/init.d/functions,/etc/rc.d/rc.sysinit和/etc/fstab各有多上行
  #!/bin/bash
  for wcline in /etc/init.d/functions /etc/rc.d/rc.sysinit /etc/fstab;do
  echo "$wcline的行数为:" `cat $wcline | wc -l`
  done
  或者
  # cat wcline.sh
  #!/bin/bash
  for wcline in /etc/init.d/functions /etc/rc.d/rc.sysinit /etc/fstab;do
  echo "$wcline: `wc -l $wcline | cut -d' ' -f1` lines;"
  done
  或者
  # cat wcline.sh
  #!/bin/bash
  for wcline in /etc/init.d/functions /etc/rc.d/rc.sysinit /etc/fstab;do
  lineCount=`wc -l $wcline | cut -d' ' -f1`
  echo "$wcline: $lineCount lines;"
  done
  2.将上题中三个文件复制到/tmp目录中,分别将每个文件的最近一次修改时间改为2016年3月15日13点27分
  for fileName in /etc/init.d/functions /etc/rc.d/rc.sysinit /etc/fstab;do
  cp - $fileName /tmp
  touch -m -t 201603151327 /tmp/`basename $fileName`
  done
  # ll functions rc.sysinit fstab
  -rw-r--r-- 1 root root   866 Mar 15 13:27 fstab
  -rw-r--r-- 1 root root 18586 Mar 15 13:27 functions
  -rwxr-xr-x 1 root root 19688 Mar 15 13:27 rc.sysinit
  3.显示/etc/passwd中第3,7,11个用户的用户名和用户id
  # cat uid.sh
  #!/bin/bash
  for lineNo in 3 7 11;do
  head -n $lineNo /etc/passwd | tail -1 | cut -d: -f1,3
  done
  # bash uid.sh
  daemon:2
  shutdown:6
  operator:11
  4.用file命令显示/var/log目录下的每个文件的内容类型
  # cat file.sh
  #!/bin/bash
  dirName=/var/log
  for fileName in $dirName/*;do
  file $fileName
  done
  执行脚本,验证结果
  # bash file.sh
  /var/log/anaconda.ifcfg.log: ASCII text
  /var/log/anaconda.log: ASCII English text
  /var/log/anaconda.program.log: ASCII English text, with very long lines, with overstriking
  /var/log/anaconda.storage.log: ASCII C++ program text, with very long lines
  /var/log/anaconda.syslog: ASCII English text, with very long lines
  /var/log/anaconda.xlog: ASCII English text
  /var/log/anaconda.yum.log: ASCII text
  /var/log/audit: directory
  /var/log/boot.log: ASCII English text, with CRLF, CR line terminators, with escape sequences
  /var/log/btmp: empty
  /var/log/ConsoleKit: directory
  for总结:
  通过使用一个变量去遍历给定列表中的每个元素,在每次变量赋值时执行一次循环体,直至赋值完成所有元素退出循环
  生成列表的总结:
  1. 直接给出列表
  2. 使用文件名通配的方式生成序列
  3. 可以使用{}或seq命令生成数字序列
  4. 使用命令生成 cat、ls
  bash中的算数运算
  在bash中无需事先声明变量,bash是一种弱类型的语言,无需区分变量类型,既然不用区分类型,变量中数据存储的默认格式是字符串,所以字符串是不能进行数值运算的,
  # num1=1
  # num2=3
  # $num1+$num2
  -bash: 1+3: command not found
  # sum=$num1+$num2
  # echo $sum
  1+3
  #
  若想进行数值运算:
  declare
  -i: 整形
  -x: 环境变量
  let varName=算数表达式
  varName=$[算数表达式]
  varName=$((算数表达式))
  varName=`expr $num1 + $num2`
  # aa=$[$num1*$num2]
  # echo $aa
  3
  # let sum=$num1+$num2
  # echo $sum
  4
  # aa=$(($num1*$num2))
  # echo $aa
  3
  # expr $num1 + $num2
  4
  操作符:+ - * / %
  bash不能进行浮点计算
  # let er=$num1/$num2
  # echo $er
  0
  自加运算
  total=$[$total+1]
  let total+=1
  let total++
  # a=40
  # a=$[$a+2]
  # echo $a
  42
  # a=40
  # echo $a
  40
  # let a+=2
  # echo $a
  42
  #
  练习
  1. 计算100以内所有正整数之和
  每次都是sum自身的基础上加i
  # cat sum.sh
  #!/bin/bash
  declare -i sum=0
  for i in {1..100};do
  let sum=$sum+$i
  done
  echo "the sum is: $sum"
  执行脚本
  # bash sum.sh
  the sum is: 5050
  可以单步执行时加-x参数
  # bash -x sum.sh
  + declare -i sum=0
  + for i in '{1..100}'
  + let sum=0+1
  + for i in '{1..100}'
  + let sum=1+2
  + for i in '{1..100}'
  + let sum=3+3
  2. 分别计算100以内所有偶数之和和奇数之和
  # cat oddsum.sh
  #!/bin/bash
  declare -i oddsum=0,evensum=0
  for i in `seq 2 2 100`;do
  oddsum=$[$oddsum+$i]
  done
  for j in `seq 1 2 100`;do
  evensum=$[$evensum+$j]
  done
  echo "the evensum is $evensum,the odd sum is: $oddsum"
  3. 计算当前系统所有用户的ID之和
  # cat useridsum.sh
  #!/bin/bash
  declare -i uidSum=0
  for i in `cut -d: -f3 /etc/passwd`; do
  uidSum=$[$uidSum+$i]
  done
  echo "the UIDSum is: $uidSum"
  执行脚本结果如下:
  # bash useridsum.sh
  the UIDSum is: 70943
  4. 计算/etc/init.d/functions /etc/rc.d/rc.sysinit /etc/issue三个文件的字符数之和
  # cat bytecount.sh
  #!/bin/bash
  declare -i bytesCount=0
  for file in /etc/init.d/functions /etc/rc.d/rc.sysinit /etc/issue; do
  let bytesCount=$bytesCount+`wc -c $file | cut -d' ' -f1`
  done
  echo $bytesCount
  执行脚本的结果
  # bash bytecount.sh
  38321
  5. 新建用户user1-user10,并计算用户的id之和
  # cat userid.sh
  #!/bin/bash
  declare -i uidSum=0
  for i in `seq 1 10`; do
  useradd user$i
  let uidSum=$uidSum+`id -u user$i`
  done
  echo$uidSum
  执行脚本
  # bash userid.sh
  5195
  位置参数:
  $0:脚本自身
  $1:脚本的第一个参数
  $2:
  # cat pos.sh
  #!/bin/bash
  echo $1
  echo $2
  echo $0
  # chmod +x pos.sh
  # ./pos.sh 5 9
  5
  9
  ./pos.sh
  # cat pos.sh
  #!/bin/bash
  echo "the sum is: $[$1+$2]."
  # ./pos.sh 10 20
  the sum is: 30.
  $# 引用位置参数的个数
  # cat pos.sh
  #!/bin/bash
  echo "the sum is: $[$1+$2]."
  echo "the mul is: $[$1*$2]."
  echo $#
  # ./pos.sh 10 20 3 8
  the sum is: 30.
  the mul is: 200.
  4
  $* 引用所有的位置参数
  # cat ./pos.sh
  #!/bin/bash
  echo "the sum is: $[$1+$2]."
  echo "the mul is: $[$1*$2]."
  echo $#
  echo $*
  # ./pos.sh 4 5 7 7
  the sum is: 9.
  the mul is: 20.
  4
  4 5 7 7
  交互式脚本
  给变量默认值
  varName=${varName:-value}
  如果varName不空,则返回varName的值,否则返回value的值
  如果varName不空,则其值不变;否则,varName会使用value作为其值
  # echo $a
  42
  # a=${a:-45}
  # echo $a
  42
  # unset a
  # a=${a:-45}
  # echo $a
  45
  举例:
  # cat read1.sh
  #!/bin/bash
  read -t 5 -p "Enter a number:" num1
  num1=${num1:-0}
  echo $num1
  # bash read1.sh
  Enter a number:6
  6
  过5秒钟不输入任何数字,num1的值为0
  # bash read1.sh
  Enter a number:0
  # read a b
  50 100
  # echo $a
  50
  # echo $b
  100
  # read c d
  30
  # echo $c
  30
  # echo $d
  #
  # read e f
  2 4 5 6 7
  # echo $e
  2
  # echo $f
  4 5 6 7
  #
  练习:
  1. 通过键盘给定一个文件路径,来判断文件内容类型:
  # cat yyy.sh
  #!/bin/bash
  read -p "Enter a file path: " filename
  file $filename
  # bash yyy.sh
  Enter a file path: /etc/fstab
  /etc/fstab: ASCII text
  bash编程之条件判断
  条件判断的目的是:判定后续操作的前提条件是否满足
  条件判断的常用类型:
  整数测试:
  字符测试:
  文件测试:
  布尔值:
  真,假
  逻辑运算:
  与运算:
  真 && 真 = 真
  真 && 假 = 假
  假 && 真 = 假
  假 && 假 = 假
  或运算
  真 || 真 = 真
  真 || 假 = 真
  假 || 真 = 真
  假 || 假 = 假
  非运算
  !真 = 假
  !假 = 真
  bash如何做测试
  1 test 表达式
  2 [ 表达式 ] 表达式两端必须要有空格
  在什么样的场景中使用bash条件测试
  bash中条件判断使用if
  单分支
  if 条件; then
  分支1;
  fi
  双分支
  if 条件; then
  分支1;
  else
  分支2;
  fi
  多分支
  if 条件1;then
  分支1;
  elif 条件2; then
  分支2;
  elif 条件3; then
  分支3;
  ...
  else
  分支n;
  fi
  只要命令用作条件,就表示引用的是其状态结果(即执行成功与否),而非命令的输出结果,因此,不能使用命令替换符(``,$()),只有将命令执行的结果保存到变量当中,或引用命令执行结果时需要使用反撇号$()
  练习:
  1. 让用户指定一个文件,判定如果文件有空白行,就显示空白行数
  # cat yin.sh
  #!/bin/bash
  #
  read -p "Enter a file path: " filename
  if grep "^$" $filename &> /dev/null; then
  linesCount=`grep "^$" $filename | wc -l`
  echo "$filename has $linesCount space lines."
  fi
  执行结果
  # bash yin.sh
  Enter a file path: /etc/rc.d/rc.sysinit
  /etc/rc.d/rc.sysinit has 96 space lines.
  如果输入/etc/passwd,由于没有空格,分支语句将不会被执行。
  # bash yin.sh
  Enter a file path: /etc/passwd
  #
  2. 让用户指定一个文件,判定如果文件有空白行,就显示空白行数,如果没有空白行,就显示没有空白行
  #!/bin/bash
  #
  read -p "Enter a file path: " filename
  if grep "^$" $filename &> /dev/null; then
  linesCount=`grep "^$" $filename | wc -l`
  echo "$filename has $linesCount space lines."
  else
  echo "$filename hava no space line."
  fi
  执行结果
  # bash yin.sh
  Enter a file path: /etc/passwd
  /etc/passwd hava no space line.
  3. bash整数测试
  二元测试
  -gt:大于 [ $num1 -gt $num2 ]
  -lt:小于
  -ge:大于等于
  -le:小于等于
  -ne:不等于
  -eq:等于
  练习:
  1. 用户通过命令行输入两个数字,比较大小,整数是通过命令行参数传递而来的
  # cat euu.sh
  #!/bin/bash
  #
  if [ $1 -gt $2 ]; then
  echo "the max num is $1."
  else
  echo "the max num is $2."
  fi
  # chmod +x euu.sh
  # ./euu.sh 67 98
  the max num is 98.
  # ./euu.sh 12 30
  the max num is 30.
  脚本自定义退出:
  # cat exit.sh
  #!/bin/bash
  echo "hello"
  exit 6
  echo "world"
  执行脚本
  # bash exit.sh
  hello
  # echo $?
  6
  如果注释exit 6
  # cat exit.sh
  #!/bin/bash
  echo "hello"
  #exit 6
  echo "world"
  执行脚本
  # bash exit.sh
  hello
  world
  # echo $?
  0
  修改脚本为
  # cat exit.sh
  #!/bin/bash
  ls /etcccc
  echo "hello"
  #exit 6
  echo "world"
  执行脚本
  # bash exit.sh
  ls: cannot access /etcccc: No such file or directory
  hello
  world
  # echo $?
  0
  判断两个数字的大小,如果用户只输入1个数字,没有办法判断,所以还可以补充一下上面的脚本
  # cat euu.sh
  #!/bin/bash
  #
  if [ $# -lt 2 ]; then
  echo "stupid..."
  echo "`basename $0` argu1 argu2"
  exit 4
  fi
  if [ $1 -gt $2 ]; then
  echo "the max num is $1."
  else
  echo "the max num is $2."
  fi
  执行脚本的的结果如下
  # ./euu.sh 20
  stupid...
  euu.sh argu1 argu2
  # ./euu.sh 20 10
  the max num is 20.
  练习:
  1. 写一个脚本,实现如下功能
  让用户通过键盘输入一个用户名,如果用户存在,就显示其用户名和uid否则就显示用户名不存在
  # cat aa2.sh
  #!/bin/bash
  read -t 10 -p "Enter a username:" username
  # username-${username:-root}

  if>  userid=`id -u $username`
  echo "$username: $userid"
  else
  echo "$username not exist."
  fi
  执行脚本
  # bash aa2.sh
  Enter a username:huarong
  huarong not exist.
  # bash aa2.sh
  Enter a username:user1
  user1: 515
  2. 让用户通过键盘输入一个用户名,如果用户不存在就退出,如果用户的uid大于等于500,就说明他是普通用户,否则就说明是管理员或系统用户
  # cat aa3.sh
  #!/bin/bash
  read -p "Enter a username:" username

  if !>  echo "$usernam not exist."
  exit 6
  fi
  userid=`id -u $username`
  if [ $userid -ge 500 ]; then
  echo "A common user;"
  else
  echo "Admin or system user;"
  fi
  执行脚本
  # bash aa3.sh
  Enter a username:root
  Admin or system user;
  # bash aa3.sh
  Enter a username:user1
  A common user;
  3. 让用户通过键盘输入一个用户名,如果用户不存在就退出,如果其UID等于GID,就说他是一个"good guy",否则就说他是个"bad guy"
  # cat aa4.sh
  #!/bin/bash
  read -p "Enter a username:" username

  if !>  echo "$usernam not exist."
  exit 6
  fi
  if [ `id -u $username` -eq `id -g $username` ]; then
  echo "good guy"
  else
  echo "bad guy"
  fi
  执行脚本
  4. 判断当前系统上的所有用户是good guy还是bad by
  for username in `cut -d: -f1 /etc/passwd`; do
  if [ `id -u $username` -eq `id -g $username` ]; then
  echo "good guy"
  else
  echo "bad guy"
  fi
  done
  5. 写一个脚本,实现如下功能:
  添加10个用户stu1-stu10,但要先判断用户是否存在,如果存在,就显示其已经存在,否则就添加此用户,,最后显示一共添加了几个用户
  # cat aa5.sh
  #!/bin/bash
  declare -i usercount=0
  for i in {1..10}; do

  if>  echo "stu$i exists."
  else
  useradd stu$i && echo "stu$i add finished."
  let usercount++
  fi
  done
  echo "add $usercount users;"
  6. 求200以内所有3的整数倍的正整数之和
  # cat sum1.sh
  #!/bin/bash
  declare -i sum=0
  for i in {1..200}; do
  if [ $[$i%3] -eq 0 ]; then
  let sum+=$i
  fi
  done
  echo "the sum is: $sum."
  # bash sum1.sh
  the sum is: 6633.
  组合条件测试,对条件做逻辑运算
  与运算的短路操作
  与:
  条件1 && 条件2
  条件1为假,则最终结果一定为假,因此条件2将不被执行
  条件1为真,则最终结果取决于后面条件,因此条件2必须被执行
  # username=root

  #>  uid=0(root) gid=0(root) groups=0(root)
  root exist
  # username=roooot

  #>  id: roooot: No such user
  #
  或:
  条件1 || 条件2
  条件1为真,则最终结果一定为真,因此条件2将不被执行。
  ot@Centos6 ~]# username=root

  #>  #
  条件2为假,则最终结果取决于后面条件,因此条件2必须被执行
  # username=roooot

  #>  roooot notexist

  #>  root exist.
  # username=rootttt

  #>  rootttt notexist
  非:! 条件
  bash编程之字符测试
  双目:
  >
  <
  ==等于,等值比较
  ~~:左侧是字符串,右侧是一个模式,判定左侧的字符串能否右侧的模式所匹配,通常只在[[]中使用,模式中可以使用行首,行尾锚定符,但模式不要叫引号
  单目:
  -n $stringvar:字符串是否不空,不空为真,空则为假
  -z $stringvar:字符串是否为空,空则为真,不空则假
  stringA="root"
  stringB="hello"
  [ "stringA" == "stringB" ]
  echo $?
  1
  stringB="root"
  双引号不能被省略
  # clear
  # stringA="root"
  # [ -n "$stringA" ]
  # echo $?
  0
  #
  # [ -z "$stringA" ]
  # echo $?
  1
  # [ -z "$stringB" ]
  # echo $?
  0
  # [[ "$stringA"] =~ ot ]]
  # echo $?
  0
  # [[ "$stringA"] =~ to ]]
  # echo $?
  1
  #
  判定某用户的默认shell是以sh结尾的
  # [[ `grep "^$username\>" /etc/passwd | cut -d: -f7` =~ sh$ ]]
  # echo $?
  0
  判定所有用户是否拥有可登陆shell
  # cat aa1.sh
  #!/bin/bash
  for username in `cut -d: -f1 /etc/passwd`; do
  if [[ `grep "^$username\>" /etc/passwd | cut -d: -f7` =~ sh$ ]]; then
  echo "login user: $username"
  else
  echo "nologin user: $username"
  fi
  done
  bash编程之文件测试
  单目测试
  -e file:
  -a file: 测试文件是否存在
  -f file /path/to/file:测试是否为普通文件
  -d file /path/to/somefile:测试是否为目录文件
  -b file /path/to/somefile:测试文件是否为块设备文件
  -c file /path/to/somefile:测试文件是否为字符设备文件
  -h file /path/to/somefile:测试文件是否为符号链接文件
  -p file /path/to/somefile:测试文件是否为管道文件
  -r /path/to/somefile 测试其有效用户是否对此文件有读取权限
  -w /path/to/somefile 测试其有效用户是否对此文件有写入权限
  -x /path/to/somefile 测试其有效用户是否对此文件有执行权限
  双目测试
  file1 -nt file2:测试file1是否为file2更 新一些
  # filename=/tmp/hellodir
  # [ -e $filename ] || mkdir $filename
  # ls
  让用户交互式输入一个用户名,先判定用户是否存在,不存在,则以7为退出码;
  判断用户的shell是否为/bin/bash;如果是,则显示为"bash user",退出码为0,否则显示为not bash user,退出码为1;
  # cat aa1.sh
  #!/bin/bash
  #
  read -p "Enter a user name: " username

  if !>  echo "No such user."
  exit 7
  fi
  usershell=`grep "^$username\>" /etc/passwd | cut -d: -f7`
  if [[ "$usershell" == "/bin/bash" ]]; then
  echo "bash user."
  returnValue=0
  else
  echo "Not bash usre."
  returnValue=1
  fi
  exit $returnValue
  执行脚本
  # bash aa1.sh
  Enter a user name: bin
  Not bash usre.
  # echo $?
  1
  # bash aa1.sh
  Enter a user name: binnn
  No such user.
  # echo $?
  7
  # bash aa1.sh
  Enter a user name: root
  bash user.
  编写一下脚本:
  显示如下菜单:
  cpu) show cpu info;
  mem) show memory info;
  quit) quit
  enter your option:
  如果用户选择cpu,则显示文件/proc/cpuinfo的信息
  如果用户选择mem,则显示文件/proc/meminfo的信息
  如果用户选择quit,则退出,且退出码为5
  如果用户键入其他字符,则显示未知选项,请重新执行脚本,退出码为6;
  # bash aa6.sh
  cpu) print cpu information
  mem) print memory information
  quit) Quit
  Enter your option: quit
  quit
  # cat aa6.sh
  #!/bin/bash
  #
  returnValue=0
  cat
页: [1]
查看完整版本: bash shell的基本概述与操作