Links

15. 内建命令

内建命令 builtin 是包含在 Bash 工具集中的命令,又写作 built in。使用内建命令通常出于性能原因或是需要直接存取 shell 内部变量的考量。内建命令执行速度比外部命令的执行速度快,因为外部命令通常需要派生[^1]出一个单独的进程执行。

命令或 shell 本身启动或生成一个子进程用于执行任务的操作被称为派生 forking。新生成的进程被称作子进程,而派生出子进程的进程被称作父进程。当子进程在执行任务时,父进程也仍在运行。
需要注意的是,父进程可以获取子进程的进程ID,并能传递参数给子进程,但反之则不行。该机制会产生一些难以捉摸的问题。
样例 15-1. 脚本生成多个自身实例
#!/bin/bash
# spawn.sh
PIDS=$(pidof sh $0) # 脚本的多个实例的进程ID。
P_array=( $PIDS ) # 将进程ID放置到数组中(为什么?)。
echo $PIDS # 显示父进程和子进程的进程ID。
let "instances = $(#P_array[*]} - 1" # 元素数量减1。
# 为什么要减1?
echo "$instances instance(s) of this script running."
echo "[Hit Ctl-C to exit.]"; echo
sleep 1 # 闲置等待。
sh $0 # 再运行一次。
exit 0 # 这不是必须的;脚本永远不会执行到这里。
# 为什么不是必须的?
# 在键入 Ctl-C 退出脚本后,
#+ 是否所有生成的脚本实例都会终止?
# 如果是,为什么?
# 注意:
# ----
# 注意不要长时间运行这个脚本。
# 它最终会占用大量的系统资源。
# 你认为让脚本生成大量的自身实例
#+ 是否是一个可取的编写脚本的技巧?
# 为什么?
通常来说,Bash 的内建命令不会在脚本中通过派生子进程来执行。而在脚本中调用外部系统命令或筛选器通常需要派生子进程。

一些内建命令可能与系统命令重名,但是这些都是在 Bash 内部重新实现后的命令。例如 Bash 中的 echo 命令与系统命令 /bin/echo 的功能基本一致,但是它们本质上并不一样。
#!/bin/bash
echo "This line uses the \"echo\" builtin."
/bin/echo "This line uses the /bin/echo system command."
关键词 keyword 是保留使用的词汇、标记或运算符。关键词在 shell 中具有特殊意义,是 shell 语法的组成部分。例如 forwhiledo! 都是关键词。关键词与 内建命令 相似,它们都是硬编码到 Bash 中的。但是关键词本身不是命令,而是构成命令的子单位[2]

输入与输出 I/O

echo

打印一个表达式或变量到标准输出 stdout(参考 样例 4-1)。
echo Hello
echo $a
echo 命令需要 -e 选项来打印转义字符。参考 样例 5-2
通常情况下,每一个 echo 命令都会在最后打印一个终端换行符,但使用 -n 选项可以禁止这个行为。
echo 可通过管道被用于给一系列命令提供值。
if echo "$VAR" | grep -q txt # if [[ $VAR = *txt* ]]
then
echo "$VAR contains the substring sequence \"txt\""
fi
echo命令替换 结合可以用于给变量赋值。
a=`echo "HELLO" | tr A-Z a-z`
需要注意的是 ``echo command``` 会删除 command` 中生成的所有换行符。
$IFS(内部字段分隔符)变量通常包含 \n(换行符) 作为其空白字符集之一。因此,Bash将换行符把command的输出分割为echo命令参数。然后echo以空格分割,输出这些参数。
bash$ ls -l /usr/share/apps/kjezz/sounds
-rw-r--r-- 1 root root 1407 Nov 7 2000 reflect.au
-rw-r--r-- 1 root root 362 Nov 7 2000 seconds.au
bash$ echo `ls -l /usr/share/apps/kjezz/sounds`
total 40 -rw-r--r-- 1 root root 716 Nov 7 2000 reflect.au -rw-r--r-- 1 root root ...
那么,我们如何在echo输出的字符串中嵌入换行符呢?
# 嵌入一个换行符?
echo "Why doesn't this string \n split on two lines?"
# 不会分割。
# 让我们尝试一些其他的。
echo
echo $"A line of text containing
a linefeed."
# 打印不同的两行(嵌入换行符)。
# 但是,变量前缀的"$"符号真的必需吗?
echo
echo "This string splits
on two lines."
# 其实,并不需要"$"。
echo
echo "---------------"
echo
echo -n $"Another line of text containing
a linefeed."
# 打印不同的两行(嵌入换行符)。
# 即便是 -n 参数也无法取消这里的换行符。
echo
echo
echo "---------------"
echo
echo
# 然而,以下这行并没有实现预期的效果。
# 为什么?提示:赋值给一个变量。
string1=$"Yet another line of text containing
a linefeed (maybe)."
echo $string1
# 又一行包含换行的文本(也许)。
# ^^^
# 换行符变成了一个空格。
# 感谢Steve Parker指出。
note
该命令是一个shell内建程序,尽管二者行为类似,但是与/bin/echo不同。
bash$ type -a echo
echo is a shell builtin
echo is /bin/echo

