1. shell script入门

为了将减少每天重复的代码量,将每日例行的代码可以写入bash中。

一、Bash简介

shell script 是利用 shell 的功能所写的一个『程序 (program)』,这个程序是使用纯文本文件,将一些shell 的语法与指令(含外部指令)写在里面, 搭配正规表示法管线命令数据流重导向等功能,以达到我们所想要的处理目的

shell script 用在系统管理上面是很好的一项工具,但是用在处理大量数值运算上, 就不够好了,因为Shell scripts 的速度较慢,且使用的 CPU 资源较多,造成主机资源的分配不良。

在 shell script 的撰写中还需要用到底下的注意事项

  1. 指令的执行是从上而下、从左而右的分析与执行;
  2. 指令、选项与参数间的多个空白都会被忽略掉;
  3. 空白行也将被忽略掉,并且 [tab] ****按键所推开的空白同样视为空格键;
  4. 如果读取到一个 Enter 符号 (CR) ,就尝试开始执行该行 (或该串) 命令;
  5. 至于如果一行的内容太多,则可以使用 \[Enter]来延伸至下一行;
  6. #可做为批注!任何加在 # 后面的资料将全部被视为批注文字而被忽略!

命令的执行:

方法一:

直接指令下达: shell.sh 档案必须要具备可读与可执行 (rx) 的权限,然后:

绝对路径:使用 /home/dmtsai/shell.sh 来下达指令;

相对路径:假设工作目录在 /home/dmtsai/ ,则使用 ./shell.sh 来执行

方法二:

变量『PATH』功能:将 shell.sh 放在 PATH 指定的目录内,例如: ~/bin/

以 bash 程序来执行:透过『 bash shell.sh 』或『 sh shell.sh 』来执行

注意:

script 的执行方式差异 (source, sh script, ./script)

利用直接执行的方式来执行 script

利用 source 来执行脚本:在父程序中执行source ./script

意思就是说source后,脚本中的变量会写入终端,这个变量以后还保存,而其他的方式执行脚本后,脚本中的变量仅仅停留在这个脚本中。这也就是为什么修改环境变量后要source.

二、一个简单的例子

Hello.sh

#!/bin/bash 
# This is a very simple example
echo Hello World

问题:

  1. 第一行的 #! 是什么意思
  2. 第一行的 /bin/bash 又是什么意思
  3. 第二行是注释吗
  4. echo 语句
  5. 如何执行该程序

在 BASH 中 第一行的 “#!” 及后面的 “/bin/bash” 就表明该文件是一个 BASH 程序,需要由 /bin 目录下的 bash 程序来解释执行。

第二行的 “# This is a …” 就是 BASH 程序的注释,在 BASH 程序中从“#”号(注意:后面紧接着是“!”号的除外)开始到行尾的多有部分均被看作是程序的注释。

第三行的 echo 语句的功能是把 echo 后面的字符串输出到标准输出中去。由于 echo 后跟的是 “Hello World” 这个字符串,因此 “Hello World”这个字串就被显示在控制台终端的屏幕上了。

运行:

一种是显式制定 BASH 去执行:
$ bash hello.sh
方法二:
用/bin/bash 程序去解释执行 hello 文件的:
$ chmod u+x hello
$ ./hello.sh

三、详解

3.1输入和输出

在 Linux 系统中:标准输入(stdin)默认为键盘输入;标准输出(stdout)默认为屏幕输出;标准错误输出(stderr)默认也是输出到屏幕(上面的std 表示 standard)。在 BASH 中使用这些概念时一般将标准输出表示为 1,将标准错误输出表示为 2。

输入、输出及标准错误输出主要用于 I/O 的重定向,就是说需要改变他们的默认设置。先看这个例子:

$ ls > ls_result
$ ls -l >> ls_result

上面这两个命令分别将 ls 命令的结果输出重定向到 ls_result 文件中和追加到 ls_result 文件中,而不是输出到屏幕上。”>“就是输出(标准输出和标准错误输出)重定向的代表符号,连续两个 “>” 符号,即 “>>” 则表示不清除原来的而追加输出。下面再来看一个稍微复杂的例子:

$ find /home -name lost* 2> err_result

这个命令在 “>” 符号之前多了一个 “2”,”2>” 表示将标准错误输出重定向。由于 /home 目录下有些目录由于权限限制不能访问,因此会产生一些标准错误输出被存放在 err_result 文件中。大家可以设想一下

find /home -name lost* 2>>err_result 

命令会产生什么结果?

如果直接执行

find /home -name lost* > all_result 

其结果是只有标准输出被存入 all_result 文件中,要想让标准错误输出和标准输入一样都被存入到文件中,那该怎么办呢?看下面这个例子:

