1. 一个简单的例子:

$ vim readname.sh
#file:readname.sh
#!/bin/bash
echo -n "Enter your name: "
read user_name
if [ -n "$user_name" ] ; then
echo "Hello $user_name!"
exit
else
echo "You did not tell me your name..."
exit
fi

执行:

-rw-r--r--.  root root  7月   : readname.sh
root@javis:~/Documents/bash$ chmod +x readname.sh
root@javis:~/Documents/bash$ ./readname.sh
Enter your name: Julian
Hello Julian!
root@javis:~/Documents/bash$ ./readname.sh
Enter your name:
You did not tell me your name...

2. 命令行参数和函数

  给一个脚本的命令行参数可以成为变量,这些变量的名字是数字。$1 是第一个命令行的参数,$2 是第二个,以此类推。$0 是调用该脚本所采用的名字,所以它的取值并不固定。

  变量$# 是提供给脚本的命令行参数的个数,变量$*里保存有全部的参数。这两个变量都不包括或者算上 $0。

  如果调用的脚本不带参数,或者参数不正确,那么该脚本应该打印一段用法说明,提醒用户怎样使用它。下面这个脚本的例子接受两个参数,验证这两个参数都是目录,然后显示它们。如果参数无效,那么这个脚本会打印一则用法说明,并且用一个非零的返回码退出。如果调用这个脚本的程序检查该返回码,那么它就会知道这个脚本有没有正确执行。

# file-name: src_dst.sh
#!/bin/bash
function show_usage {
echo "Usage: $0 source_dir dest_dir"
exit
} # Main program starts here
if [ $# -ne ]; then
show_usage
else # There are two arguments
if [ -d $ ]; then
source_dir=$
else
echo 'Invalid source directory'
show_usage
fi
if [ -d $ ]; then
dest_dir=$
else
echo 'Invalid destination directory'
show_usage
fi
fi printf "Source directory is ${source_dir}\n"
printf "Destination directory is ${dest_dir}\n"

正确使用:

root@javis:~/Documents/bash$ ./src_dst.sh  /bin /etc
Source directory is /bin
Destination directory is /etc
root@javis:~/Documents/bash$

错误使用:

root@javis:~/Documents/bash$ ./src_dst.sh  hello hi
Invalid source directory
Usage: % source_dir dest_dir

3. 变量的作用域

  在脚本里的变量是全局变量,但是函数可以用 local 声明语句,创建自己的局部变量。考虑下面的代码:

#!/bin/bash
function localizer {
echo "==> In function localizer, a starts as '$a'"
local a
echo "==> After local declaration , a is '$a'"
a="localizer version"
echo "==> Leaving localizer, a is '$a'"
} a="test"
echo "Before calling localizer, a is '$a'"
localizer
echo "After calling localizer, a is '$a'"

运行结果:

root@javis:~/Documents/bash$ ./test.sh
Before calling localizer, a is 'test'
==> In function localizer, a starts as 'test'
==> After local declaration , a is ''
==> Leaving localizer, a is 'localizer version'
After calling localizer, a is 'test'

  以上结果显示,局部变量 $a 屏蔽了全局变量 $a。在localizer内,在碰到 local 声明了局部变量 $a 之前,全局变量 $a 都可以可见;local 实际上是一条命令,它从执行的地方开始,创建局部变量。

4.控制流程

(i)  一条 if 语句的结束标识是 fi 。要把几条 if 语句串起来,可以用 elif 这个关键字,它的意思是 "else if"。例如:

if [ $base -eq  ] && [ $dm -eq  ]; then
doSomeThingA
elif [ $base -ne ] && [ $dm -eq ]; then
doSomeThingB
else
echo '==> Doing Nothing'
fi

下表给出 bash 的数值和字符串比较运算。 bash 比较数值采用文字运算符,而比较字符串采用符号运算符,这正好和 Perl 相反。

字符串 数值 为真,如果
x=y x -eq y  x等于y
x!=y x -ne y x不等于y
x<y x -lt y x小于y
x<=y x -le y x小于等于y
x>y x -gt y x大于y
x>=y x -ge y x 大于等于y
-n x - x不为空
-z x - x为空

下表给出 bash 的文件取值运算符

运算符 为真,如果
-d file file 存在,且是目录
-e file file 存在
-f file file 存在,且是普通文件
-r file 用户有 file 的读权限
-s file file 文件存在且不为空
-w file

用户有 file 的写权限

file1 -nt file2 file1 比 file2 新
file1 -ot file2 file1 比 file2 旧

(ii)  虽然 elif 的形式能用,但是为了清除起见,用 case 语句做选择是更好的方法。 case 的语法如下面的这个函数历程所示,该函数集中给一个脚本写日志。特别值得注意的是,每一选择条件之后有一个右括号,而在条件符合时每个要执行的语句块之后有两个分号。case 语句以 esac 结尾。

# The log level is set in the global variable LOG_LEVEL. The choices
# are , from most to least severe, Error, Warning, Info , and Debug. function logMsg {
message_level=$
message_itself=$
LOG_LEVEL=
if [ $message_level -le $LOG_LEVEL ]; then
case $message_level in
) message_level_text="Error";;
) message_level_text="Warning";;
) message_level_text="Info";;
) message_level_text="Debug";;
*) message_level_text="Other";;
esac
echo "${message_level_text}: $message_itself"
fi
} logMsg testing ~

