shell脚本入门笔记

转载:http://mp.weixin.qq.com/s?__biz=MzA3MTIxNzkyNg==&mid=204081791&idx=1&sn=27bb1d827e0f8582596f090471a5c098&scene=5#rd

简单shell脚本

!/bin/bash

这一行表明,不管用户选择的是那种交互式shell,该脚本需要使用bash shell来运行。由于每种shell的语法大不相同,所以这句非常重要。

简单实例

下面是一个非常简单的shell脚本。它只是运行了几条简单的命令


1

2

3

4


#!/bin/bash

echo "hello, $USER. I wish to list some files of yours"

echo "listing files in the current directory, $PWD"

ls # 列出当前目录所有文件

首先,请注意第四行。在bash脚本中,跟在#符号之后的内容都被认为是注释(除了第一行)。Shell会忽略注释。这样有助于用户阅读理解脚本。 ?$USER和 $PWD都是变量。它们是bash脚本自定义的标准变量,无需在脚本中定义即可使用。请注意,在双引号中引用的变量会被展开(expanded)。“expanded”是一个非常合适的形容词:基本上,当shell执行命令并遇到$USER变量时,会将其替换为该变量对应的值。

变量

任何编程语言都会用到变量。你可以使用下面的语句来定义一个变量:


1

X="hello"

并按下面的格式来引用这个变量:

$X

更具体的说,$X表示变量X的值。关于语义方面有如下几点需要注意:

  • 等于号两边不可以有空格!例如,下面的变量声明是错误的 :

1

X = hello
  • 在我所展示的例子中,引号并不都是必须的。只有当变量值包含空格时才需要加上引号。例如:

1

2


X = hello world # 错误

X = "hello world" # 正确

这是由于shell将每一行命令视为命令及其参数的集合,以空格分隔。 foo=bar就被视为一条命令。foo = bar 的问题就在于shell将空格分开的foo视为命令。同样,X=hello world的问题就在于shell将X=hello视为一条完整的命令,而”world”则被彻底无视(因为赋值命令不需其他参数)。

单引号 VS 双引号

基本上来说,变量名会在双引号中展开,单引号中则不会。如果你不需要引用变量值,那么使用单引号可以很直观的输出你期望的结果。 An example 示例


1

2

3

4


#!/bin/bash

echo -n ‘$USER=‘ # -n选项表示阻止echo换行

echo "$USER"

echo "\$USER=$USER" # 该命令等价于上面的两行命令

输出如下(假设你的用户名为elflord)) $USER=elflord $USER=elflord


1

2

3


$USER=elflord

$USER=elflord

从例子中可以看出,在双引号中使用转义字符也是一种解决方案。虽然双引号的使用更灵活,但是其结果不可预见。如果要在单引号和双引号之间做出选择,最好选择单引号。

使用引号封装变量

有时候,使用双引号来保护变量名是个很好的点子。如果你的变量值存在空格或者变量值为空字符串,这点就显得尤其重要。看下面这个例子:


1

2

3

4

5


#!/bin/bash

X=""

if [ -n $X ]; then # -n 用来检查变量是否非空

echo "the variable X is not the empty string"

fi

运行这个脚本,输出如下:

the variable X is not the empty string

为何?这是因为shell将$X展开为空字符串,表达式[-n]返回真值(因为改表达式没有提供参数)。再看这个脚本:


1

2

3

4

5


#!/bin/bash

X=""

if [ -n "$X" ]; then # -n 用来检查变量是否非空

echo "the variable X is not the empty string"

fi

在这个例子中,表达式展开为[ -n ""],由于引号中内容为空,因此该表达式返回false值。

在执行时展开变量

为了证实shell就像我上面说的那样直接展开变量,请看下面的例子:


1

2

3

4

5


#!/bin/bash

LS="ls"

LS_FLAGS="-al"

$LS $LS_FLAGS $HOME

乍一看可能有点不好理解。其实最后一行就是执行这样一条命令:

Ls -al /home/elflord

(假设当前用户home目录为/home/elflord)。这就说明了shell仅仅只是将变量替换为对应的值再执行命令而已。

使用大括号保护变量

这里有一个潜在的问题。假设你想打印变量X的值,并在值后面紧跟着打印”abc”。那么问题来了:你该怎么做呢? 先试一试:


1

2

3


#!/bin/bash

X=ABC

echo "$Xabc"

