编写shell脚本(一)

【Linux】Ubuntu16.04配置samba服务实现与win10共享文件夹访问
彻底卸载软件

Ubuntu下配置支持Windows访问的samba共享

vi filename打开文件,执行 : set ff=unix 设置文件为unix,然后执行:wq,保存成unix格式。

tr -d '\r' < test.sh > test2.sh 
这样也可以

什么是shell脚本

最简单的解释,一个 shell 脚本就是一个包含一系列命令的文件。shell 读取这个文件,然后执行 文件中的所有命令,就好像这些命令已经直接被输入到了命令行中一样。

  • Write a script.
  • Make the script executable. 对于脚本文件,有两个常见的权限设置;权限为755的脚本,则每个人都能执行,和权限为700的 脚本,只有文件所有者能够执行。注意为了能够执行脚本,脚本必须是可读的。
  • Put the script somewhere the shell can find it.
    什么使我们的脚本不同于其它的程序?结果证明,什么也没有。我们的 脚本没有问题。是脚本存储位置的问题。回到第12章,我们讨论了 PATH 环境变量及其在系统 查找可执行程序方面的作用。回顾一下,如果没有给出可执行程序的明确路径名,那么系统每次都会 搜索一系列的目录,来查找此可执行程序。这个/bin 目录就是其中一个系统会自动搜索的目录。 这个目录列表被存储在一个名为 PATH 的环境变量中。这个 PATH 变量包含一个由冒号分隔开的目录列表。

当做了这个修改之后,它会在每个新的终端会话中生效。为了把这个修改应用到当前的终端会话中, 我们必须让 shell 重新读取这个 .bashrc 文件。这可以通过 “sourcing”.bashrc 文件来完成:

. .bashrc

这个点(.)命令是 source 命令的同义词,一个 shell 内建命令,用来读取一个指定的 shell 命令文件, 并把它看作是从键盘中输入的一样。
如果我们编写了一个脚本,系统中的每个用户都可以使用它, 那么这个脚本的传统位置是 /usr/local/bin。系统管理员使用的脚本经常放到 /usr/local/sbin 目录下。 大多数情况下,本地支持的软件,不管是脚本还是编译过的程序,都应该放到 /usr/local 目录下, 而不是在 /bin/usr/bin 目录下。这些目录都是由 Linux 文件系统层次结构标准指定,只包含由 Linux 发行商 所提供和维护的文件

格式技巧

  1. 长选项名称
    为了减少输入,当在命令行中输入选项的时候,短选项更受欢迎,但是当书写脚本的时候, 长选项能提供可读性。
  2. 缩进和行继续符
find playground \
    \( \
        -type f \
        -not -perm 0600 \
        -exec chmod 0600 ‘{}’ ‘;’ \
    \) \
    -or \
    \( \
        -type d \
        -not -perm 0711 \
        -exec chmod 0711 ‘{}’ ‘;’ \
    \)

启动第一个项目

The program we will write is a report generator. It will present various statistics about our system and its status, and will produce this report in HTML format, so we can view it with a web browser such as Firefox or Konqueror.

在这里插入图片描述

shell变量名

规则
变量名可由字母数字字符(字母和数字)和下划线字符组成。

变量名的第一个字符必须是一个字母或一个下划线。

变量名中不允许出现空格和标点符号。

给变量赋值

variable=value

注意在赋值过程中,变量名、等号和变量值之间必须没有空格。

a=z                     # Assign the string "z" to variable a.
b="a string"            # Embedded spaces must be within quotes.
c="a string and $b"     # Other expansions such as variables can be
                        # expanded into the assignment.

d=$(ls -l foo.txt)      # Results of a command.
e=$((5 * 7))            # Arithmetic expansion.
f="\t\ta string\n"      # Escape sequences such as tabs and newlines.

可以在同一行对多个变量赋值:

a=5 b="a string"

在变量名周围上下文不明确的情况下,,可以用{}

[me@linuxbox ~]$ filename="myfile"
[me@linuxbox ~]$ touch $filename
[me@linuxbox ~]$ mv $filename $filename1
mv: missing destination file operand after `myfile'
Try `mv --help' for more information.
mv $filename ${filename}1

通过添加花括号,shell 不再把末尾的1解释为变量名的一部分。