测试输出:

root@javis:~/Documents/bash$ ./test1.sh
Warning: testing

这个函数演示了许多系统管理应用经常采取的 "日志级别"方案。脚本的代码产生详尽程度不同的日志消息,但是只有那些在全局设定的阈值 $LOG_LEVEL 之内的消息才被真正记录到日志里,或者采取相应的行动。为了阐明每则消息的重要性,在消息文字之前用一个标签说明其关联的日志级别。

(iii) 循环

① bash 的 for...in 结构可以让它很容易对一组值或者文件执行若干操作,尤其是和文件名通配功能联合起来使用的时候。在下面这个 for 循环里,其中的*.sh 模式会返回当前目录下能够匹配的文件名列表。 for 语句则逐一遍历这个列表,接着把每个文件名赋值给变量 $script。

#!/bin/bash
suffix=BACKUP--`date +%Y%m%d-%H%M` for script in *.sh; do
newname="$script.$suffix"
echo "Copying $script to $newname"
cp $script $newname
done

运行测试:

root@javis:~/Documents/bash$ ./loop.sh
Copying loop.sh to loop.sh.BACKUP---
Copying readname.sh to readname.sh.BACKUP---
Copying src_dst.sh to src_dst.sh.BACKUP---
Copying test1.sh to test1.sh.BACKUP---
Copying test.sh to test.sh.BACKUP---

也可以静态的输入文件名,就像下面这样:

for script  file1.sh  file2.sh ; do

② bash 也有从传统语言来看更为熟悉的 for 循环,在这种 for 循环里,可以指定起始、增量和终止子句。例如:

for ((i=;i<$CPU_COUNT; i++)); do
CPU_LIST="$CPU_LIST $i"
done

③ 接下来的例子演示了 bash 的while 循环,这种循环也能用于处理命令行参数,以及读取一个文件里的各行:

#!/bin/bash
exec <$
counter=
while read line;do
echo "$counter: $line"
((counter++))
done

运行实例:

root@javis:~/Documents/bash$ ./whileloop.sh poem.txt
: good , better , best
: never let it rest
: till good is better
: and better is best

  上面这段脚本有两个有趣的功能。exec语句重新定义了该脚本的标准输入,变成由第一个命令行参数指定的任何文件。这个文件必须要有,否则脚本会出错。

  在 while 子句里的 read 语句实际上是 shell 的内置命令,但它的作用就和一条外部命令一样。外部命令也可以放在 while 子句里;在这种情况下,当外部命令返回一个非零退出状态时,它会结束 while 循环。

  表达式 $((counter++)) 这样的 $((...)) 写法要求强制进行数值计算。它还可以利用$来标记变量名。++是人们在C 和其他语言中熟悉的后置递增运算符。它返回它前面的那个变量的值,但返回之后还要把这个变量的值再加1.

  $((...))的技巧在双引号里也起作用,所以可以把整个循环体紧凑地写到一行里。

while read line; do
echo "$((counter++)): $line"
done