printf

printf,即格式化打印,该命令是增强版的echo。它是C语言printf()库函数的有限变体,并且语法有些不同。
printf format-string... parameter...
这是/bin/printf/usr/bin/printf命令的Bash内置版本。请参见(系统命令的)printf man手册以获得更深入的内容。
note
老版本的Bash可能不支持printf
样例 15-2. printf在起作用
#!/bin/bash
# printf示例。
declare -r PI=3.14159265358979 # 只读变量,即常量。
declare -r DecimalConstant=31373
Message1="Greetings,"
Message2="Earthling."
echo
printf "Pi to 2 decimal places = %1.2f" $PI
echo
printf "Pi to 9 decimal places = %1.9f" $PI # 它甚至准确地舍入了。
printf "\n" # 打印一个换行符,
# 等价于'echo' . . .
printf "Constant = \t%d\n" $DecimalConstant # 插入一个缩进符(\t)。
printf "%s %s \n" $Message1 $Message2
echo
# ==========================================#
# C函数的模拟,sprint()。
# 加载带有格式化字符串的变量。
echo
Pi12=$(printf "%1.12f" $PI)
echo "Pi to 12 decimal places = $Pi12" # 舍入错误!
Msg=`printf "%s %s \n" $Message1 $Message2`
echo $Msg; echo $Msg
# 碰巧的是,"sprintf"函数现在可以
# 当做Bash的可加载模块进行访问,
# 但这不可移植。
exit 0
printf的一个实用场景是格式化错误信息。
E_BADDIR=85
var=nonexistent_directory
error()
{
printf "$@" >&2
# 格式化所传递的位置参数,并将其发送到标准错误(stderr)。
echo
exit $E_BADDIR
}
cd $var || error $"Can't cd to %s." "$var"
# 感谢S.C.
另请参阅样例 36-17

read