#!/bin/bash
# Program to output a system information page
TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z") 
TIME_STAMP="Generated $CURRENT_TIME, by $USER"
echo "<HTML>
        <HEAD>
                <TITLE>$TITLE</TITLE>
        </HEAD>
        <BODY>
                <H1>$TITLE</H1>
                <P>$TIME_STAMP</P>
        </BODY>
</HTML>"
<HTML>
    <HEAD>
          <TITLE>System Information Report for ubuntu</TITLE>
    </HEAD>
    <BODY>
          <H1>System Information Report for ubuntu</H1>
	  <p>generated 02/21/2020 08:15:29 PM +0800, by root</p>
    </BODY>
</HTML>

Here Document 另一种I/O重定向形式

command << token
text
token
cat << _EOF_
<HTML>
         <HEAD>
                <TITLE>$TITLE</TITLE>
         </HEAD>
         <BODY>
                <H1>$TITLE</H1>
                <P>$TIME_STAMP</P>
         </BODY>
</HTML>
_EOF_

注意这个 token 必须在一行中单独出现,并且文本行中 没有末尾的空格。
优点是:here documents 中的单引号和双引号会失去它们在 shell 中的特殊含义,这允许我们在报告中随意嵌入引号

cat << _EOF_
> $foo
> "$foo"
> '$foo'
> \$foo
> _EOF_
some text
"some text"
'some text'
$foo

如果我们把重定向操作符从 “<<” 改为 “<<-”,shell 会忽略在此 here document 中开头的 tab 字符。 这就能缩进一个 here document,从而提高脚本的可读性

自顶向下设计

./sys_info_page: line 6: report_uptime: command not found
./sys_info_page: line 6: report_disk_space: command not found
./sys_info_page: line 6: report_home_space: command not found

两种方法来创建额外命令:

  • 写三个脚本,放置到 环境变量 PATH 所列出的目录下
  • 把这些脚本作为 shell 函数嵌入到我们的程序中
    shell 函数是位于其它脚本中的“微脚本”,作为自主程序。Shell 函数有两种语法形式:
function name() {
    commands
    return
}
and
name () {
    commands
    return
}

两种形式是等价的,可以交替使用。注意()是必须的

局部变量

#!/bin/bash
# local-vars: script to demonstrate local variables
foo=0 # global variable foo
funct_1 () {
    local foo  # variable foo local to funct_1
    foo=1
    echo "funct_1: foo = $foo"
}
funct_2 () {
    local foo  # variable foo local to funct_2
    foo=2
    echo "funct_2: foo = $foo"
}
echo "global:  foo = $foo"
funct_1
echo "global: foo = $foo"
funct_2
echo "global: foo = $foo"
#正如我们所看到的,通过在变量名之前加上单词 local,来定义局部变量。这就创建了一个只对其所在的 shell 函数起作用的变量。在这个 shell 函数之外,这个变量不再存在。对两个 shell 函数中的局部变量 foo 赋值,不会影响到在函数之外定义的变量 foo 的值
global:  foo = 0
funct_1: foo = 1
global: foo = 0
funct_2: foo = 2
global: foo = 0

命令结果被 pre 标签包围, 为的是保持命令的输出格式
被包围在 pre 元素中的文本通常会保留空格和换行符。而文本也会呈现为等宽字体。pre标签的一个常见应用就是用来表示计算机的源代码。
使用 df -h 命令来确定磁盘空间的数量
我们使用带有 -sh 选项的 du 命令来完成这个任务。然而,这并不是此问题的完整解决方案。虽然它会 在一些系统(例如 Ubuntu)中起作用,但是在其它系统中它不工作。这是因为许多系统会设置家目录的 权限,以此阻止其它用户读取它们,这是一个合理的安全措施。在这些系统中,这个 report_home_space 函数, 只有用超级用户权限执行我们的脚本时,才会工作。一个更好的解决方案是让脚本能根据用户的使用权限来 调整自己的行为。