UNIX/Linux系统管理技术手册(2)----bash脚本编程的更多相关文章

  1. UNIX/Linux系统管理技术手册(3)----bash 数组和算术运算

    复杂的数据结构和计算不是 bash 的特长.但它的确至少提供了数组和算术运算. 1.算术运算 所有的 bash 变量的值都是字符串,所以 bash 在赋值的时候并不区分数字 1 和 字符串 " ...

  2. UNIX/Linux系统管理技术手册(1)----脚本和shell

    1. 管道和重定向 (i) 要让第二条命令只有在第一条命令成功完成之后才执行,可以用一个 && 符号把两条命令隔开.例如: $ > /dev/null && cd ...

  3. Unix/Linux系统管理技术手册学习笔记——shell

    创建日期:2016/02/29 更新日期:2016/02/29 shell变量赋值时不能在等号两边留空白,否则shell会把变量名误认为是命令名 双引号括起来的变量可以进行替换(用*和?这样的文件名匹 ...

  4. Linux系统管理技术手册——第6章 添加新用户

    6.1/etc/passwd文件 用户登录时Linux识别用户的文件/etc/passwd /etc/passwd包括7个字段: 登录名(不超过32位,使用NIS系统后不超过8位) 经过加密的口令或口 ...

  5. Linux应用环境实战10:Bash脚本编程语言中的美学与哲学(转)

    阅读目录 一.一切皆是字符串 二.引用和元字符 三.字符串从哪里来.到哪里去 四.再加上一点点的定义,就可以推导出整个Bash脚本语言的语法了 五.输入输出重定向 六.Bash脚本语言的美学:大道至简 ...

  6. 高级Bash脚本编程指南(27):文本处理命令(三)

    高级Bash脚本编程指南(27):文本处理命令(三) 成于坚持,败于止步 处理文本和文本文件的命令 tr 字符转换过滤器. 必须使用引用或中括号, 这样做才是合理的. 引用可以阻止shell重新解释出 ...

  7. Bash脚本编程学习笔记08:函数

    官方资料:Shell Functions (Bash Reference Manual) 简介 正如我们在<Bash脚本编程学习笔记06:条件结构体>中最后所说的,我们应该把一些可能反复执 ...

  8. Bash脚本编程学习笔记04:测试命令test、状态返回值、位置参数和特殊变量

    我自己接触Linux主要是大学学习的Turbolinux --> 根据<鸟哥的Linux私房菜:基础篇>(第三版) --> 马哥的就业班课程.给我的感觉是这些课程对于bash的 ...

  9. 脚本命令高级Bash脚本编程指南(31):数学计算命令

    题记:写这篇博客要主是加深自己对脚本命令的认识和总结实现算法时的一些验经和训教,如果有错误请指出,万分感谢. 高等Bash脚本编程指南(31):数学盘算命令 成于坚持,败于止步 操作数字 factor ...

随机推荐

  1. NASA的10条代码编写原则

    NASA的10条代码编写原则 作者: Gerard J. Holzmann 来源: InfoQ 原文链接 英文原文:NASA's 10 Coding Rules for Writing Safety ...

  2. dp--hdu1171(01背包)

    hdu1171 题目 Problem Description Nowadays, we all know that Computer College is the biggest department ...

  3. vue 之bug<1> Warn : [vue-router] Duplicate named routes definition:

    原因:定义重复的路由名称. 我有3个不同的(父级)vue文件分别配置了3个相同的(子级)vue文件,配置路由的js文件里子集路由的name重复了. 解决方案: 一天一个小Bug

  4. Linux - 组管理和权限管理

    l Linux组基本介绍 在linux中的每个用户必须属于一个组,不能独立于组外.在linux中每个文件有所有者.所在组.其它组的概念. 1) 所有者 2) 所在组 3) 其它组 4) 改变用户所在的 ...

  5. Cloudera Manager安装之Cloudera Manager安装前准备(Ubuntu14.04)(一)

    其实,基本思路跟如下差不多,我就不多详细说了,贴出主要图. 博主,我是直接借鉴下面这位博主,来进行安装的!(灰常感谢他们!) 在线和离线安装Cloudera CDH 5.6.0  Cloudera M ...

  6. C 六度空间理论的实现

    “六度空间”理论又称作“六度分隔(Six Degrees of Separation)”理论.这个理论可以通俗地阐述为:“你和任何一个陌生人之间所间隔的人不会超过六个,也就是说,最多通过五个人你就能够 ...

  7. 如何使标签a处于不可用状态

    今天做项目的时候突然发现a标签下用disabled无法使它的点击事件失效(貌似ie下可以,没有测试过), 首先说一下项目要求,点击a标签(点击之后以防多次快速点击,这里需要点击后使标签a实现),触发a ...

  8. 入门系列之在Ubuntu上使用Netdata设置实时性能监控

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由小翼 发表于云+社区专栏 介绍 Netdata通过可扩展的Web仪表板提供准确的性能监控,可以显示Linux系统上的流程和服务.它监控 ...

  9. 远程连接postgresql和redis设置

    1. 让Postgresql服务器被远程访问 1.1 编辑 pg_hba.conf,配置用户的访问权限 vi /etc/postgresql/8.4/main/pg_hba.conf 增加设置项 ho ...

  10. Windows加密API的层次

    Windows平台下的应用程序可以分为托管的.NET程序和本机的Win32(以及Win64)两大类..NET有着类似于JAVA的虚拟机和二进制码托管运行环境,提供了在不同Windows平台上的代码可携 ...