为了进一步提高效率,逐步用 linux 替代 windows,如果不会编写 shell 脚本则无法发挥命令行的优势。
Shell脚本
- Shell 有些独特,因为它不仅是一个功能强大的命令行接口,也是一个脚本语言解释器。
- 一个 shell 脚本就是一个包含一系列命令的文件。shell 读取这个文件,然后执行 文件中的所有命令,就好像这些命令已经直接被输入到了命令行中一样。
基本步骤
为了成功地创建和运行一个 shell 脚本,我们需要做三件事情:
- 编写一个脚本。基本格式如下:
#!/bin/bash
# This is our first script.
echo ‘Hello World!‘
这个#!
字符序列是一种特殊的结构,被用来告诉操作系统将执行此脚本所用的解释器的名字。每个 shell 脚本都应该把这一文本行 作为它的第一行。
-
使脚本文件可执行。 使用
chmod
命令,对于脚本文件,有两个常见的权限设置;权限为755的脚本,则每个人都能执行,和权限为700的 脚本,只有文件所有者能够执行。注意为了能够执行脚本,脚本必须是可读的。 -
把脚本放置到 shell 能够找到的地方。为了能够运行此脚本,我们必须指定脚本文件明确的路径,如下:
[me@linuxbox ~]$ ./hello_world
Hello World!
如果没有给出可执行程序的明确路径名,那么系统每次都会搜索一系列的目录,来查找此可执行程序。这个目录列表被存储在一个名为 PATH 的环境变量中。这个 PATH 变量包含一个由冒号分隔开的目录列表。
- 如果我们的脚本位于此列表中任意目录下,那么不明确指定脚本文件路径也可以运行。
- 如果这个 PATH 变量不包含这个目录,我们能够手动添加。在.bashrc 文件中包含下面 这一行文本:
export PATH=~/bin:"$PATH"
当做了这个修改之后,它会在每个新的终端会话中生效。为了把这个修改应用到当前的终端会话中, 我们必须让 shell 重新读取这个 .bashrc 文件。
[me@linuxbox ~]$ . .bashrc
这个点(.)命令是 source 命令的同义词,一个 shell 内建命令,用来读取一个指定的 shell 命令文件, 并把它看作是从键盘中输入的一样。
保存位置
~/bin
目录是存放为个人所用脚本的好地方。
如果我们编写了一个脚本,系统中的每个用户都可以使用它,那么这个脚本的传统位置是 /usr/local/bin
。
系统管理员使用的脚本经常放到 /usr/local/sbin
目录下。
大多数情况下,本地支持的软件,不管是脚本还是编译过的程序,都应该放到 /usr/local
目录下, 而不是在 /bin
或 /usr/bin
目录下。
格式技巧
- 为了减少输入,当在命令行中输入选项的时候,短选项更受欢迎,但是当书写脚本的时候, 长选项能提供可读性。
- 当使用长命令的时候,通过把命令在几个文本行中展开,可以提高命令的可读性。
find playground \( -type f -not -perm 0600 -exec chmod 0600 ‘{}’ ‘;’ \) -or \( -type d -not -perm 0711 -exec chmod 0711 ‘{}’ ‘;’ \)
变量
- 当 shell 碰到一个变量的时候,它会自动地创建它。这不同于许多编程语言,它们中的变量在使用之前,必须显式的声明或是定义。
- shell 不会在乎变量值的类型;它把它们都看作是字符串。
展开
把一个命令的输出作为一个展开模式来使用:
[me@linuxbox ~]$ ls -l $(which cp)
-rwxr-xr-x 1 root root 71516 2007-12-05 08:58 /bin/cp
随着引用程度加强,越来越多的展开被禁止。如果需要禁止所有的展开,我们要使用单引号。以下例子是无引用,双引号,和单引号的比较结果:
[me@linuxbox ~]$ echo text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER
text /home/me/ls-output.txt 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
函数
Shell 函数有两种语法形式:
function name {
commands
return
}
和
name () {
commands
return
}
通过在变量名之前加上单词 local,来定义局部变量。这就创建了一个只对其所在的 shell 函数起作用的变量。在这个 shell 函数之外,这个变量不再存在。
位置参数
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开始的位置参数列表。当它被用双引号引起来的时候, 它把每一个位置参数展开成一个由双引号引起来的分开的字符串。 |
“$@” 在大多数情况下是最有用的方法,因为它保留了每一个位置参数的完整性。
条件判断
if commands; then
commands
[elif commands; then
commands...]
[else
commands]
fi
经常与 if 一块使用的命令是 test。这个 test 命令执行各种各样的检查与比较。 它有两种等价模式:
test expression
和
[ expression ]
目前的 bash 版本包括一个复合命令,作为加强的 test 命令替代物。它使用以下语法:
[[ expression ]]
这个[[ ]]命令非常 相似于 test 命令(它支持所有的表达式),但是增加了一个重要的新的字符串表达式:
string1 =~ regex
如果 string1匹配扩展的正则表达式 regex,其返回值为真。
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 语句继续执行下一条测试,而不是简单地终止运行。
循环
- 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