这个脚本没有任何输出。究竟哪里出了问题?这是由于shell以为我们想要打印变量Xabc的值,实际上却没有这个变量。为了解决这种问题可以用大括号将变量名包围起来,从而避免其他字符的影响。下面这个脚本可以正常工作:

!/bin/bashX=ABCecho “${X}abc”


1

2

3


#!/bin/bash

X=ABC

echo "${X}abc"

条件语句, if/then/elif

在某些情况下,我们需要做条件判断。比如判断字符串长度是否为0?判断文件foo是否存在?它是一个链接文件还是实际文件?首先,我们需要if命令来执行检查。语法如下:


1

2

3

4

5

6


if condition

then

statement1

statement2

..........

fi

当指定条件不满足时,可以通过else来指定其他执行动作。


1

2

3

4

5

6

7

8


if condition

then

statement1

statement2

..........

else

statement3

fi

当if条件不满足时,可以添加多个elif来检查其他条件是否满足。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17


if condition1

then

statement1

statement2

..........

elif condition2

then

statement3

statement4

........

elif condition3

then

statement5

statement6

........

fi

当相关条件满足时,shell会执行在相应的if/elif与下个elif或fi之间的语句。事实上,判断条件可以是任意命令,当且只当命令返回并且退出状态为0时,才会执行该条件块中的语句(换句话说,就是当命令成功返回时)。不过在本文的学习中,我们只会关注“test”或“[]”形式的条件判断。

Test命令与操作符

条件判断中的命令几乎都是test命令。test根据测试条件通过或失败来返回true或false(更准确的说是返回0或非0值)。如下所示:


1

test operand1 operator operand2

对某些测试来说,只需要一个操作数(operand2)通常是下面这种情况的简写:


1

[ operand1 operator operand2 ]

为了让我们的讨论更接地气一点,给出下面一些例子:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23


#!/bin/bash

X=3

Y=4

empty_string=""

if [ $X -lt $Y ] # is $X less than $Y ?

then

echo "$X=${X}, which is smaller than $Y=${Y}"

fi

if [ -n "$empty_string" ]; then

echo "empty string is non_empty"

fi

if [ -e "${HOME}/.fvwmrc" ]; then # test to see if ~/.fvwmrc exists

echo "you have a .fvwmrc file"

if [ -L "${HOME}/.fvwmrc" ]; then # is it a symlink ?

echo "it‘s a symbolic link

elif [ -f "${HOME}/.fvwmrc" ]; then # is it a regular file ?

echo "it‘s a regular file"

fi

else

echo "you have no .fvwmrc file"

fi

需要注意的细节

Test命令的格式为“操作数< 空格 >操作符< 空格 >操作数”或者“操作符< 空格 >操作数”,这里特别说明必须要有这些空格,因为shell将没有空格的第一串字符视为一个操作符(以-开头)或者操作数。比如下面这个:

if [ 1=2 ]; then echo “hello”fi

它会打印出hello,这明显与预期结果是不一致的(因为shell只看到操作数1=2,没看到操作符)。

还有一种隐藏陷阱是未加引号的变量。像我们之前例子说的-n测试时变量须加引号的情形。其实,不管在什么情况下,加上引号总是没有坏处的,还有可能规避一些很奇葩的错误。因为有时候不加引号的变量扩展开的测试结果会让人非常困惑。例如:


1

2

3

4

5

6


#!/bin/bash

X="-n"

Y=""

if [ $X = $Y ] ; then

echo "X=Y"

fi

这个脚本打印出来的结果是错误的,因为shell将判断展开为 [ -n = ],但是”=”的长度不为0,所以条件判断通过从而导致输出结果为“X=Y”。

Test操作符简介

下图是test操作符的快速查询列表。当然这个列表并不全面,但记下这些就足够平常使用了(如果还需要了解其他操作符,可以查看man手册)。

operator produces true if… number of operands
-n operand non zero length 1
-z operand has zero length 1
-d there exists a directory whose name is operand 1
-f there exists a file whose name is operand 1
-eq the operands are integers and they are equal 2
-neq the opposite of -eq 2
= the operands are equal (as strings) 2
!= opposite of = 2
-lt operand1 is strictly less than operand2 (both operands should be integers) 2
-gt operand1 is strictly greater than operand2 (both operands should be integers) 2
-ge operand1 is greater than or equal to operand2 (both operands should be integers) 2
-le operand1 is less than or equal to operand2 (both operands should be integers) 2

