编写shell脚本
为了进⼀步提⾼效率,逐步⽤ linux 替代 windows,如果不会编写 shell 脚本则⽆法发挥命令⾏的优势。
1 Shell脚本
Shell 有些独特,因为它不仅是⼀个功能强⼤的命令⾏接⼝,也是⼀个脚本语⾔解释器。
⼀个 shell 脚本就是⼀个包含⼀系列命令的⽂件。shell 读取这个⽂件,然后执⾏⽂件中的所有命令,就好像这些命令已经直接被输⼊到了命令⾏中⼀样。
2 基本步骤
为了成功地创建和运⾏⼀个 shell 脚本,我们需要做三件事情:
1. 编写⼀个脚本hello_world。Linux并不根据后缀名判断⽂件类型,如果命名为hello_world.sh,执⾏的时候要输⼊完整的⽂件名。基本格式如
下:
#!/bin/bash
# This is our first script.
echo 'Hello World!'
这个#!字符序列是⼀种特殊的结构,被⽤来告诉操作系统将执⾏此脚本所⽤的解释器的名字。每个 shell 脚本都应该把这⼀⽂本⾏作为它的第⼀⾏。
2. 使脚本⽂件可执⾏。使⽤chmod命令,对于脚本⽂件,有两个常见的权限设置;权限为755的脚本,则每个⼈都能执⾏,和权限为700
的脚本,只有⽂件所有者能够执⾏。注意为了能够执⾏脚本,脚本必须是可读的。
3. 把脚本放置到 shell 能够到的地⽅。为了能够运⾏此脚本,我们必须指定脚本⽂件明确的路径,如下:shell最简单脚本
[me@linuxbox ~]$ ./hello_world
Hello World!
如果没有给出可执⾏程序的明确路径名,那么系统每次都会搜索⼀系列的⽬录,来查此可执⾏程序。
这个⽬录列表被存储在⼀个名为PATH 的环境变量中。这个 PATH 变量包含⼀个由冒号分隔开的⽬录列表。
如果我们的脚本位于此列表中任意⽬录下,那么不明确指定脚本⽂件路径也可以运⾏。
如果这个 PATH 变量不包含这个⽬录,我们能够⼿动添加。⽐如,在.bashrc ⽂件中包含下⾯这⼀⾏⽂本将~/bin加⼊到PATH变量:export PATH=~/bin:"$PATH"
注意这个命令是向PATH中增加新的⽬录项,等号左侧是PATH变量,右侧是新的值,其中$PATH是对PATH的展开(后⾯会讲)。
当做了这个修改之后,它会在每个新的终端会话中⽣效。为了把这个修改应⽤到当前的终端会话中,我们必须让 shell 重新读取这个
.bashrc ⽂件。
[me@linuxbox ~]$ . .bashrc
这个点(.)命令是 source 命令的同义词,⼀个 shell 内建命令,⽤来读取⼀个指定的 shell 命令⽂件,并把它看作是从键盘中输⼊的⼀样。
3 保存位置
~/bin⽬录是存放为个⼈所⽤脚本的好地⽅。
如果我们编写了⼀个脚本,系统中的每个⽤户都可以使⽤它,那么这个脚本的传统位置是/usr/local/bin。
系统管理员使⽤的脚本经常放到/usr/local/sbin⽬录下。
⼤多数情况下,本地⽀持的软件,不管是脚本还是编译过的程序,都应该放到/usr/local⽬录下,⽽不是在/bin或/usr/bin⽬录下。
4 格式技巧
为了减少输⼊,当在命令⾏中输⼊选项的时候,短选项更受欢迎,但是当书写脚本的时候,长选项能提供可读性。
当使⽤长命令的时候,通过把命令在⼏个⽂本⾏中展开,可以提⾼命令的可读性。
find playground \
\( \
-type f \
-not -perm 0600 \
-exec chmod 0600 ‘{}’ ‘;’ \
\) \
-or \
\( \
-type d \
-not -perm 0711 \
-exec chmod 0711 ‘{}’ ‘;’ \
\)
5 变量
当 shell 碰到⼀个变量的时候,它会⾃动地创建它。这不同于许多编程语⾔,它们中的变量在使⽤之前,必须显式的声明或是定义。
shell 不会在乎变量值的类型;它把它们都看作是字符串。
6 展开
6.1 命令展开
把⼀个命令的输出作为⼀个展开模式来使⽤:
[me@linuxbox ~]$ ls -l $(which cp)
-rwxr-xr-x 1 root root 71516 2007-12-05 08:58 /bin/cp
6.2 字符串展开
基本⽤法
所有的变量存储的都是字符串,我们在使⽤这些字符串时有时候需要进⾏简单的处理,⽐如只取⼀部分、去掉后缀名等等。下⾯的命令就是按照特定要求展开字符串:
var = /home/me/video/maopian.avi
命令格式功能
${var}完全展开字符串。
${#var}展开为字符串var的长度。
${var:offset}提取字符串var中第 offset 个字符到末尾的部分。
${var:offset:length}提取字符串var中第 offset 个字符起 length 个字符。
开头/末尾字符清除
命令格式功能
${var#pattern}从 var 字符串中清除开头⼀部分匹配 pattern的⽂本。清除最短的匹配结果
${var##pattern}清除最长的匹配结果
${var%pattern}清除的⽂本从 var 所包含字符串的末尾开始
${var%%pattern}清除的⽂本从 var所包含字符串的末尾开始
查替换。对 var 的内容执⾏查和替换操作。如果到了匹配通配符 pattern 的⽂本,则⽤ string 的内容替换它。/string 可能会省略掉,这样会导致删除匹配的⽂本。
命令格式功能
${var/pattern/string}只有第⼀个匹配项会被替换掉。
${var//pattern/string}所有的匹配项都会被替换掉。
${var/#pattern/string}要求匹配项出现在字符串的开头
${var/%pattern/string}要求匹配项出现在字符串的末尾
引⽤
有时候我们的字符串中包含了可展开的形式,但我们本意不想让这部分展开。通过引⽤,我们可以禁⽌⼀部分字符串的展开。
随着引⽤程度加强,越来越多的展开被禁⽌。如果需要禁⽌所有的展开,我们要使⽤单引号。
以下例⼦是⽆引⽤,双引号,和单引号的⽐较结果:
[me@linuxbox ~]$ echo text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER
text /home/ a b foo 4 me
[me@linuxbox ~]$ echo "text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER"
text ~/*.txt  {a,b} foo 4 me
[me@linuxbox ~]$ echo 'text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER'
text ~/*.txt  {a,b} $(echo foo) $((2+2)) $USER
7 函数
Shell 函数有两种语法形式:
function name {
commands
return
}
name () {
commands
return
}
通过在变量名之前加上单词 local,来定义局部变量。这就创建了⼀个只对其所在的 shell 函数起作⽤的变量。在这个 shell 函数之外,这个变量不再存在。
8 位置参数
shell 提供了⼀个称为位置参数的变量集合,这个集合包含了命令⾏中所有独⽴的单词。
#!/bin/bash
# posit-param: script to view command line parameters
echo "
Number of arguments: $#
\$0 = $0
\$1 = $1
\$2 = $2
\$3 = $3
\$4 = $4
"
输出结果:
[me@linuxbox ~]$ posit-param a b c d
Number of arguments: 4
$0 = /home/me/bin/posit-param
$1 = a
$2 = b
$3 = c
$4 = d
即使不带命令⾏参数,位置参数$0总会包含命令⾏中出现的第⼀个单词,也就是已执⾏程序的路径名。
实际上通过参数展开⽅式你可以访问的参数个数多于9个。只要指定⼀个⼤于9的数字,⽤花括号把该数字括起来就可以。例如${10}、 ${55}、${211}等等。
shell 还提供了⼀个名为$#,可以得到命令⾏参数个数。
正如位置参数被⽤来给 shell 脚本传递参数⼀样,它们也能够被⽤来给 shell 函数传递参数。
shell 提供了两种特殊的参数。他们⼆者都能扩展成完整的位置参数列表.
参数描述
$*展开成⼀个从1开始的位置参数列表。当它被⽤双引号引起来的时候,展开成⼀个由双引号引起来的字符串,包含了所有的位置参数,每个位置参数由 shell 变量 IFS 的第⼀个字符(默认为⼀个空格)分隔开。
$@展开成⼀个从1开始的位置参数列表。当它被⽤双引号引起来的时候,它把每⼀个位置参数展开成
⼀个由双引号引起来的分开的字符串。
“$@” 在⼤多数情况下是最有⽤的⽅
法,因为它保留了每⼀个位置参数的
完整性。
9 条件判断
if commands; then
commands
[elif commands; then
<]
[else
commands]
fi
经常与 if ⼀块使⽤的命令是 test。这个 test 命令执⾏各种各样的检查与⽐较。它有两种等价模式:
test expression
[ expression ]
⽬前的 bash 版本包括⼀个复合命令,作为加强的 test 命令替代物。它使⽤以下语法:
[[ expression ]]
这个[[ ]]命令⾮常相似于 test 命令(它⽀持所有的表达式),但是增加了⼀个重要的新的字符串表达式:
string1 =~ regex
如果 string1匹配扩展的正则表达式 regex,其返回值为真。
10 case语句
#!/bin/bash
# case4-2: test a character
read -n 1 -p "Type a character > "
echo
case $REPLY in
[[:upper:]])    echo "'$REPLY' is upper case." ;;&
[[:lower:]])    echo "'$REPLY' is lower case." ;;&
[[:alpha:]])    echo "'$REPLY' is alphabetic." ;;&
[[:digit:]])    echo "'$REPLY' is a digit." ;;&
[[:graph:]])    echo "'$REPLY' is a visible character." ;;&
[[:punct:]])    echo "'$REPLY' is a punctuation symbol." ;;&
[[:space:]])    echo "'$REPLY' is a whitespace character." ;;&
[[:xdigit:]])  echo "'$REPLY' is a hexadecimal digit." ;;&
esac
case 语句使⽤的模式和路径展开中使⽤的那些是⼀样的。模式以⼀个 “)” 为终⽌符。
还可以使⽤竖线字符作为分隔符,把多个模式结合起来。这就创建了⼀个 “或” 条件模式。
添加的 “;;&” 的语法允许 case 语句继续执⾏下⼀条测试,⽽不是简单地终⽌运⾏。
11 循环
while 语法举例
#!/bin/bash
# while-count: display a series of numbers
count=1
while [ $count -le 5 ]; do
echo $count
count=$((count + 1))
done
echo "Finished."
util 语法举例
#!/bin/bash
# until-count: display a series of numbers
count=1
until [ $count -gt 5 ]; do
echo $count
count=$((count + 1))
done
echo "Finished."
for 语法举例
第⼀种形式:
#!/bin/bash
for i in A B C D; do
echo $i;
done
第⼆种形式:
#!/bin/bash
# simple_counter : demo of C style for command
for (( i=0; i<5; i=i+1 )); do
echo $i
done

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。