function report_uptime(){
  cat <<- _EOF_
  <h2> system uptime </h2>
  <pre>$(uptime)</pre>
_EOF_
  return 
}
function report_disk_space(){
  cat <<- _EOF_
  <h2> disk space utilization </h2>
  <pre>$(df -h)</pre>
_EOF_
  return 
}
function report_home_space(){
  cat <<- _EOF_
  <h2> home space utilization </h2>
  <pre>$(du -sh /home/*)</pre>
_EOF_
  return 
}


_EOF_一定要定格写。。。。不然匹配补上。。。

流程控制:if分支结构

if [ $x=5 ]

其他地方的缩进无所谓,但是[空格$x=5 空格]必须有,艹

退出状态

当命令执行完毕后,命令(包括我们编写的脚本和 shell 函数)会给系统发送一个值,叫做退出状态。 这个值是一个 0 到 255 之间的整数,说明命令执行成功或是失败。按照惯例,一个零值说明成功,其它所有值说明失败。在执行完命令后用echo $?来检查退出状态

root@ubuntu:/code# echo $?
0
root@ubuntu:/code# false
root@ubuntu:/code# echo $?
1

当 if 之后的命令执行成功的时候,命令 echo “It’s true.” 将会执行,否则此命令不执行。 如果 if 之后跟随一系列命令,则将计算列表中的最后一个命令:

[me@linuxbox ~]$ if false; true; then echo "It's true."; fi
It's true.
[me@linuxbox ~]$ if true; false; then echo "It's true."; fi
[me@linuxbox ~]$

测试

经常与 if 一块使用的命令是 test。这个 test 命令执行各种各样的检查与比较。 它有两种等价模式:

test expression
[ expression ]

这里的 expression 是一个表达式,其执行结果是 true 或者是 false。当表达式为真时,这个 test 命令返回一个零 退出状态,当表达式为假时,test 命令退出状态为1。

文件表达式

在这里插入图片描述
在这里插入图片描述

# !/bin/bash
file=~/.bashrc
if [ -e "$file" ];then 
	if [ -f "$file" ];then 
		echo "$file is a regular file."
	fi
	if [ -d "$file" ]; then 
		echo "$file is a directory."
	fi
	if [ -r "$file" ]; then
		echo "$file is readable."
	fi
	if [ -w "$file" ]; then
		echo "$file is writable."
	fi
	if [ -x "$file" ]; then 
		echo "$file is executable/searchable."
	fi
else 
	echo "$file does not exist."
	exit 1
fi
exit  # 差点忘记这个exist了,怪不得上面的fi迟迟不变色

对于$file的“”不是必须的,但是可以防范空参数,如果$file展开为空值,操作符会被解释为非空字符串。用引号把参数引起来确保了操作符之后总是跟着一个字符串,即使字符串为空。
exit 命令
把它转换为函数接受一个单独的,可选的参数,其成为脚本的退出状态。当不传递参数时,退出状态默认为零。
The exit command appearing on the last line of the script is there as a formality. When a script “runs off the end” (reaches end of file), it terminates with an exit status of zero by default, anyway.

/root/.bashrcwww does not exist.
root@ubuntu:/code# echo $?
1

# !/bin/bash
function test()
{
	file=~/.bashrc
	if [ -e "$file" ];then 
		if [ -f "$file" ];then 
			echo "$file is a regular file."
		fi
		if [ -d "$file" ]; then 
			echo "$file is a directory."
		fi
		if [ -r "$file" ]; then
			echo "$file is readable."
		fi
		if [ -w "$file" ]; then
			echo "$file is writable."
		fi
		if [ -x "$file" ]; then 
			echo "$file is executable/searchable."
		fi
	else 
		echo "$file does not exist."
		return  1
	fi

}
test

如果文件不存在,echo $? 也是1

字符串表达式

在这里插入图片描述
当与 test 一块使用的时候, > 和 < 表达式操作符必须用引号引起来(或者是用反斜杠转义)。 如果不这样,它们会被 shell 解释为重定向操作符,造成潜在的破坏结果。
Notice the redirection that is applied to the echo command. This redirects the error message “There is no answer.” to standard error, which is the “proper” thing to do with error messages.
把错误信息 “There is no answer.” 重定向到标准错误,这是处理错误信息的“正确”方法。

# !/bin/bash
function test-string(){
	answer=xxx
	if [ -z "$answer" ];then 
		echo "there is no answer" >&2
		exit 1
	fi
	if [[ "$answer" = "yes" ]]; then		
		echo "the answer is yes."
	elif [[ "$answer" = "no" ]]; then
		echo "the answer is no"
	else
		echo "the answer is unknown."
	fi

}
test-string

[ “$answer"空格= 空格"yes”] 空格必须有,否则直接全员成立。。。。。

int = -5
echo $int
script.sh: line 1: int: command not found

注意区分。。。。。。。

root@ubuntu:/code# ./test.txt 
the answer is yes.
the answer is no
整型表达式

在这里插入图片描述

# !/bin/bash
function test-integer(){
	int=-5
	if [[ -z "$int" ]]; then
		#statements
		echo "int is empty" >&2
		exit 1
	fi
	if [[ $int -eq 0 ]]; then
		echo "int is zero"
	else
		if [[ $int -lt 0 ]]; then
			#statements
			echo "int is negative"
		else
			echo "int is positive"
		fi
		if [[ $((int%2)) -eq 0 ]]; then
			#statements
			echo "int is even"
		else
			echo "int is odd"
		fi
	fi

}
test-integer
if [[ $((int%2)) -eq 0 ]] #注意找个展开的运用
不在操作符后面不需要""
[[ expression ]]

前的 bash 版本包括一个复合命令,作为加强的 test 命令替代物。这个[[ ]]命令非常 相似于 test 命令(它支持所有的表达式),但是增加了一个重要的新的字符串表达式:string1 =~ regex
The script needs a way to verify that the constant contains an integer.

if [[ "$int" =~ ^-?[0-9]+$ ]]; then

Another added feature of [[ ]] is that the == operator supports pattern matching the same way pathname expansion does. For example:[[ ]]添加的另一个功能是==操作符支持类型匹配,正如路径名展开所做的那样。例如

file=foo.bar
if [[ $file == foo.* ]]; then
	echo "$FILE matches pattern 'foo.*'"
else
	echo "no matches"
fi
(( ))算数真测试

( ))被用来执行算术真测试。如果算术计算的结果是非零值,则其测试值为真。

# !/bin/bash
function test-integer(){
	int=4
if [[ "$int" =~ ^-?[0-9]+$ ]]; then
	if ((int == 0)); then
		echo "int is zero"
	else
		if ((int < 0)); then
			#statements
			echo "int is negative"
		else
			echo "int is positive"
		fi
		if ((((int % 2)) == 0)); then
			#statements
			echo "int is even"
		else
			echo "int is odd"
		fi
	fi
else
	echo "int is not an integer" >&2
fi
}
test-integer


注意我们使用小于和大于符号,以及==用来测试是否相等。这是使用整数较为自然的语法了。也要 注意,因为复合命令 (( )) 是 shell 语法的一部分,而不是一个普通的命令,而且它只处理整数, 所以它能够通过名字识别出变量,而不需要执行展开操作。

综合表达式

在这里插入图片描述

#!/bin/bash
min_val=1
max_val=100
int=1x50
if [[ $int =~ ^-?[0-9]+$ ]]; then
	if [[ $int -ge min_val && $int -le max_val ]]; then
		echo "$int is in range of min_val and max_val"
	else
		echo "$int is out range of min_val and max_val"
	fi
else
	echo "int is not an integer"
fi

我们也可以对表达式使用圆括号,为的是分组。如果不使用括号,那么否定只应用于第一个 表达式,而不是两个组合的表达式

int=150
if [[ $int =~ ^-?[0-9]+$ ]]; then
	if [[ !($int -ge min_val && $int -le max_val) ]]; then
		echo "$int is out range of min_val and max_val"
	else
		echo "$int is in range of min_val and max_val"
	fi
else
	echo "int is not an integer"
fi

Since all expressions and operators used by test are treated as command arguments by the shell (unlike [[ ]] and (( )) ), characters which have special meaning to bash, such as <, >, (, and ), must be quoted or escaped.

控制操作符:分支的另一种方法
command1 && command2
command1 || command2
[ -d temp ] || mkdir temp #如果不是目录才会创建目录
[ -d temp ] || exit 1
区分用户
function report_home_space(){
  if [[ $(id -u) -eq 0 ]]; then
    cat <<- _EOF_
    <h2> home space utilization($USER) </h2>
    <pre>$(du -sh /home/*)</pre>
_EOF_
  else
    cat <<- _EOF_
    <h2> home space utilization ($USER)</h2>
    <pre>$(du -sh $HOME)</pre>
_EOF_
  fi
  
  return 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值