$ find /home -name lost* > all_result 2>& 1

上面这个例子中将首先将标准错误输出也重定向到标准输出中,再将标准输出重定向到 all_result 这个文件中。这样我们就可以将所有的输出都存储到文件中了。为实现上述功能,还有一种简便的写法如下:

$ find /home -name lost* >& all_result

如果那些出错信息并不重要,下面这个命令可以让你避开众多无用出错信息的干扰:

$ find /home -name lost* 2> /dev/null

3.2变量

BASH 中的变量都是不能含有保留字,不能含有 “-” 等保留字符,也不能含有空格。

hello2.py例如:

#!/bin/bash 
# give the initialize value to STR
STR="Hello World" 
echo $STR 

在上面这个程序中我们需要注意下面几点:

  1. 变量赋值时,’=‘左右两边都不能有空格;

2.BASH 中的语句结尾不需要分号(”;“);

3.除了在变量赋值和在FOR循环语句头中,BASH 中的变量使用必须在变量前加”$“符号,同学们可以将上面程序中第三行改为 “echo STR” 再试试,看看会出什么结果。==>output: STR

4.由于 BASH 程序是在一个新的进程中运行的,所以该程序中的变量定义和赋值不会改变其他进程或原始 Shell 中同名变量的值,也不会影响他们的运行。

四、案例

[sam] vi sh01.sh

#! /bin/bash
#Program:
#    This progaram shows "Hello World !" in your screen.
# History:
#2014/07/21  Sam First release
PATH=/bin:/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Hello World! \a \n"
exit 0

#变量由用户定义

[sam] vi sh02.sh

#! /bin/bash
#Program:
#  User inputs his first name and last name . Progaram shows his full name.
#History :
#2014/07/21 Sam First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

read -p "Please input your first name :" fisrtname  # 提示使用者输入
read -p "Please input your last name :"  lastname   #提示使用者输入
echo -e "\nYour full name is :$fisrtname $lastname"   #结果由屏幕输出

#利用日期进行文件建立

[sam] vi sh03.sh
#!/bin/bash
#Program:
#   Program creats three files, which named by user's input
#   and date command.
#History:
#  2014/07/22  Sam  First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# 1. 让使用者输入文件名,并取得 fileuser 这个变量;
echo -e "I will use 'touch' command to create 3 files." # 纯粹显示信息
read -p "Please input your filename: " fileuser         # 提示使用者输入
# 2. 为了避免用户随意按 Enter ,利用变量功能分析文件名是否有设定?
filename=${fileuser:-"filename"}           # 开始判断有否配置文件名
# 3. 开始利用 date 指令来取得所需要的档名了;
date1=$(date --date='2 days ago' +%Y%m%d)  # 前两天的日期
date2=$(date --date='1 days ago' +%Y%m%d)  # 前一天的日期
date3=$(date +%Y%m%d)                      # 今天的日期
file1=${filename}${date1}                  # 底下三行在配置文件名
file2=${filename}${date2}
file3=${filename}${date3}
# 4. 将档名建立吧!
touch "$file1"                             # 底下三行在建立档案
touch "$file2"
touch "$file3"

#数值的运算

[sam] vi sh04.sh

#!/bin/bash
#  Program:
#    User inputs 2 integer numbers; program will cross these two numbers.
#History:
#  2014/07/22  Sam  First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/licl/bin:/usr/local/sbin:~/bin
export PATH
echo -e "You SHOULD input 2 numbers, I will cross them! \n"
read -p "first number:  " firstnu
read -p "second number: " secnu
total=$(($firstnu*$secnu))
echo -e "\nThe result of $firstnu x $secnu is ==> $total"

五、报错与讨论

5.1参数-e显示出来

在下面运行例子sh sh01.sh运行后出来的结果为 -e Hello Word!

这个-e 是echo 的一个参数,为什么运行脚本会显示出来

而直接 echo -e “Hello Word !“ 这个-e是不显示出来的

解决办法:

你运行下echo $SHELL看看当前用的是不是bash作shell

/bin/zsh  #原来不是bash作为默认的shell, 说明zsh和bash的echo不一样

用bash sh01.sh运行脚本试试

或者把该脚本加上可执行属性再直接运行

没必要改把默认的shell改为bash

给脚本加上可执行属性

脚本在运行时会自动调用bash

参考资料:

http://blog.chinaunix.net/uid-25932176-id-2973818.html

个人公众号,比较懒,很少更新,可以在上面提问题:

更多精彩,请移步公众号阅读:

Sam avatar
About Sam
专注生物信息 专注转化医学