标准输入(stdin)“读取”变量值,即以交互式获取键盘的输入。-a参数可以使read可以获取数组变量(参见样例 27-6)。
样例 15-3. 变量赋值,使用read
#!/bin/bash
# “读取”变量。
echo -n "Enter the value of variable 'var1': "
# echo命令的-n参数用于防止换行。
read var1
# 注意在var1前没有'$'符号,因为它需要被设置。
echo "var1 = $var1"
echo
# 单个 'read' 语句可以设置多个变量。
echo -n "Enter the values of variables 'var2' and 'var3' "
echo -n "(separated by a space or tab): "
read var2 var3
echo "var2 = $var2 var3 = $var3"
# 如果你仅输入一个值,
# 那么其他变量将仍保持未设置的状态(null)。
exit 0
read命令没有相关联的变量接收输入时,它将把输入传递给指定的变量$REPLY
样例 15-4. 当read没有变量时会发生什么
#!/bin/bash
# read-novar.sh
echo
# -------------------------- #
echo -n "Enter a value: "
read var
echo "\"var\" = "$var""
# 这里所有都在预期之中。
# -------------------------- #
echo
# ------------------------------------------------------------------- #
echo -n "Enter another value: "
read # 没有提供给read的变量,那么...
# 输入给'read'的内容赋值给默认变量,$REPLY。
var="$REPLY"
echo "\"var\" = "$var""
# 这等价于第一个代码块。
# ------------------------------------------------------------------- #
echo
echo "========================="
echo
# 但是,这表明即使以常规方式 “读取” 变量后,
# $REPLY也可用。
# ================================================================= #
# 在一些情况下,你可能希望放弃第一次所读取的值。
# 在这种情况下,简单地忽略$REPLY变量即可。
{ # 代码块。
read # 第一行,需要被丢弃。
read line2 # 第二行,存入变量中。
} <$0
echo "Line 2 of this script is:"
echo "$line2" # # read-novar.sh
echo # #!/bin/bash 一行被丢弃。
# 另请参阅soundcard-on.sh脚本。
exit 0
通常,输入 \ 会忽略输入read的换行符。-r选项会使输入的 \ 进行字面转义。
样例 15-5. 多行输入至read
#!/bin/bash
echo
echo "Enter a string terminated by a \\, then press <ENTER>."
echo "Then, enter a second string (no \\ this time), and again press <ENTER>."
read var1 # 当读取$var1时,"\"忽略换行符。
# first line \
# second line
echo "var1 = $var1"
# var1 = first line second line
# 对于以 “\” 结尾的每一行,
# 你都会在下一行出现提示,以继续将字符输入到var1中。
echo; echo
echo "Enter another string terminated by a \\ , then press <ENTER>."
read -r var2 # -r选项会使 "\" 以字面读取。
# first line \
echo "var2 = $var2"
# var2 = first line \
# 数据输入以第一个 <ENTER> 终止。
echo
exit 0
read命令有一些有趣的选项,允许在不敲击ENTER键的情况下输出提示甚至读取击键。
# 在不敲击ENTER的情况下读取击键。
read -s -n1 -p "Hit a key " keypress
echo; echo "Keypress was "\"$keypress\""."
# -s 选项表示不要输出输入。
# -n N 选项表示仅接受N个字符输入。
# -p 选项表示在读取输入之前输出以下提示。
# 使用这些选项比较棘手,因为它们需要按照正确的顺序。
read-n选项同样支持检测箭头键以及其他某些不常用的键。
样例 15-6. 检测箭头键
#!/bin/bash
# arrow-detect.sh: 检测到箭头键,以及其他键。
# 感谢Sandro Magi告诉我怎么实现。
# --------------------------------------------
# 按键生成的字符代码。
arrowup='\[A'
arrowdown='\[B'
arrowrt='\[C'
arrowleft='\[D'
insert='\[2'
delete='\[3'
# --------------------------------------------
SUCCESS=0
OTHER=65
echo -n "Press a key... "
# 如果按下了上面未列出的键,可能还需要按ENTER键。
read -n3 key # 读取三个字符。
echo -n "$key" | grep "$arrowup" #检查是否检测到字符代码。
if [ "$?" -eq $SUCCESS ]
then
echo "Up-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowdown"
if [ "$?" -eq $SUCCESS ]
then
echo "Down-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowrt"
if [ "$?" -eq $SUCCESS ]
then
echo "Right-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowleft"
if [ "$?" -eq $SUCCESS ]
then
echo "Left-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$insert"
if [ "$?" -eq $SUCCESS ]
then
echo "\"Insert\" key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$delete"
if [ "$?" -eq $SUCCESS ]
then
echo "\"Delete\" key pressed."
exit $SUCCESS
fi
echo " Some other key pressed."
exit $OTHER
# ========================================= #
# Mark Alexander想出了上述脚本的简化版 (谢谢!)。
# 它消除了对grep的需要。
#!/bin/bash
uparrow=$'\x1b[A'
downarrow=$'\x1b[B'
leftarrow=$'\x1b[D'
rightarrow=$'\x1b[C'
read -s -n3 -p "Hit an arrow key: " x
case "$x" in
$uparrow)
echo "You pressed up-arrow"
;;
$downarrow)
echo "You pressed down-arrow"
;;
$leftarrow)
echo "You pressed left-arrow"
;;
$rightarrow)
echo "You pressed right-arrow"
;;
esac
exit $?
# ========================================= #
# Antonio Macchi有一个更简洁的替代方案。
#!/bin/bash
while true
do
read -sn1 a
test "$a" == `echo -en "\e"` || continue
read -sn1 a
test "$a" == "[" || continue
read -sn1 a
case "$a" in
A) echo "up";;
B) echo "down";;
C) echo "right";;
D) echo "left";;
esac
done
# ========================================= #
# 练习:
# --------
# 1) 添加对"Home"、"End"、"PgUp"和"PgDn"键的检测。
note
read-n选项会不检测ENTER(新行)键。
read-t选项允许定时输入(参阅样例 9-4样例 A-41)。
-u选项采用目标文件的文件描述符
read命令还可以从重定向标准输入(stdin)的文件中 “读取” 其变量值。如果文件包含多行,则仅将第一行分配给变量。如果read具有多个参数,则每个变量都会被分配一个连续的空格描述字符串。请注意!
样例 15-7. 使用文件重定向read
#!/bin/bash
read var1 <data-file
echo "var1 = $var1"
# var1设置为输入文件"data-file"的整个第一行
read var2 var3 <data-file
echo "var2 = $var2 var3 = $var3"
# 注意这里"read"的反直觉行为。
# 1) 倒回输入文件的开头。
# 2) 现在每个变量将设置为相应的字符串,
# 以空白符分割,而不是文本的一整行。
# 3) 最后一个变量获取该行的余数。
# 4) 如果要设置的变量比文件第一行的空格终止的字符串多,
# 则多余的变量保持为空。
echo "------------------------------------------------"
# 如何用循环解决上述问题:
while read line
do
echo "$line"
done <data-file
# 感谢Heiner Steven指出。
echo "------------------------------------------------"
# 如果您不希望默认值为空格,
# 请使用$IFS(内部字段分隔符变量)将输入行拆分后传递给read。
echo "List of all users:"
OIFS=$IFS; IFS=: # /etc/passwd使用":"作为字段分隔符。
while read name passwd uid gid fullname ignore
do
echo "$name ($fullname)"
done </etc/passwd # I/O重定向。
IFS=$OIFS # 恢复原先的$IFS。
# 这代码片段由Heiner Steven所写。
# 如果将$IFS变量设置在循环体中,
# 则无需将原始$IFS存储在临时变量中。
# 感谢Dim Segebart指出。
echo "------------------------------------------------"
echo "List of all users:"
while IFS=: read name passwd uid gid fullname ignore
do
echo "$name ($fullname)"
done </etc/passwd # I/O重定向。
echo
echo "\$IFS still $IFS"
exit 0
note
将输出管道传输read,再使用echo来设置变量会失败。然而,将输出管道传输给cat看起来奏效。
cat file1 file2 |
while read line
do
echo $line
done
然而,正如Bjön Eriksson展示的:
样例 15-8. 从管道中读取发生的问题
#!/bin/sh
# readpipe.sh
# 该样例由Bjon Eriksson提供。
### shopt -s lastpipe
last="(null)"
cat $0 |
while read line
do
echo "{$line}"
last=$line
done
echo
echo "++++++++++++++++++++++"
printf "\nAll done, last: $last\n" # 如果你取消第5行的注释,
# 那么这行的输出会改变。
# (Bash,大于等于4.2版本)
exit 0 # 代码的末尾。
# 脚本的 (部分) 输出如下。
# 'echo'的输出部分由括号括起来。
#############################################
./readpipe.sh
{#!/bin/sh}
{last="(null)"}
{cat $0 |}
{while read line}
{do}
{echo "{$line}"}
{last=$line}
{done}
{printf "nAll done, last: $lastn"}
All done, last: (null)
变量 (last) 设置在循环/子shell内,
但其值不会在循环外持续存在。
gendiff脚本(通常可以在许多Linux发行版上的/usr/bin目录下找到)将find的输出通过管道传输到while read结构中。
find $1 \( -name "*$2" -o -name ".*$2" \) -print |
while read f; do
. . .
可以将文本粘贴read的输入字段中(但不能多行!)。请参阅样例A-38

文件系统命令

cd

熟悉的cd跳转目录命令可以在脚本中使用,命令执行后将跳转至指定的目录中。
(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
[来自Alan Cox所写先前引用的样例]
cd-P(物理)选项用于忽略符号链接。
cd -将跳转至$OLDPWD,即之前的工作目录。
note
当带有两个正斜杠时,cd命令将不能正常工作。
bash$ cd //
bash$ pwd
//
当然,输出应该是 / 。这是命令行和脚本都存在的问题。

pwd

打印工作目录。该命令给出了用户(或者脚本)当前的目录(参阅样例 15-9)。其效果等同于读取内置变量$PWD的值。

pushd, popd, dirs

这个命令集是一种为工作目录添加书签的机制,通过一种有序的方式在目录间来回移动。将目录名推入堆栈用于跟踪。该命令选项允许对目录堆栈进行各种操作。
pushd dir-name将路径dir-name推送到目录堆栈上(堆栈的顶部),同时将当前工作目录更改为dir-name
popd从目录堆栈中删除(弹出)顶层目录路径名,同时将当前工作目录更改为现在位于堆栈顶层的目录。
dirs列出目录堆栈的内容(与$DIRSTACK变量进行比较)。成功执行的pushdpopd将自动调用dirs
需要对当前工作目录进行各种更改而不硬编码目录名更改的脚本可以很好地利用这些命令。请注意,隐式$DIRSTACK数组变量(可从脚本中访问)保存目录堆栈的内容。
样例 15-9. 更改当前的工作目录
#!/bin/bash
dir1=/usr/local
dir2=/var/spool
pushd $dir1
# 将自动执行"dirs"(将目录堆栈列表输出到标准输出)。
echo "Now in directory `pwd`." # 使用反引号括起来的'pwd'。
# 现在,在目录"dir1"中做一些事情。
pushd $dir2
echo "Now in directory `pwd`."
# 现在,在目录"dir2"中做一些事情。
echo "The top entry in the DIRSTACK array is $DIRSTACK."
popd
echo "Now back in directory `pwd`."
# 现在,在目录"dir1"中再做一些事情。
popd
echo "Now back in original working directory `pwd`."
exit 0
# 如果你不执行'popd' -- 然后退出该脚本会发生什么呢?
# 你会处在哪个目录下呢?为什么?

变量命令

let

let命令用于对变量执行算术运算。[3]在许多情况下,它充当expr的一个简洁版本。
样例 15-10. 让let来做算术运算
#!/bin/bash
echo
let a=11 # 等价于'a=11'
let a=a+5 # 等价于let "a = a + 5"
# (双引号和空格可以使其更易于阅读。)
echo "11 + 5 = $a" # 16
let "a <<= 3" # 等价于let "a = a << 3"
echo "\"\$a\" (=16) left-shifted 3 places = $a"
# 128
let "a /= 4" # 等价于let "a = a / 4"
echo "128 / 4 = $a" # 32
let "a -= 5" # 等价于let "a = a - 5"
echo "32 - 5 = $a" # 27
let "a *= 10" # 等价于let "a = a * 10"
echo "27 * 10 = $a" # 270
let "a %= 8" # 等价于let "a = a % 8"
echo "270 modulo 8 = $a (270 / 8 = 33, remainder $a)"
# 6
# "let"允许C风格的操作符吗?
# 是的,正如(( ... ))双括号结构可以。
let a++ # C风格(后置)增加。
echo "6++ = $a" # 6++ = 7
let a-- # C风格减少。
echo "7-- = $a" # 7-- = 6
# 当然,++a等也是允许的 . . .
echo
# 三元运算符。
# 参见上方代码,$a=6。
let "t = a<7?7:11" # True
echo $t # 7
let a++
let "t = a<7?7:11" # False
echo $t # 11
exit
note
在特定的上下文中,let命令可以返回一个惊人的退出状态
# Evgeniy Ivanov指出:
var=0
echo $? # 0
# 预期中。
let var++
echo $? # 1
# 命令执行成功,但为什么不是$?=0 ???
# 简直不可理喻!
let var++
echo $? # 0
# 预期中。
# 同样的 . . .
let var=0
echo $? # 1
# 命令执行成功,但为什么不是$?=0 ???
# 然而,正如Jeff Gorak指出,
# 这是"let"设计规范的一部分 . . .
# "如果最后一个参数值为0,let返回1;
# 否则let返回0。"['help let']

eval

eval arg1 [arg2] ... [argN]
组合表达式或表达式列表中的参数并对其求值。表达式中的任何变量都将被引申含义。最终结果是将字符串转换成命令
eval命令可用于从命令行或在脚本中生成代码。
bash$ command_string="ps ax"
bash$ process="ps ax"
bash$ eval "$command_string" | grep "$process"
26973 pts/3 R+ 0:00 grep --color ps ax
26974 pts/3 R+ 0:00 ps ax
每次调用eval都会强制对其参数重新转义
a='$b'
b='$c'
c=d
echo $a # $b
# 第一层。
eval echo $a # $c
# 第二层。
eval eval echo $a # d
# 第三层。
# 感谢E. Choroba.
样例 15-11. 展示eval的效果
#!/bin/bash
# 练习"eval" ...
y=`eval ls -l` # 类似于y=`ls -l`
echo $y # 但是删除了换行符,因为"echo"变量没有被括起来。
echo
echo "$y" # 当变量被括起来时,换行符仍旧保留。
echo; echo
y=`eval df` # 类似于y=`df`
echo $y # 但是删除了换行符。
# 当不保留LF时,可能更加容易来解析输出,
# 可以使用诸如"awk"的实用工具。
echo
echo "==========================================================="
echo
eval "`seq 3 | sed -e 's/.*/echo var&=ABCDEFGHIJ/'`"
# var1=ABCDEFGHIJ
# var2=ABCDEFGHIJ
# var3=ABCDEFGHIJ
echo
echo "==========================================================="
echo
# Now, showing how to do something useful with "eval" . . .
# 现在,展示如何使用"eval"做一些有用的事情 . . .
# (感谢您,E. Choroba!)
version=3.4 # 我们能在一个命令中把版本分成
# 主版本和小版本吗?
echo "version = $version"
eval major=${version/./;minor=} # 将version中的'.'替换为';minor='
# 该替换产生了'3; minor=4'
# 所以eval执行的结果为minor=4, major=3
echo Major: $major, minor: $minor # Major: 3, minor: 4
样例 15-12. 使用eval从变量中进行筛选
#!/bin/bash
# arr-choice.sh
# 向函数传递参数来
# 从一组变量中选择一个特定的变量。
arr0=( 10 11 12 13 14 15 )
arr1=( 20 21 22 23 24 25 )
arr2=( 30 31 32 33 34 35 )
# 0 1 2 3 4 5 序号 (从0开始标号)
choose_array ()
{
eval array_member=\${arr${array_number}[element_number]}
# ^ ^^^^^^^^^^^^
# 使用eval来构建变量名,
# 在这个特定的场景中,是数组名。
echo "Element $element_number of array $array_number is $array_member"
} # 可以重写函数,从而接受更多参数。
array_number=0 # 第一个数组。
element_number=3
choose_array # 13
array_number=2 # 第三个数组。
element_number=4
choose_array # 34
array_number=3 # 空数组 (array3没有分配空间)
element_number=4
choose_array # (null)
# 感谢Antonio Macchi指出。
样例 15-13. 输出命令行参数
#!/bin/bash
# echo-params.sh
# 请使用一些命令行参数来调用该脚本。
# 例如:
# sh echo-params.sh first second third fourth fifth
params=$# # 命令行参数的序号。
param=1 # 从第一个命令行参数开始。
while [ "$param" -le "$params" ]
do
echo -n "Command-line parameter "
echo -n \$$param # 仅给出变量的 *名称*。
# ^^^ # $1, $2, $3, 等等。
# 为什么?
# \$ 转义了第一个 "$"
# 所以它能够以文本输出。
# 并且 $param 解除了与 "$param" 的关联 . . .
# . . . 如预期一般。
echo -n " = "
eval echo \$$param # 给出变量的 *值*。
# ^^^^ ^^^ # "eval"强制将 \$$ 中的 *赋值号*
# 作为间接变量引用。
(( param ++ )) # 继续下一个。
done
exit $?
# =================================================
$ sh echo-params.sh first second third fourth fifth
Command-line parameter $1 = first
Command-line parameter $2 = second
Command-line parameter $3 = third
Command-line parameter $4 = fourth
Command-line parameter $5 = fifth
样例 15-14. 强制注销
#!/bin/bash
# 杀死ppp进程来强制注销。
# 当然,是拨号连接。
# 脚本需要被root用户运行。
SERPORT=ttyS3
# 取决于硬件甚至是内核版本,
# 您机器上的调制解调器端口可能不同 --
# /dev/ttyS1 或 /dev/ttyS2。
killppp="eval kill -9 `ps ax | awk '/ppp/ { print $1 }'`"
# -------- ppp的进程ID -------
$killppp # 这个变量现在是一条命令。
# 以下的操作必须由root用户完成。
chmod 666 /dev/$SERPORT # 恢复r+w权限,不然呢?
# 由于在ppp上执行SIGKILL更改了串行(serial)端口的权限,
# 我们需要将权限恢复到以前的状态。
rm /var/lock/LCK..$SERPORT # 删除串行(serial)端口锁文件。为什么?
exit $?
# 练习:
# ---------
# 1) 让脚本检查是否是root用户在调用它。
# 2) 在试图终止进程之前,检查要终止的进程是否正在运行。
# 3) 基于“fuser”编写此脚本的替代版本: