提示:
本文内容 引用 《Shell 脚本学习指南》大家有兴趣可以入手
部分使用菜鸟教程的知识点作为补充
还有一些自己的理解,以及众多博客文
对于Shell 脚本,我自己并没有系列的学习过,大都是在工作中,现学现卖。
基本上都要实现某一需求,去找相关的资料,再去整合,编写和测试。
对于服务器来说,如果掌握了Shell 其实能更好的提高效率,有些需求,比如在服务器上对文件的操作,对应用程序的自动备份等等,使用Shell 脚本,效率就很高。
因此,结合自己工作经验和Shell 脚本学习指南,就自己的Shell 知识进行归纳整理
当然因为是我自己整理的笔记,只根据自己的理解和需要 做的,有些没摘抄或者不全的,大家自己查查
提示:以下是本篇文章正文内容,下面案例可供参考
Shell 与awk 、Perl 、Python、Ruby 都是脚本编程语言。他们的优点就是,多半运行在比编译型语言还高的层级,能够轻易处理文件和目录之类的对象。缺点:效率通常不如编译型语言。
之所以使用Shell 脚本 是基于:
简单性:Shell 是高级语言;通过它,可以简洁表达复杂的操作。
可移植性:使用POSIX的标准所定义的功能,可以做到脚本无须修改就可在不同的系统上执行。
开发容易:可以在短时间内完成一个功能强大又好用的脚本。
当一个文件中开头的两个字符是 #!时,内核会扫描该行其余部分,看是否参在可用来执行程序的结束器的完整路径。(中间如果出现任何空白符号都会略过。)此外,内核还会扫描是否有一个选项要传递给解释器。内核会以指定的选项来引用解释器,再搭配命令的其他部分。
假设有个 csh 脚本,名为/usr/ucb/whizprog ,它的第一行所示:
#! /bin/csh -f
再者,如果Shell 的查找路径里有 /usr/ucb ,当用户输入 whizprog -q /dev/tty01 这条命令,内核解释 #! 这行后,便会以如下的方式来引用 csh :
/bin/csh -f /usr/ucb/whizprog -q /dev/tty01
这样的机制让我们得以轻松地引用任何解释器。例如我们可以这样引用独立地 awk 程序
#! /bin/awk -f
简单理解就是 #! 开头,系统会直接去找合适的解释器
Shell 脚本通常一开始都是 #! /bin/sh 开头.如果你的 /bin/sh 并不符合POSIX标准,请将这个路径改为符合POSIX标准的Shell。下面是几个初级的陷阱,请特别留意:
Shell 识别三种基本命令:内建命令,Shell函数以及外部命令
命令的原理
1.格式简单 以空白(Space 键或Tab 键)隔开命令行中各个组成部分
2.命令名称是命令行第一个项目,后面通常跟着选项,任何额外的参数都会放在选项之后
3.选项的开头是一个破折号(或减号),后面接着一个字母
变量就是为某个信息片段所起的名字
变量名称开头是一个字母或者下划线符号,后面接任意长度的字母、数字、或下划线。长度无限制
赋值方式:变量名称=字符,中间完全没有任何空白。
当你想取出Shell 变量值时:$变量名
当所赋予的值内含空格时,请加上引号
first=isaac midedle=bash last=singer //单行可进行多次赋值
fullname1="isaac bash singer" //值中包含空格时使用引号
oldname=$fullname1 //此处不需要引号
fullname2="$first $middle $last" //这里需要双引号
没什么可说的,输出语句
echo 语句
echo加空格 加输出内容
$ echo 输出的测试语句
输出的测试语句
printf 语句
$ printf "输出的测试语句"
输出的测试语句
以 < 改变标准输入
program < file 可将 program 的标准输入修改为 file
tr -d '\r' < dos-file.txt ...
以 > 改变标准输出
program > file 可将program 的标准输出修改为file
tr -d '\r' < dos-file.txt > UNIX-file.txt
//这条命令会以tr 将 dos-file.txt 里的 ASCII 回车删除,再将转换完成的数据输出到 UNIX-file.txt。dos-file.txt里的原始数据不会有变化。
“>” 重定向符 在目的文件不存在时,会新建一个。然而,如果目的文件已存在,它就会被覆盖;原本的数据会丢失。
以 >> 附加到 文件
program >> file 可将program 的标准输出附加到file 的结尾处
for f in dos-file*.txt
do
tr -d '\r' < dos-file.txt >> UNIX-file.txt
done
如">" 在目的文件不存在时,会新建一个。然而,如果目的文件已存在,它就不会被覆盖,而是将数据所产生的数据附加到文件结尾处。
以 | 建立管道
program1 | program2 可将program1 的标准输出修改为program2 的标准输入
tr -d '\r' < dos-file.txt | sort > UNIX-file.txt
//先删除输入文件内的回车字符,在完成数据的排序后,将结果输出到目的文件
管道可以把两个以上执行中的程序衔接在一起。第一个程序的标准输出修改为第二个程序的标准输入,管道的效率比使用临时文件的程序快十倍。
tr 命令
tr [option] source-char-list replace-char-list
用途:转换字符。 eg:将 大写字符 转换成小写。选项可以让你指定所要删除的字符,以及将一串重复出现的字符浓缩成一个。
选项 | 作用 |
---|---|
-c | 取source-char-list 的反义。tr 要转换的字符,变成未列在source-char-list 中的字符。 |
-C | 与-c 相似,但所处理的字符(可能时包含多个字节的宽字符),而非二进制的字节值。 |
-d | 自标准输入删除source-char-list 中的字符,而不是转换它们。 |
-s | 浓缩重复的字符。如果标准输入中连续重复出现在source-char-list 中的字符,则将其浓缩成一个。 |
第一个文件 /dev/null : 位桶 传送到此文件的数据都会被系统丢掉。
我一直接理解成 垃圾桶,不能回收的回收站
第二个文件 /dev/tty :/dev/tty如果一个控制台有一个终端的话,那么这个文件就是对应的当前的这个控制终端的别名。当程序打开此文件是,Linux会自动将它重定向到一个终端窗口[一个实体的控制台(console)或者串行端口(serial port),也可能是一个通过网络与窗口登陆的为终端(pseudterminal)],因此该文件对于读取人工输入时(例如密码)特别有用。
一个校验密码的例子eg:
#! /bin/sh
printf "Enter your password: "
stty -echo
read passwd1 < /dev/tty
printf "\nEnter your password again: "
read passwd2 < /dev/tty
stty echo
if [ "$passwd1" = "$passwd2" ]
then
passwd=$passwd1
printf "\nPassword set successfully!\n"
else
printf "\nThe two passwords you typed do not match!\n"
fi
stty(set tty) 命令是用来控制终端的,stty -echo表示关闭自动打印每个输入字符的功能,stty echo表示恢复自动打印每个输入字符的功能。运行上面的脚本,在终端上并不会显示输入的密码,保证了密码的安全性。
Shell 会沿着查找路径 $ PATH 来寻找命令。$ PATH 是一个以冒号分割的目录列表,可以在列表所指定的目录下找到所要执行的命令。
默认路径因系统而异,不过至少包括/bin 与 /usr/bin ,以及供本地系统安装人员安装程序的 /usr/local/bin
$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:
//bin 的目录用来保存可执行文件
如果你要编写自己的脚本,最好准备自己的bin 来存放它们,并且让Shell 能够自动找到它们。这不难,只要建立自己的bin 目录,并将它加入 $ PATH中的列表即可。
(这个在实际操作中挺有用的)
$ cd 自己的目录下(/home 或者 自己的用户目录下 /home/username)
$ mkdir bin //建立个人bin 目录
$ mv testsh bin //将我们的脚本放入该目录
$ PATH=$PATH:$HOME/bin //将个人的bin 目录附加到PATH
$ testsh //执行
要让修改永久生效,在 .profile 文件中把你的bin 目录加入 $PATH ,每次登录时Shell 都将读取 .profile 文件
补充知识,$PATH 里的空项目表示当前目录。
空项目于路径值中间时,可以用两个连续的冒号来表示。如果将冒号直接置于最前端或尾端,可以分别表示查找时最先查找或最后查找当前目录:
PATH=:/bin:/usr/bin:/usr/local/bin 先找当前目录
PATH=/bin:/usr/bin:/usr/local/bin: 最后找当前目录
PATH=/bin:/usr/bin::/usr/local/bin 当前目录居中
如果你希望将当前目录纳入查找路径,更好的做法是使用点号;测试中,发现空项目 在同一系统不同的版本中并未正确支持,因此空项目在可移植性上有问题。空项目存在安全问题,不建议使用!!!
位置参数指的也就是Shell 脚本的命令行参数。在Shell函数里,它们也可以是函数的参数。各参数都由整数来命名。基于历史原因,当它超过9时,就应该用大括号把数字框起来:
详情可见后面章节
可以在脚本里,用set -x 命令将执行跟踪的功能打开,然后再用set +x 命令关闭它
$ sh -x testEcho.sh //开启执行追踪
$ vi testEcho2.sh //新建testEcho2.sh 脚本 进行开追踪和关追踪
#! /bin/sh
set -x
echo lst echo
set +x
echo 2nd echo
$ ./testEcho2.sh //直接执行就可以看见追踪信息了
以grep 程序查找文本
grep 匹配固定的字符串
grep hello testEcho.sh //查找testEcho.sh 里的hello
grep 的语法: grep [option …] pattern-spec [files …]
显示匹配一个或多个模式的文本行
选项 | 作用 |
---|---|
-E | 可取代 egrep |
-F | 使用固定字符串进行匹配。grep -F 可取代 fgrep |
-e pat-list | 通常,第一个非选项的参数会指定要匹配的模式。 |
-f pat-file | 从 pat-file 文件读取模式匹配 |
-i | 模式匹配时忽略字母大小写差异 |
-l | 列出匹配模式地文件名称 |
-q | 静默的。如果模式匹配匹配,则grep 会成功地离开,而不将匹配地行写入标准输出;否则即是不成功 |
-s | 不显示错误信息。通常与-q 并用 |
-v | 显示不匹配模式的行 |
正则表达式时由两个基本组成部分所建立:一般字符和特殊字符。一般字符指的是任何没有特殊意义的字符,某些情况下,特殊字符也可以视为一般字符。特殊字符常称为元字符,本章接下来都会以meta字符表示。
POSIX的全称是Portable Operating System Interface for uniX,它由一系列规范构成,定义了UNIX操作系统应当支持的功能,所以“POSIX规范的正则表达式”其实只是“关于正则表达式的POSIX规范”,它定义了BRE(Basic Regular Expression,基本型正则表达式)和ERE(Extended Regular Express,扩展型正则表达式)两大流派。在兼容POSIX的UNIX系统上,grep和egrep之类的工具都遵循POSIX规范,一些数据库系统中的正则表达式也符合POSIX规范。
下为POSIX BRE与ERE的meta字符
字符 | BRE/ERE | 模式含义 |
---|---|---|
\ | 两者都可 | 通常用以关闭后续字符的特殊意义。有时则是相反地打开后续字符地特殊意义 |
. | 两者都可 | 匹配任何单个地字符,但NULL 除外 |
* | 两者都可 | 匹配在它之前地任何数目(或没有)地单个字符 |
^ | 两者都可 | 匹配紧接着的正则表达式,在行或字符串的起始处。BRE:仅在正则表达式的开头处具有此特殊含义,ERE::置于任何位置都具有特殊含义 |
$ | 两者都可 | 匹配前面的正则表达式,在字符串或行结尾处。BRE:仅在正则表达式的结尾处具有此特殊含义,ERE::置于任何位置都具有特殊含义 |
[…] | 两者都可 | 方括号表达式,匹配方括号内的任一字符。 |
\{n,m \} | BRE | 区间表达式,匹配在它前面的单个字符重现的次数区间。重现n至m次,m最小值为255 |
\( \) | BRE | 将\( 与 \)间的模式存储在特使的保留空间。最多可以将9个独立的子模式存储在单个模式中。匹配于子模式的文本,可通过转义序列\1 至\9,被重复使用在相同模式里。例如\(ab\).*\1,指的是匹配于ab组合的两次重现,中间可存在任何数目的字符 |
\n | BRE | 重复在 \( 与 \)方括号内第n 个子模式至此点的模式。n 为1 至9的数字,1为由左开始。 |
{n,m} | ERE | 与前面的 BRE 的\{n,m \} 一样,只不过没有反斜杠 |
+ | ERE | 匹配前面正则表达式的一个或多个实例 |
? | ERE | 匹配前面正则表达式的零个或一个实例 |
| | ERE | 匹配于 符号前或后的正则表达式 |
( ) | ERE | 匹配于方括号括起来的正则表达式群 |
简单的正则表达式范例
表达式 | 匹配 |
---|---|
tolstoy | 位于一行上任何位置的7个字母 :tolstoy |
^ tolstoy | 7个字母 tolstoy ,出现在一行的开头 |
tolstoy$ | 7个字母 tolstoy ,出现在一行的结尾 |
^tolstoy$ | 正好包括7个字母 tolstoy,没有其他的任何字符 |
[Tt]olstoy | 在一行上的任意位居中,含Tolstory 或是 tolstoy |
tol.toy | 在一行上的任意位居中,含tol这3个字母,加上任何一个字符,再接着toy 这3个字母 |
tol.*toy | 在一行上的任意位居中,含tol这3个字母,加上任何任意的0或多个字符,再接着toy 这3个字母,例如: toltoy、tolstoy、tolWHOtoy等 |
POSIX 标准强化其字符集范围的能力,以匹配英文字母字符。
字符集、排序符号、等价字符集
类别 | 匹配字符 |
---|---|
[:alnum:] | 数字字符 |
[:alpha:] | 字母字符 |
[:blank:] | 空格(space)与定位(Tab)字符 |
[:cntrl:] | 控制字符 |
[:digit:] | 数字字符 |
[:graph:] | 非空格字符 |
[:lower:] | 小写字母字符 |
[:print:] | 可显示的字符 |
[:print:] | 可显示的字符 |
[:punct:] | 标点符号字符 |
[:space:] | 空白字符 |
[:upper:] | 大写字母字符 |
[:xdigit:] | 十六进制数字 |
额外的GNU正则表达式运算符
运算符 | 含义 |
---|---|
\w | 匹配任何单词组成字符 ,等同于 [[:alnum:]] |
\W | 匹配任何非单词组成字符,等同于 [^[:alnum:]] |
\<\> | 匹配单词的起始 与 结尾 |
\B | 匹配两个单词组成字符之间的空字符串 |
\b | 匹配单词的起始或结尾所找到的空字符串。 |
\’ \` | 分别匹配emacs缓冲区的开始与结尾。GNU通常视为与 ^ 及 $ 同义 |
export,readonly
语法 :
export name[=word]…
export -p
readonly name[=word] …
readonly -p
用途 :
export 用于修改或打印环境变量,readonly则使得变量不得修改
主要选项:
-p 打印命令的名称以及所有被导出(只读)变量的名称与值,这种方式可使得Shell重新读取输出以便重新建立环境(只读设置)
export 经常使用,可以着重看下
export: 用法是将变量放进环境里。环境是一个名称与值的简单列表,可供所有执行中的程序使用。
PATH=$PATH:/usr/local/bin 更新PATH
export PATH 导出它
export -p 显示当前的环境
export 的命令 在Linux 的时候中,第一次使用,就是配置JDK 的环境变量
export 相当于导出变量。" = "赋值符号定义的变量只在当前shell中可用,外部(子shell中)使用时,定义时需要用export A=“xxx”,或在使用时 使用export A。 如果希望下载软件后不加入路径就能启动该程序,要把可执行程序的路径加入 PATH 中
vi /etc/profile 打开配置 /etc/profile 文件中设置的变量是全局变量
其中加入
export JAVA_HOME=/usr/java-1.6.xxx //java的目录
export PATH=$JAVA_HOME/bin:$PATH //将java加入PATH中
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar //更新CLASSPATH
最后sourse 下
source /etc/profile //使环境变量生效
第一组字符串处理运算符用来测试变量的存在状态,且为在某种情况下允许某种情况下允许默认值的替换。如下所示
替换运算符
运算符 | 替换 |
---|---|
${varname:-word} | 如果varname 存在且非null 则返回其值;否则,返回 word。 用途:如果变量未定义,则返回默认值 范例:如果count 未定义,则${count:-0}的值为0,count依旧不存在 |
${varname:=word} | 如果varname 存在且非null 则返回其值;否则,设置它为word 用途:如果变量未定义,则设置变量为默认值 范例:如果count 未定义,则${count:-0}设置count为0 ,就可以使用count |
${varname:?message} | 如果varname 存在且非null 则返回其值;否则,显示 varname:message,并退出当前命令或脚本。省略message会出现默认信息 parameter null or not set. 用途:为了捕获由于变量未定义所导致的错误。 范例:${count:?“undefined!”}将显示 count:undefined!,且如果count未定义,则退出。 |
${varname:+word} | 如果varname 存在且非null 则返回word;否则,返回null。 用途:为测试变量的存在 范例:如果count 已定义,则${count:+1}返回1 count赋值为0,返回也是1 |
模式匹配运算符
假设 path 的值为 /home/tolstoy/mem/long.file.name
运算符 | 替换 |
---|---|
${variable#pattern} | 如果模式匹配变量值的开头处,则删除匹配的最短部分,并返回剩下的部分 |
例:${path#/*/} | 结果:tolstoy/mem/long.file.name |
${variable##pattern} | 如果模式匹配变量值的开头处,则删除匹配的最长部分,并返回剩下的部分 |
例:${path##/*/} | 结果:long.file.name |
${variable%pattern} | 如果模式匹配变量值的结尾处,则删除匹配的最短部分,并返回剩下的部分 |
例:${path%.*} | 结果:/home/tolstoy/mem/long.file |
${variable%%pattern} | 如果模式匹配变量值的结尾处,则删除匹配的最长部分,并返回剩下的部分 |
例:${path%%.*} | 结果:/home/tolstoy/mem/long |
#位置在键盘靠左,%位置在键盘靠右,所以#匹配前面,%匹配后面,这样更好记忆
所谓位置参数,指的是Shell 脚本的命令行参数;同时也表示在Shell
函数内的函数参数。他们的名称以单个的整数来命名。当这个整数大于9时,就可以以花括号{} 括起来:
echo first arg is $1
echo tenth arg is ${10}
下面介绍特殊“变量”
$# # 提供传递到Shell脚本或函数的参数总数
示例:
while [ $# != 0 ] #以shift 逐渐减少$#,循环将会终止
do
case $1 in
.... #处理第一个参数
esac
shift #移开第一个参数
done
$*,$@ #一次表示所有的命令行参数。这两个参数可用来把命令行参数传递给脚本或函数所执行的程序
"$*" #将所有命令行参数视为单个字符串。等同于“$1 $2 ...”.
"$@" #将所有命令行参数视为单独的个体,也就是单独字符串。 等同于"$1" "$2" ....。
#这是将参数传递给其他程序的最佳方式,因为它会保留所有内嵌在每个参数的任何空白。
set #命令可以做的事很多。调用此命令而未给予任何选项,则它会设置位置参数的值,并将之前存在的任何值丢弃:
例子:
set -- hi there how do you do # -- 会结束选项部分,自hi 开始新的参数
shift #命令是用来“截去”来自列表的位置参数,由左开始。一旦执行shift,$1的初始值会永远消失,取而代之的是$2的旧值。$2的值,变成$3的旧值,以此类推。$#则会逐次减1。shift也可使用一个可选的参数,也就是要位移的参数的计数。
#单纯的shift 等同于 shift 1。
以下是综合范例
$ set -- hello "hi there" greetings #设置新的位置参数
$ echo there are $# total arguments #显示计数值
there are 3 total arguments
$ for i in $* #循环处理每一个参数
> do echo i is $i
> done
i is hello
i is hi
i is there
i is greetings
$ for i in $@ #在没有双引号的情况下,$* 与 $@ 是一样的
> do echo i is $i
> done
i is hello
i is hi
i is there
i is greetings
$ for i in "$*" #加双引号的情况下,$* 表示一个字符串
> do echo i is $i
> done
i is hello hi there greetings
$ for i in "$@" #加双引号的情况下 $@ 保留真正的参数值
> do echo i is $i
> done
i is hello
i is hi there
i is greetings
$ shift #截去第一个参数
$ echo there are now $# arguments #证明它已消失
there are now 2 arguments
$ for i in "$@"
> do echo i is $i
> done
i is hi there
i is greetings
以惯例来说,退出状态为0表示“成功”。内置变量 ? (以 $? 访问它) 包括了Shell 最近一次所执行的一个程序的退出状态
$ ls -l /dev/null #ls一个存在的文件
crw-rw-rw- 1 root root 1,3 Aug 30 2020 /dev/null #ls的输出
$ echo $? #显示退出状态
0
$ ls test #ls一个不存在的文件
ls报错......
$ echo $? #显示退出状态
1
结束状态的值 | 意义 |
---|---|
0 | 命令成功地退出 |
>0 | 在重定向或单次展开期间失败 |
1-125 | 命令不成功地退出。特定的退出值得含义,是由各个单独得命令定义的 |
126 | 命令找到了,但文件无法执行 |
127 | 命令找不到 |
>128 | 命令因收到信号而死亡 |
Shell 脚本可以使用exit 命令传递一个退出值给它的调用者。
只要将一个数字传递给它,作为第一个参数即可。脚本会立即退出,并且调用者会收到该数字且作为脚本的退出值:
exit 42
此命令还是很常见和使用的,往往使用在异常报错的退出或者传递成功的标志
Shell 脚本中也非常常见的条件判断语句
使用程序的退出状态,最简单的方式就是使用if 语句。一般语法如下:
if pipeline
[ pipeline ... ]
then
statements-if-true-1
[ elif pipeline
[ pipeline ... ]
then
statements-if-true-2
][else
statements-if-all-else-fails]
fi
方括号表示的是可选的部分
Shell执行第一组介于if 与 then 之间的语句块。如果最后一条执行的语句成功退出,它便执行statements-if-true-1,否则,如果elif ,它会尝试下一组语句块。如果最后一条语句成功地退出,则会执行statements-if-true-2.它会以这种方式继续,执行相对应地语句块,直到它碰到一个成功退出地命令为止。
如果if 或 elif 语句里没有一个为真,并且else 子句存在,它会执行statements-if-all-else-fails.否则,它什么事也不做。整个if …fi 语句地退出状态,就是在then 或else 后面地最后一个被执行命令地退出状态。如果无任何命令执行,则退出状态为0.
说这么多废话,就是和其他语言的 if --else if --else 类似,其中fi (是if 反过来)作为结束标志
if [ $a -eq $b ]
then
echo "a 等于 b"
exit 0
elif [ $a -gt $b ]
then
echo "a 大于 b"
else
echo "a 小于 b"
echo "两个数对比成功"
#如果 a=10,b=20 则输出 a小于b 两个数对比成功
#如果 a=10,b=10 则输出 a 等于 b (exit 直接终止程序了,后面不在输出)
#如果 a=20.b=10 则输出 a大于b 两个数对比成功
补充关系运算符
运算符 | 说明 |
---|---|
-eq | 检测两个数是否相等,相等返回true |
-ne | 检测两个数是否不相等,不相等返回true |
-gt | 检测左边数是否大于右边,是返回true |
-lt | 检测左边数是否小于右边,是返回true |
-ge | 检测左边数是否大于等于右边,是返回true |
-le | 检测左边数是否小于等于右边,是返回true |
test 命令 用途:为了测试Shell 脚本里的条件,通过退出状态返回其结果。要特别注意的是:这个命令的第二种形式,方括号根据字面意义逐字地输入,且必须与括起来的expression 以空白隔开
###test 语法
## test [ expression ] 或 [ [expression ] ]
#第一种
if test "$str1" = "str2"
then
...
fi
#第二种
if [ "$str1" = "$str2" ]
then
...
fi
这个命令 经常犯的错 就在 空格,有时忘记打了,导致判断语句不生效,最后白白浪费时间去排查
test 表达式
这里罗列写常用的判断运算符,6.2.2章节中的补充运算符也是
运算符 | 如果…则为真 |
---|---|
= | 等于则为真 *常用,这里和java 双等号 不一样,注意点 |
!= | 不相等则为真 *常用 |
-z 字符串 | 字符串的长度为零则为真 |
-n 字符串 | 字符串的长度不为零则为真 |
-e 文件名 | 如果文件存在则为真 *常用 |
-r 文件名 | 如果文件存在且可读则为真 |
-w 文件名 | 如果文件存在且可写则为真 |
-x 文件名 | 如果文件存在且可执行则为真 |
-s 文件名 | 如果文件存在且至少有一个字符则为真 |
-d 文件名 | 如果文件存在且为目录则为真 |
-f 文件名 | 如果文件存在且为普通文件则为真 *常用 |
-c 文件名 | 如果文件存在且为字符型特殊文件则为真 |
-b 文件名 | 如果文件存在且为块特殊文件则为真 |
#也可以测试否定的结果,只需前置!字符即可。
if[ -f "$file" ]
then
echo $file is a regular file
elif [ -d "$file" ]
then
echo $file is a directory
fi
if [ ! -x "$file" ]
then
echo $file is NOT executable
fi
test 的注意点
1.需要参数:
所有Shell 变量展开都应该以引号括起来
if[ -f "$file" ],不加引号的话,万一$file是空的,就会报错了
2.字符串 特殊处理
字符串值为空 或是开头带有一个减号时,test会被混淆。因此会有种写法:
在字符串值前置字母X(X的使用时随意的,但这是传统用法)
if [ "X$answer" = "Xyes" ]
说实话,本人在工作中,遇到过此类情况,看到别人这么写,当时是满头问号
3.test 是可以被愚弄的
用起来难度较高,这里不多说
4.只能作整数数字测试
这个不多说,别判断浮点数,别用
不多说Shell 的case …esac 为多选择语句,与其他语言中的swich…case类似
每个case 分支用右圆括号开始,用两个分号;;表示 break,即执行结束,跳出整个case…esac语句,esac(就是case反过来)作为结束标志。
case 值 in
模式1) ##第一种情况
command1
command2
...
;;
模式2) ##第二种情况
command1
command2
...
;;
*) ##如果无匹配,则是其他,相当于defalt
command1
;;
esac
#示例1:
case $1 in
-f)
.... #针对-f选项的程序代码
;;
-d | --directory) #允许长选项
... #针对-d选项的程序代码
;;
*)
echo $1:unknown option >&2 #(>&2,是传送输出到标准错误)
exit 1
;;
esac
#示例2:
echo '输入1到4之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
1) echo '你选择了1'
;;
2) echo '你选择了2'
;;
3) echo '你选择了3'
;;
4) echo '你选择了4'
;;
*) echo '你没有选择输入1到4之间的数字'
;;
esac
for 循环里的in 列表是可选的,如果省略,Shell 循环会遍历整个命令行参数
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
#示例1:
for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done
#输出
The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5
#示例2:
for str in This is a string
do
echo $str
done
#输出
This
is
a
string
与程序语言类似
while 和 until 唯一的不同之处在于,如何退出condition 的退出状态。只要condition 是成功退出,while 会继续循环。只要condition 未成功结束,until 则执行循环。
until 用的少
语法如下
while conditon
do
statements
done
until conditon
do
statements
done
#示例:
int=1
while(($int<=5 ))
do
echo $int
let "int++"
done
#let 命令是 BASH 中用于计算的工具,用于执行一个或多个表达式,
#变量计算中不需要加上$ 来表示变量。
#如果表达式中包含了空格或其他特殊字符,则必须引起来
#输出:
1
2
3
4
5
#无限循环
while :
do
command
done
#或者
while true
do
command
done
#或者
for (( ; ;))
break 与 continue 都接受可选的数值参数,可分别用来指出要中断或继续
while con1 #外部循环
do
while con2 #内部循环
do
...
break 2 #外部循环的中断
done
done
... #在中断之后,继续执行这里的程序
while :
do
echo -n "输入 1 到 5 之间的数字: "
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的!"
continue
echo "游戏结束"
;;
esac
done
#当输入大于5的数字时,该例中的循环不会结束,语句 echo "游戏结束" 永远不会被执行。
在执行shift 之后,原来的$1 就会消失,以$2 的旧值取代,$2的新值即为 $ 3的旧值,以此类推,而$ #的值就会逐次减少
位置参数可以用shift命令左移。比如shift 3表示原来的$4现在变成$1,原来的$5现在变成$2等等,原来的$1、$2、$3丢弃,$0不移动。不带参数的shift命令相当于shift 1。
[ function ] funname [()]
{
action;
[return int;]
}
#示例1
demoFun(){
echo "这是我的第一个 shell 函数!"
}
echo "-----函数开始执行-----"
demoFun
echo "-----函数执行完毕-----"
#输出:
-----函数开始执行-----
这是我的第一个 shell 函数!
-----函数执行完毕-----
#示例2
funWithReturn(){
echo "这个函数会对输入的两个数字进行相加运算..."
echo "输入第一个数字: "
read aNum
echo "输入第二个数字: "
read anotherNum
echo "两个数字分别为 $aNum 和 $anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"
#输出:
这个函数会对输入的两个数字进行相加运算...
输入第一个数字:
1
输入第二个数字:
2
两个数字分别为 1 和 2 !
输入的两个数字之和为 3 !
#在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...
#带参数的函数示例3:
funWithParam(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "第十个参数为 $10 !"
echo "第十个参数为 ${10} !"
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
#输出:
第一个参数为 1 !
第二个参数为 2 !
第十个参数为 10 !
第十个参数为 34 !
第十一个参数为 73 !
参数总数有 11 个!
作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !
1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255
3、注意 在Shell 函数体里使用exit 会终止整个Shell 函数
4、注意, 10 不 能 获 取 第 十 个 参 数 , 获 取 第 十 个 参 数 需 要 10 不能获取第十个参数,获取第十个参数需要 10不能获取第十个参数,获取第十个参数需要{10}。当n>=10时,需要使用${n}来获取参数。
参数处理 | 说明 |
---|---|
$# | 传递到脚本或函数的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 |
$- | 显示Shell使用的当前选项,与set命令功能相同。 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
标准输入---->数据来源
标准输出---->数据出口
标准错误输出---->报告问题的地方
命令 | 说明 |
---|---|
command > file | 将输出重定向到 file。 |
command < file | 将输入重定向到 file。 |
command >> file | 将输出以追加的方式重定向到 file。 |
n > file | 将文件描述符为 n 的文件重定向到 file。 |
n >> file | 将文件描述符为 n 的文件以追加的方式重定向到 file。 |
n >& m | 将输出重定向到 file。 |
n <& m | 将输入文件 m 和 n 合并。 |
<< tag | 将开始标记 tag 和结束标记 tag 之间的内容作为输入。 |
需要注意:
文件描述符
输出重定向
command1 > file1
上面这个命令执行command1然后将输出的内容存入file1。
注意任何file1内的已经存在的内容将被新内容替代。如果要将新内容添加在文件末尾,请使用>>操作符。
#执行下面的 who 命令,它将命令的完整的输出重定向在用户文件中(users):
$ who > users
#执行后,并没有在终端输出信息,这是因为输出已被从默认的标准输出设备(终端)重定向到指定的文件。
#你可以使用 cat 命令查看文件内容:
$ cat users
#输出:
_mbsetupuser console Oct 31 17:35
tianqixin console Oct 31 17:35
tianqixin ttys000 Dec 1 11:33
#输出重定向会覆盖文件内容,请看下面的例子:
$ echo "菜鸟教程:www.runoob.com" > users
$ cat users
#输出:
菜鸟教程:www.runoob.com
#如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾,例如:
$ echo "菜鸟教程:www.runoob.com" >> users
$ cat users
#输出:
菜鸟教程:www.runoob.com
菜鸟教程:www.runoob.com
输入出重定向
#本来需要从键盘获取输入的命令会转移到文件读取内容
command1 < file1
示例:我们需要统计 users 文件的行数,执行以下命令
$ wc -l users
2 users
$ wc -l < users
2
#注意:上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。
command1 < infile > outfile
#同时替换输入和输出,执行command1,从文件infile读取内容,然后将输出写入到outfile中。
重定向深入讲解
标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。
默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。
#如果希望 stderr 重定向到 file,可以这样写:
$ command 2>file
#如果希望 stderr 追加到 file 文件末尾,可以这样写:
$ command 2>>file
#如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:
$ command > file 2>&1
或者
$ command >> file 2>&1
#如果希望对 stdin 和 stdout 都重定向,可以这样写
$ command < file1 >file2
#command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。
Here Document
Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。
#基本形式
command << delimiter
document
delimiter
#它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。
#结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
#开始的delimiter前后的空格会被忽略掉。
#示例 在命令行中通过 wc -l 命令计算 Here Document 的行数
$ wc -l << EOF
欢迎来到
菜鸟教程
www.runoob.com
EOF
3 # 输出结果为 3 行
cat << EOF
欢迎来到
菜鸟教程
www.runoob.com
EOF
#输出:
欢迎来到
菜鸟教程
www.runoob.com
实战
这个是在实际项目中经常用到的
#如果希望屏蔽 stdout 和 stderr,可以这样写:
command > /dev/null 2>&1
#部署时 在Linux 上静默启动weblogic
nohup startWeblogic.sh >/dev/null 2>&1 &
#这里的 2 和 > 之间不可以有空格,2> 是一体的时候才表示错误输出
read [ -r ] variable
用途:将信息读入一个或多个Shell变量
主要选项:-r 原始读取
未完待续