循环

循环结构允许我们执行重复的步骤或者在若干个不同条目上执行相同的程序。Bash中有下面两种循环

  • for 循环
  • while 循环

For 循环

直接来个例子,来直观地感受for循环的语法。


1

2

3

4

5


#!/bin/bash

for X in red green blue

do

echo $X

done

For循环会遍历空格分开的条目。注意,如果某一项含有空格,必须要用引号引起来,例子如下:


1

2

3

4

5

6

7

8


#!/bin/bash

colour1="red"

colour2="light blue"

colour3="dark green"

for X in "$colour1" $colour2" $colour3"

do

echo $X

done

如果我们漏掉for循环中的引号,你能猜想出会发生什么吗?这个例子说明,除非你确认变量中不会包含空格,否则最好都用引号将变量保护起来。

在for循环中使用通配符

如果shell解析字符串时遇到*号,会将它展开为所有匹配的文件名。当且仅当目标文件与号展开后的字符串一致才会匹配成功。例如,单独的*号展开为当前目录的所有文件,中间以空格分开(包含隐藏文件)。

所以:

echo *

列出当前目录下的所有文件和目录。

echo *.jpg

列出所有的jpeg图片格式的文件。

echo ${HOME}/public_html/*.jpg

列出home目录中public_html目录下的所有jpeg文件。

正是由于这种特性,使得我们可以很方便的来操作目录和文件,尤其是和for循环结合使用时,更是便利。例子如下:


1

2

3

4

5


#!/bin/bash

for X in *.html

do

grep -L ‘<UL>‘ "$X"

done

打印出当前目录下所有不包含<UL>字段的html文件。

While 循环

当给定条件为真值时,while循环会重复执行。例如:


1

2

3

4

5

6

7


#!/bin/bash

X=0

while [ $X -le 20 ]

do

echo $X

X=$((X+1))

done

这样导致这样的疑问: 为什么bash不能使用C风格的for循环呢?

for (X=1,X<10; X++)

这也跟bash自身的特性有关,之所以不允许这种for循环是由于:bash是一种解释性语言,因此其运行效率比较低。也正是由于这个原因,高负荷迭代是不允许的。

命令替换

Bash shell有个非常好用的特性叫做命令替换。允许我们将一个命令的输出当做另一个命令的输入。比如你想要将命令的输出赋值给变量X,你可以通过变量替换来实现。

有两种命令替换的方式:大括号扩展和反撇号扩展。

大括号扩展: $(commands) 会展开为命令commands的输出结果。并且允许嵌套使用,所以commands中允许包含子大括号扩展。

反撇好扩展:将commands扩展为命令commands的输出结果。不允许嵌套。

这里有一个例子:


1

2

3

4

5

6

7


#!/bin/bash

files="$(ls)"

web_files=`ls public_html`

echo "$files" # we need the quotes to preserve embedded newlines in $files

echo "$web_files" # we need the quotes to preserve newlines

X=`expr 3 * 2 + 4` # expr evaluate arithmatic expressions. man expr for details.

echo "$X"

$()替换方式的优点不言自明:非常易于嵌套。并且大多数bourne shell的衍生版本都支持(POSIX shell 或者更好的都支持)。不过,反撇号替换更简单明了,即使是最基本的shell它也提供了支持(任意版本的#!/bin/sh都可以)

时间: 2024-11-07 10:32:36

shell脚本入门笔记的相关文章

Shell 脚本入门--下

Shell 脚本入门--下 本为大家介绍,本篇介绍内容有:逻辑运算符(& .| .!.^).测试命令.脚本的执行过程 1.逻辑运算符 (1)&与和&&短路与 True .false 1       0 与: 1 & 1 = 1 1 & 0 = 0 0 & 1 = 0 0 & 0 = 0 对于&来说,如果左侧条件为false,也会计算右侧条件的值.特点只要有一个false就认为是false 短路与: false && =

shell脚本相关笔记

书写一个shell脚本并使之执行的步骤: 1. 打开一个文件,以 #! bin/bash作为开头 2.在文件中写入一些正确可执行的shell语句 3.保存文件 4.更改文件权限,执行文件 一些小技巧: 如果想在系统中添加自己的命令,比如执行自己建好的shell脚本语句这样一条命令 实例: 1.创建文件cd.sh 内容为: #! bin/bash cd xxx/yyy/zzz 2.保存文件 3.执行文件./cd.sh 现在发现并没有能够打开我们想要打开的目录并进去. 原因是shell还有分父she

shell 脚本实战笔记(6)--集群环境配置检测

1). 背景: 集群部署的时候, 需要一致的配置和环境设置. 对于虚拟机集群, 可以借助镜像拷贝, 复制和还原集群机器. 对与物理机集群而言, 则不一样, 如果机器一多, 多人去操作和配置, 对于成熟精干的团队还好, 对于不熟悉环境的小团队, 由于水平的参差不齐, 往往会导致不一致的环境. 因此无论如何, 写脚本进行自动化的配置和环境校验总是最佳实践. 2). 假设应用场景:*) 系统环境: 安装CDH5, 集群规模为16台机器, 每台机器16CPU, 内存16G, 2块SATA盘共500G,

shell 脚本实战笔记(3)--集群机器的时间同步设置

背景: 有些分布式服务(比如HBase服务), 依赖于系统时间戳, 如果集群各个节点, 系统时间不一致, 导致服务出现诡异的情况. 解决方案: 那如何同步集群各个节点之间的时间? 采用NTP(Network Time Protocol)方式来实现, 选择一台机器, 作为集群的时间同步服务器, 然后分别配置服务端和集群其他机器 1.NTP服务端 *) 安装ntp服务 yum install ntp *) 配置/etc/ntp.conf 这边采用本地机器作为时间的原点 注释server列表 #ser

Bash Shell脚本编程笔记总结(一)

本文是上课笔记总结,涉及细节知识点会在以后文章说明! bash脚本编程: 脚本程序:解释器解释执行: shell: 交互式接口:编程环境: shell: 能够提供一些内部命令,并且能通过PATH环境变量找到外部命令:把命令提交给内核启动为进程: 编程环境: 流程控制语句: 顺序执行: 循环执行: 选择执行: 条件测试:真.假 $? 命令的状态结果: 0: 真 1-255: 假 过程式的编程语言的元素:变量.流程.函数.数组 变量:局部变量.本地变量.环境变量.位置参数变量.特殊变量 变量: 数值

Bash Shell脚本编程笔记总结(二)

本文接上一部分:Bash Shell脚本编程笔记总结(一) 数组: 连续的多个独立内存空间,每个内存空间相当于一个变量 数组元素:数组名+索引 索引:从0开始编号 声明数组: declare -a ARRAR_NAME 关联数组: declare -A ARRAY_NAME 支持稀疏格式: 数组元素的赋值: (1) 一次只赋值一个元素 ARRAY[index]=VALUE a[0]="hello" (2) 一次赋值全部元素 ARRAY=("mon" "tu

Linux Shell脚本入门--cut命令

Linux Shell脚本入门--cut命令 cut cut 命令可以从一个文本文件或者文本流中提取文本列. cut语法 [[email protected] ~]# cut -d'分隔字符' -f fields <==用于有特定分隔字符 [[email protected] ~]# cut -c 字符区间 <==用于排列整齐的信息 选项与参数: -d :后面接分隔字符.与 -f 一起使用: -f :依据 -d 的分隔字符将一段信息分割成为数段,用 -f 取出第几段的意思: -c :以字符 (

shell script 入门 笔记

shell script 入门 在 shell script 的撰写中还需要用到底下的注意事项: 1.  指令的执行是从上而下.从左而右的分析与执行: 2.  指令的执行就如同第五章内提到的: 指令.选项不参数间的多个空白都会被忽略掉: 3.  空白行也将被忽略掉,而且 [tab] 按键所推开的空白同样规为空格键: 4.  如果读取到一个 Enter 符号 (CR) ,就尝试开始执行该行 (或该串) 命令: 5.  至亍如果一行的内容太多,则可以使用『 \[Enter] 』来延伸至下一行: 6.

shell 脚本实战笔记(8)--ssh免密码输入执行命令

前言: ssh命令, 没有指定密码的参数. 以至于在脚本中使用ssh命令的时候, 必须手动输入密码, 才能继续执行. 这样使得脚本的自动化执行变得很差, 尤其当ssh对应的机器数很多的时候, 会令人抓狂.本文讲解了两种方式, 一种借助expect脚本, 一种借助sshpass来实现. *) 借助expect脚本来实现1. expect不是系统自带的工具, 需要安装yum install expect -y 2. expect脚本的编写规则 1. [#!/usr/bin/expect] 告知系统脚