sed使用规则

      sed 是很有用(但常被遗忘)的 UNIX 流编辑器。sed是十分强大和小巧的文本流编辑器。使用sed 可以执行字符串替换、创建更大的 sed 脚本以及使用 sed 的附加、插入和更改行命令。在以批处理方式编辑文件或以有效方式创建 shell 脚本来修改现有文件方面,它是十分理想的工具。

sed 示例

sed 通过对输入数据执行任意数量用户指定的编辑操作(“命令”)来工作。sed 是基于行的,因此按顺序对每一行执行命令。然后,sed 将其结果写入标准输出 (stdout),它不修改任何输入文件。

让我们看一些示例。头几个会有些奇怪,因为我要用它们演示 sed 如何工作,而不是执行任何有用的任务。然而,如果您是 sed 新手,那么理解它们是十分重要的。下面是第一个示例:

$ sed -e 'd' /etc/services

如果输入该命令,将得不到任何输出。那么,发生了什么?在该例中,用一个编辑命令 'd' 调用 sed。sed 打开 /etc/services 文件,将一行读入其模式缓冲区,执行编辑命令(“删除行”),然后打印模式缓冲区(缓冲区已为空)。然后,它对后面的每一行重复这些步骤。这不会产生输出,因为 "d" 命令除去了模式缓冲区中的每一行!

在该例中,还有几件事要注意。首先,根本没有修改 /etc/services。这还是因为 sed 只读取在命令行指定的文件,将其用作输入 -- 它不试图修改该文件。第二件要注意的事是 sed 是面向行的。'd' 命令不是简单地告诉 sed 一下子删除所有输入数据。相反,sed 逐行将 /etc/services 的每一行读入其称为模式缓冲区的内部缓冲区。一旦将一行读入模式缓冲区,它就执行 'd' 命令,然后打印模式缓冲区的内容(在本例中没有内容)。我将在后面为您演示如何使用地址范围来控制将命令应用到哪些行 -- 但是,如果不使用地址,命令将应用到所有行。第三件要注意的事是括起 'd' 命令的单引号的用法。养成使用单引号来括起 sed 命令的习惯是个好注意,这样可以禁用 shell 扩展。

另一个 sed 示例

下面是使用 sed 从输出流除去 /etc/services 文件第一行的示例:

$ sed -e '1d' /etc/services | more

如您所见,除了前面有 '1' 之外,该命令与第一个 'd' 命令十分类似。如果您猜到 '1' 指的是第一行,那您就猜对了。与第一个示例中只使用 'd' 不同的是,这一次使用的 'd' 前面有一个可选的数字地址。通过使用地址,可以告诉 sed 只对某一或某些特定行进行编辑。

地址范围

现在,让我们看一下如何指定地址范围。在本例中,sed 将删除输出的第 1 到 10 行:

$ sed -e '1,10d' /etc/services | more

当用逗号将两个地址分开时,sed 将把后面的命令应用到从第一个地址开始、到第二个地址结束的范围。在本例中,将 'd' 命令应用到第 1 到 10 行(包括这两行)。所有其它行都被忽略。

带规则表达式的地址

现在演示一个更有用的示例。假设要查看 /etc/services 文件的内容,但是对查看其中包括的注释部分不感兴趣。如您所知,可以通过以 '#' 字符开头的行在 /etc/services 文件中放置注释。为了避免注释,我们希望 sed 删除以 '#' 开始的行。以下是具体做法:

$ sed -e '/^#/d' /etc/services | more

试一下该例,看看发生了什么。您将注意到,sed 成功完成了预期任务。现在,让我们分析发生的情况:

要理解 '/^#/d' 命令,首先需要对其剖析。首先,让我们除去 'd' -- 这是我们前面所使用的同一个删除行命令。新增加的是 '/^#/' 部分,它是一种新的规则表达式地址。规则表达式地址总是由斜杠括起。它们指定一种 模式,紧跟在规则表达式地址之后的命令将仅适用于正好与该特定模式匹配的行。因此,'/^#/' 是一个规则表达式。(规则表达式的有关规定可以参见本文前面的内容)

例如:

$ sed -e '/regexp/d' /path/to/my/test/file | more

这将导致 sed 删除任何匹配的行。

对比如下的命令:

$ sed -n -e '/regexp/p' /path/to/my/test/file | more

请注意新的 '-n' 选项,该选项告诉 sed 除非明确要求打印模式空间,否则不这样做。您还会注意到,我们用 'p' 命令替换了 'd' 命令,如您所猜想的那样,这明确要求 sed 打印模式空间。就这样,将只打印匹配部分。

有关地址的更多内容

目前为止,我们已经看到了行地址、行范围地址和 regexp 地址。但是,还有更多的可能。我们可以指定两个用逗号分开的规则表达式,sed 将与所有从匹配第一个规则表达式的第一行开始,到匹配第二个规则表达式的行结束(包括该行)的所有行匹配。

例如,以下命令将打印从包含 "BEGIN" 的行开始,并且以包含 "END" 的行结束的文本块:

$ sed -n -e '/BEGIN/,/END/p' /my/test/file | more

如果没发现 "BEGIN",那么将不打印数据。如果发现了 "BEGIN",但是在这之后的所有行中都没发现 "END",那么将打印所有后续行。发生这种情况是因为 sed 面向流的特性 -- 它不知道是否会出现 "END"。

C 源代码示例

如果只要打印 C 源文件中的 main() 函数,可输入:

$ sed -n -e '/main[[:space:]]*(/,/^)/p' sourcefile.c | more

该命令有两个规则表达式 '/main[[:space:]]*(/' 和 '/^}/',以及一个命令 'p'。第一个规则表达式将与后面依次跟有任意数量的空格或制表键以及开始圆括号的字符串 "main" 匹配。这应该与一般 ANSI C main() 声明的开始匹配。

在这个特别的规则表达式中,出现了 '[[:space:]]' 字符类。这只是一个特殊的关键字,它告诉 sed 与 TAB 或空格匹配。如果愿意的话,可以不输入 '[[:space:]]',而输入 '[',然后是空格字母,然后是 -V,然后再输入制表键字母和 ']' -- Control-V 告诉 bash 要插入“真正”的制表键,而不是执行命令扩展。使用 '[[:space:]]' 命令类(特别是在脚本中)会更清楚。

现在看一下第二个 regexp。'/^}' 将与任何出现在新行行首的 '}' 字符匹配。如果代码的格式很好,那么这将与 main() 函数的结束花括号匹配。如果格式不好,则不会正确匹配 -- 这是执行模式匹配任务的一件棘手之事。因为是处于 '-n' 安静方式,所以 'p' 命令还是完成其惯有任务,即明确告诉 sed 打印该行。试着对 C 源文件运行该命令 -- 它应该输出整个 main() { } 块,包括开始的 "main()" 和结束的 '}'。

替换

让我们看一下 sed 最有用的命令之一,替换命令。使用该命令,可以将特定字符串或匹配的规则表达式用另一个字符串替换。

下面是该命令最基本用法的示例:

$ sed -e 's/foo/bar/' myfile.txt

上面的命令将 myfile.txt 中每行第一次出现的 'foo'(如果有的话)用字符串 'bar' 替换,然后将该文件内容输出到标准输出。请注意,我说的是每行第一次出现,尽管这通常不是您想要的。在进行字符串替换时,通常想执行全局替换。也就是说,要替换每行中的所有出现,如下所示:

$ sed -e 's/foo/bar/g' myfile.txt

在最后一个斜杠之后附加的 'g' 选项告诉 sed 执行全局替换。

关于 's///' 替换命令,还有其它几件要了解的事。首先,它是一个命令,并且只是一个命令,在所有上例中都没有指定地址。这意味着,'s///' 还可以与地址一起使用来控制要将命令应用到哪些行,如下所示:

$ sed -e '1,10s/enchantment/entrapment/g' myfile2.txt

上例将导致用短语 'entrapment' 替换所有出现的短语 'enchantment',但是只在第一到第十行(包括这两行)上这样做。

$ sed -e '/^$/,/^END/s/hills/mountains/g' myfile3.txt

该例将用 'mountains' 替换 'hills',但是,只从空行开始,到以三个字符 'END' 开始的行结束(包括这两行)的文本块上这样做。

关于 's///' 命令的另一个妙处是 '/' 分隔符有许多替换选项。如果正在执行字符串替换,并且规则表达式或替换字符串中有许多斜杠,则可以通过在 's' 之后指定一个不同的字符来更改分隔符。例如,下例将把所有出现的 /usr/local 替换成 /usr:

$ sed -e 's:/usr/local:/usr:g' mylist.txt

在该例中,使用冒号作为分隔符。如果需要在规则表达式中指定分隔符字符,可以在它前面加入反斜杠。

规则表达式混乱

目前为止,我们只执行了简单的字符串替换。虽然这很方便,但是我们还可以匹配规则表达式。例如,以下 sed 命令将匹配从 '<' 开始、到 '>' 结束、并且在其中包含任意数量字符的短语。下例将删除该短语(用空字符串替换):

$ sed -e 's/<.*>//g' myfile.html

这是要从文件除去 HTML 标记的第一个很好的 sed 脚本尝试,但是由于规则表达式的特有规则,它不会很好地工作。原因何在?当 sed 试图在行中匹配规则表达式时,它要在行中查找最长的匹配。在我的前一篇 sed 文章中,这不成问题,因为我们使用的是 'd' 和 'p' 命令,这些命令总要删除或打印整行。但是,在使用 's///' 命令时,确实有很大不同,因为规则表达式匹配的整个部分将被目标字符串替换,或者,在本例中,被删除。这意味着,上例将把下行:

This is what I meant.

变成:meant.

我们要的不是这个,而是:This is what I meant.

幸运的是,有一种简便方法来纠正该问题。我们不输入“'<' 字符后面跟有一些字符并以 '>' 字符结束”的规则表达式,而只需输入一个“'<' 字符后面跟有任意数量非 '>' 字符并以 '>' 字符结束”的规则表达式。这将与最短、而不是最长的可能性匹配。新命令如下:

            $ sed -e 's/<[^>]*>//g' myfile.html

在上例中,'[^>]' 指定“非 '>'”字符,其后的 '*' 完成该表达式以表示“零或多个非 '>' 字符”。对几个 html 文件测试该命令,将它们管道输出到 "more",然后仔细查看其结果。

更多字符匹配

'[ ]' 规则表达式语法还有一些附加选项。要指定字符范围,只要字符不在第一个或最后一个位置,就可以使用 '-',如下所示:

            '[a-x]*'

这将匹配零或多个全部为 'a'、'b'、'c'...'v'、'w'、'x' 的字符。另外,可以使用 '[:space:]' 字符类来匹配空格(字符类的相关信息可以参见本文前面部分内容)。

高级替换功能

我们已经看到如何执行简单甚至有些复杂的直接替换,但是 sed 还可以做更多的事。实际上可以引用匹配规则表达式的部分或全部,并使用这些部分来构造替换字符串。作为示例,假设您正在回复一条消息。下例将在每一行前面加上短语 "ralph said: ":

$ sed -e 's/.*/ralph said: &/' origmsg.txt

输出如下:

ralph said: Hiya Jim, ralph said: ralph said:

I sure like this sed stuff! ralph said:

该例的替换字符串中使用了 '&' 字符,该字符告诉 sed 插入整个匹配的规则表达式。因此,可以将与 '.*' 匹配的任何内容(行中的零或多个字符的最大组或整行)插入到替换字符串中的任何位置,甚至多次插入。这非常好,但 sed 甚至更强大。

那些极好的带反斜杠的圆括号

's///' 命令甚至比 '&' 更好,它允许我们在规则表达式中定义区域,然后可以在替换字符串中引用这些特定区域。作为示例,假设有一个包含以下文本的文件:

bar oni eeny meeny miny larry curly moe jimmy the weasel

现在假设要编写一个 sed 脚本,该脚本将把 "eeny meeny miny" 替换成 "Victor eeny-meeny Von miny" 等等。要这样做,首先要编写一个由空格分隔并与三个字符串匹配的规则表达式:

'.* .* .*'

现在,将在其中每个感兴趣的区域两边插入带反斜杠的圆括号来定义区域:

'\\(.*\\) \\(.*\\) \\(.*\\)'

除了要定义三个可在替换字符串中引用的逻辑区域以外,该规则表达式的工作原理将与第一个规则表达式相同。下面是最终脚本:

$ sed -e 's/\\(.*\\) \\(.*\\) \\(.*\\)/Victor \\1-\\2 Von \\3/' myfile.txt

如您所见,通过输入 '\\x'(其中,x 是从 1 开始的区域号)来引用每个由圆括号定界的区域。输入如下:

Victor foo-bar Von oni Victor eeny-meeny Von miny Victor larry-curly Von moe Victor jimmy-the Von weasel

随着对 sed 越来越熟悉,您可以花最小力气来进行相当强大的文本处理。您可能想如何使用熟悉的脚本语言来处理这种问题 -- 能用一行代码轻易实现这样的解决方案吗?

组合使用

在开始创建更复杂的 sed 脚本时,需要有输入多个命令的能力。有几种方法这样做。首先,可以在命令之间使用分号。例如,以下命令系列使用 '=' 命令和 'p' 命令,'=' 命令告诉 sed 打印行号,'p' 命令明确告诉 sed 打印该行(因为处于 '-n' 模式)。

$ sed -n -e '=;p' myfile.txt

无论什么时候指定了两个或更多命令,都按顺序将每个命令应用到文件的每一行。在上例中,首先将 '=' 命令应用到第 1 行,然后应用 'p' 命令。接着,sed 继续处理第 2 行,并重复该过程。虽然分号很方便,但是在某些场合下,它不能正常工作。另一种替换方法是使用两个 -e 选项来指定两个不同的命令:

$ sed -n -e '=' -e 'p' myfile.txt

然而,在使用更为复杂的附加和插入命令时,甚至多个 '-e' 选项也不能帮我们的忙。对于复杂的多行脚本,最好的方法是将命令放入一个单独的文件中。然后,用 -f 选项引用该脚本文件:

$ sed -n -f mycommands.sed myfile.txt

这种方法虽然可能不太方便,但总是管用。

一个地址的多个命令

有时,可能要指定应用到一个地址的多个命令。这在执行许多 's///' 以变换源文件中的字和语法时特别方便。要对一个地址执行多个命令,可在文件中输入 sed 命令,然后使用 '{ }' 字符将这些命令分组,如下所示:

1,20{    s/[Ll]inux/GNU\\/Linux/g     s/samba/Samba/g        s/posix/POSIX/g }

上例将把三个替换命令应用到第 1 行到第 20 行(包括这两行)。还可以使用规则表达式地址或者二者的组合:

1,/^END/{         s/[Ll]inux/GNU\\/Linux/g         s/samba/Samba/g         s/posix/POSIX/g        p }

该例将把 '{ }' 之间的所有命令应用到从第 1 行开始,到以字母 "END" 开始的行结束(如果在源文件中没发现 "END",则到文件结束)的所有行。

附加、插入和更改行

既然在单独的文件中编写 sed 脚本,我们可以利用附加、插入和更改行命令。这些命令将在当前行之后插入一行,在当前行之前插入一行,或者替换模式空间中的当前行。它们也可以用来将多行插入到输出。插入行命令用法如下:

i\\ This line will be inserted before each line

如果不为该命令指定地址,那么它将应用到每一行,并产生如下的输出:

            This line will be inserted before each line line 1 here

            This line will be inserted before each line line 2 here

            This line will be inserted before each line line 3 here

            This line will be inserted before each line line 4 here

如果要在当前行之前插入多行,可以通过在前一行之后附加一个反斜杠来添加附加行,如下所示:

            i\\ insert this line\\ and this one\\ and this one\\ and, uh, this one too.

附加命令的用法与之类似,但是它将把一行或多行插入到模式空间中的当前行之后。其用法如下:

            a\\ insert this line after each line.  Thanks! :)

另一方面,“更改行”命令将实际替换模式空间中的当前行,其用法如下:

            c\\ You're history, original line! Muhahaha!

因为附加、插入和更改行命令需要在多行输入,所以将把它们输入到一个文本 sed 脚本中,然后通过使用 '-f' 选项告诉 sed 执行它们。使用其它方法将命令传递给 sed 会出现问题。

使用 sed 的几个示例

这些示例不仅演示 sed 的能力,而且还做一些真正巧妙(和方便)的事。例如,在本文的后半部,将为您演示如何设计一个 sed 脚本来将 .QIF 文件从 Intuit 的 Quicken 金融程序转换成具有良好格式的文本文件。在那样做之前,我们将看一下不怎么复杂但却很有用的 sed 脚本。

l        文本转换

第一个实际脚本将 UNIX 风格的文本转换成 DOS/Windows 格式。您可能知道,基于 DOS/Windows 的文本文件在每一行末尾有一个 CR(回车)和 LF(换行),而 UNIX 文本只有一个换行。有时可能需要将某些 UNIX 文本移至 Windows 系统,该脚本将为您执行必需的格式转换。

            $ sed -e 's/$/\\r/' myunix.txt > mydos.txt

     在该脚本中,'$' 规则表达式将与行的末尾匹配,而 '\\r' 告诉 sed 在其之前插入一个回车。在换行之前插入回车,立即,每一行就以 CR/LF 结束。请注意,仅当使用 GNU sed 3.02.80 或以后的版本时,才会用 CR 替换 '\\r'。如果还没有安装 GNU sed 3.02.80,请在我的第一篇 sed 文章中查看如何这样做的说明。

我已记不清有多少次在下载一些示例脚本或 C 代码之后,却发现它是 DOS/Windows 格式。虽然很多程序不在乎 DOS/Windows 格式的 CR/LF 文本文件,但是有几个程序却在乎 -- 最著名的是 bash,只要一遇到回车,它就会出问题。以下 sed 调用将把 DOS/Windows 格式的文本转换成可信赖的 UNIX 格式:

            $ sed -e 's/.$//' mydos.txt > myunix.txt

     该脚本的工作原理很简单:替代规则表达式与一行的最末字符匹配,而该字符恰好就是回车。我们用空字符替换它,从而将其从输出中彻底删除。如果使用该脚本并注意到已经删除了输出中每行的最末字符,那么,您就指定了已经是 UNIX 格式的文本文件。也就没必要那样做了!

l        反转行

下面是另一个方便的小脚本。与大多数 Linux 发行版中包括的 "tac" 命令一样,该脚本将反转文件中行的次序。"tac" 这个名称可能会给人以误导,因为 "tac" 不反转行中字符的位置(左和右),而是反转文件中行的位置(上和下)。用 "tac" 处理以下文件:

            foo bar oni

     ....将产生以下输出:

oni bar foo

     可以用以下 sed 脚本达到相同目的:

            $ sed -e '1!G;h;$!d' forward.txt > backward.txt

     如果登录到恰巧没有 "tac" 命令的 FreeBSD 系统,将发现该 sed 脚本很有用。虽然方便,但最好还是知道该脚本为什么那样做。让我们对它进行讨论。

反转解释:首先,该脚本包含三个由分号隔开的单独 sed 命令:'1!G'、'h' 和 '$!d'。现在,需要好好理解用于第一个和第三个命令的地址。如果第一个命令是 '1G',则 'G' 命令将只应用第一行。然而,还有一个 '!' 字符 -- 该 '!' 字符忽略该地址,即,'G' 命令将应用到除第一行之外的所有行。'$!d' 命令与之类似。如果命令是 '$d',则将只把 'd' 命令应用到文件中的最后一行('$' 地址是指定最后一行的简单方式)。然而,有了 '!' 之后,'$!d' 将把 'd' 命令应用到除最后一行之外的所有行。现在,我们所要理解的是这些命令本身做什么。

当对上面的文本文件执行反转脚本时,首先执行的命令是 'h'。该命令告诉 sed 将模式空间(保存正在处理的当前行的缓冲区)的内容复制到保留空间(临时缓冲区)。然后,执行 'd' 命令,该命令从模式空间中删除 "foo",以便在对这一行执行完所有命令之后不打印它。

现在,第二行。在将 "bar" 读入模式空间之后,执行 'G' 命令,该命令将保留空间的内容 ("foo\\n") 附加到模式空间 ("bar\\n"),使模式空间的内容为 "bar\\n\\foo\\n"。'h' 命令将该内容放回保留空间保护起来,然后,'d' 从模式空间删除该行,以便不打印它。

对于最后的 "oni" 行,除了不删除模式空间的内容(由于 'd' 之前的 '$!')以及将模式空间的内容(三行)打印到标准输出之外,重复同样的步骤。

linux常用脚本和函数

l        #查找当前目录中是否存在指定目录,若不存在,则创建之

function mkdir_1

{

                    if test ! -d $1

                    then

                           mkdir $1

                    fi

}

l        #将指定文件中的"prefix = .*"串替换为"prefix=\\/home\\/gnome-unicore-install2\\/usr/"

#可以用来作为sed用法的参考

function modify_prefix

{

            chmod +w $1

            cp $1 $1.bak

            sed 's/prefix = .*/prefix=\\/home\\/gnome-unicore-install2\\/usr/g' $1.bak > $1

            rm $1.bak

}

l        #将指定文件中的"^LDFLAGS =.*"串替换为"LDFLAGS = -rdynamic -lgdk_pixbuf -lgtk -lgdk -lgmodule -lglib -ldl -lXext -lX11 -lm"

#change_gnome-config FILENAME

function change_gnome-config

{

                    cp $1 $1.bak

                   sed 's/^LDFLAGS =.*/LDFLAGS = -rdynamic -lgdk_pixbuf -lgtk -lgdk -lgmodule -lglib -ldl -lXext -lX11 -lm /g' $1.bak> $1    

                   rm $1.bak

}

l        #删除指定文件的含有指定字符的行

#格式:delete_line filename "word_contain"

function delete_line

{

                    chmod +w $1

                    cp $1 $1.bak

                    cat $1.bak | grep -v -e "$2" >$1      

}

l        #用途:删除文件中包含line1或(和?)line2的行

#格式:delete_line filename line1 line2

function delete_line_no

{

            chmod +w $1

            cp $1 $1.bak

            sed  $2,$3'd' $1.bak>$1

            rm $1.bak

}

l        #用途:在LINE_NO指定的行插入字符串CONTENT

#可以用来作为sed用法的参考

#格式: add_line FILENAME LINE_NO CONTENT

function add_line

{

                    chmod +w $1

            cp $1 $1.bak

            sed -e $2 'i\\' "$3" '' $1.bak > $1

            rm $1.bak

}

l        #用途:检查含有"PC24"代码的程序并打印出来

#格式: check_PC24 //after installation

function check_PC24

{

                  echo "now comes the PC24 checking..."

                  . $COMMAND_UNICORE/shell/shell_PC24 >& /dev/null

                  if test -s $COMMAND_UNICORE/PC24_result

                then :

              echo "The following file contains PC24 problems: $COMMAND_UNICORE/PC24_result "

                else

                     echo "No PC24 problem found"

                  fi

}

l        #打印标题

displayheader() {

                  echo "   *****************************************"

                  echo "   *         IeeeCC754 testing tool           *"

                  echo "   *****************************************"

                  echo " "

}

l        #打印一个菜单的做法

displayplatformmenu() {

           #clear the screen

           clear

           displayheader

           echo "   a) SunSparc "

           echo "   b) IntelPentium "

           echo "   c) AMD "

           echo "   d) Unicore32 "

           echo "   e) Unicore32(with FP2001) "

           echo " "

           echo  -n "   select a Platform > "

}

l        #接收一个菜单输入

displayplatformmenu

read answer

case ${answer} in

           a) TARGET="BasicOp";;

           b) TARGET="Conversion";;

           *) badchoice;;

esac

l        #查找当前目录下是否存在file_name文件

#可以用来作为if用法的参考

detectfile_name() {

                  if [ ! -f file_name ]

                  then

                       echo "Error: file_name does not exist.  Please check"

                exit 1;

                  else

                       echo "OK,the directy is exist"

                  fi

}

l        #将参数指定的一个或多个目录项以及其下的多级子目录下的所有文件名和目录名转换为小写。

cvitem()

{

echo "mv $1 `dirname $1`/`basename $1 | tr \\

'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`"

}

[ $# = 0 ] && { echo "Usage: lcdir item1 item2 ..."; exit; }

for item in $*     #可以用来作为for用法的参考

do

[ "`dirname $item`" != "`basename $item`" ] && {

[ -d $item ] &&

{

for subitem in `ls $item`

do

cvlc $item/$subitem

done

}

cvitem $item

}

done

l        #一个login的例子

if ($?path) then

set path=($HOME/bin $path)

else

set path=($HOME/bin /usr/bin .)

endif

if ( ! $ {?DT} ); then

stty dec new

tset -I -Q

endif

set mail=/usr/spool/mail/$USER

l        #关于if使用的几个例子

n        #执行一个命令或程序之前,先检查该命令是否存在,然後才执行

if [ -x /sbin/quotaon ] ; then

echo "Turning on Quota for root filesystem"

/sbin/quotaon /

fi

n        #得到Hostname

#!/bin/sh

if [ -f /etc/HOSTNAME ] ; then

HOSTNAME=`cat /etc/HOSTNAME`

else

HOSTNAME=localhost

fi

n        #如果某个设定档允许有好几个位置的话,例如crontab,可利用if then elif fi来找寻

if [ -f /etc/crontab ] ; then  #[ -f /etc/crontab ]等价于test -f /etc/crontab

CRONTAB="/etc/crontab"

elif [ -f /var/spool/cron/crontabs/root ] ; then

CRONTAB="/var/spool/cron/crontabs/root"

elif [ -f /var/cron/tabs/root ] ; then

CRONTAB="/var/cron/tabs/root"

fi

export CRONTAB

n        #利用uname来判断目前系统,并分别做各系统状况不同的事。

SYSTEM=`uname -s`

if [ $SYSTEM = "Linux" ] ; then

echo "Linux"

elif [ $SYSTEM = "FreeBSD" ] ; then

echo "FreeBSD"

elif [ $SYSTEM = "Solaris" ] ; then

echo "Solaris"

else

echo "What?"

fi

l        #关于while使用的几个例子

n        #无条件循环

while : ; do

echo "do something forever here"

sleep 5

done

linux常用命令

以下只说明各指令的基本用法, 若需详细说明, 请用 man 去读详细的 manual.

关于文件/目录处理的指令:

1. ls

这是最基本的文件指令。 ls 的意义为 "list",也就是将某一个目录或是某一个文件的内容显示出来。

如果你在下 ls 指令后面没有跟任何的文件名,它将会显示出目前目录中所有文件。

也可以在 ls 后面加上所要察看的目录名称或文件的名称,如:

% ls /home2/X11R5

% ls first

ls 有一些特别的参数,可以给予使用者更多有关的信息,如下:

-a : 在 UNIX 中若一个目录或文件名字的第一个字符为 "." , 则使用 ls

将不会显示出这个文件的名字,我们称此类文件为隐藏文件。如果我们要察看这类文件,则必须加上参数 -a 。

-l : 这个参数代表使用 ls 的长( long )格式,可以显示更多的信息,如文件存取权,文件拥有者( owner ),文件大小,文件最后更新日期,甚而 symbolic link 的文件是 link 到那一个文件等等。例如:

% ls -l

drwx--x--x 2 jjtseng 512 Aug 8 05:08 18

drwx--x--x 2 jjtseng 512 Aug 8 22:00 19

-rw------- 1 jjtseng 566 Aug 8 05:28 makefile

2. cp    

cp 这个指令的意义是复制("COPY") , 也就是将一个或多个文件复制成另一个文件或者是将其复制到另一个目录去。

cp 的用法如下:

cp f1 f2 : 将文件名为 f1 的文件复制一份为文件名为 f2 的文件。

cp f1 f2 f3 ... dir : 将文件 f1 f2 f3 ... 都以相同的文件名复制一份放到目录 dir 里面。

cp -r dir1 dir2 : 将 dir1 的全部内容全部复制到 dir2 里面。

cp 也有一些参数,如下:

-i : 此参数是当已经有文件名为 f2 的文件时,若迳自使用 cp 将会将原来 f2的内容覆盖,因此在要覆盖之前先询问使用者。如使用者的回答是y(yes)才执行复制的动作。

-r : 此参数是用来做递回复制用,可递归的将整个目录都复制到另一个目录中。

3. mv

mv 的意义为 move , 主要是将一文件改名或移动到另一个目录。与cp类似,它也有三种格式:

mv f1 f2 : 将文件名为 f1 的文件变更成文件名为 f2 的文件。

mv dir1 dir2 : 将文件名为 dir1 的目录变更成文件名为 dir2 的目录。

mv f1 f2 f3 ... dir : 将文件 f1 f2 f3 ... 都移至目录 dir 里面。

mv 的参数有两个,-f 和 -i , 其中 -i 的意义与 cp 中的相同,均是 interactive的意思。而 -f 为强迫( force ) , 就是不管有没有同名的文件,反正就是要执行,所有其他的参数遇到 -f 均会失效。

4. rm

rm 的意义是 remove ,也就是用来删除一个文件的指令。需要注意的是,在 UNIX 中一个被删除的文件除非是系统恰好做了备份,否则是无法像 DOS 里面一样还能够恢复的。所以在做 rm 动作的时候使用者应该要特别小心。

rm 的格式如下:

rm f1 f2 f3 .....

rm 的参数比较常用的有几个: -f , -i , 与 -r

-f : 将会使得系统在删除时,不提出任何警告讯息。

-i : 在删除文件之前均会询问是否真要删除。

-r : 递归的删除,可用于删除目录。

5. mkdir

mkdir 是一个让使用者建立一个目录的指令。你可以在一个目录底下使用 mkdir 建立一个子目录,使用的方法如下:

mkdir dirname1 [ dirname2 ... ]

8. pwd

pwd 会将当前目录的路径( path )显示出来

9. cat/more/less

以上三个指令均为察看文件内容的指令。cat 的意义是concatenate,实际就是把文件的内容显示出来的意思。

cat 有许多奇怪的参数,较常为人所使用的是 -n 参数,也就是把显示出来的内容加上行号。

cat 的用法如下:

cat [-n] :自标准输入读取内容,可以用 pipe 将别的指令的输出转向给 cat 。

cat [-n] filename : 将 filename 的内容读进来,显示在标准输出上。

问题在於 cat 它是不会停下来的,因此并不好用( 试想如果一个萤幕二十四行,而一个文件四百行,cat 一出来将会噼里啪啦不断的卷上去,使用者很难据此得到他们所需的信息。) 所以常常么使用 more 和less来帮助。

more 可以让文件根据控制台的模式一页页的显示出来,再根据使用者的要求换页或者换行。如果使用者要在某一个文件中搜寻一个特定的字符串,则按 / 然后跟着打所要搜寻的单字即可进行搜寻。

more 的使用法如下:

more filename

如果你在使用中觉得已经看到了所要看的部份,可以按'q'离开 more 的使用。

less 的用法与 more 极类似,原先它就是为了弥补 more 只能往前方卷页的缺点而设计。

Less 的用法如下:

less filename

它与 more 不同的是它可以按 y 来往上卷一行,并且可以用"?"来往回搜寻你所要找的字符。

10. chmod

chmod用来改变文件存取模式( change mode ) 。在linux中,一个文件有可读(r)可写(w)可执行(x)三种模式,分别针对该文件的拥有者( onwer )、同组用户( group member )(可以 ls -lg来观看某一文件的所属的 group ),以及其他人( other )。

一个文件如果改成可执行模式则系统就将其视为一个可执行文件,而一个目录的可执行模式代表使用者有进入该目录之权利。

chmod使用方式如下:

chmod [ -fR ] mode filename ...

其参数的意义如下:

-f Force. chmod 不会理会失败的动作。

-R Recurive. 会将目录下所有子目录及文件改为你所要改成的模式。

关于 Process 处理的指令:

1. ps

ps 是用来显示目前你的 process 或系统 processes 的状况。

其选项说明如下:

-a 列出包括其他 users 的 process 状况。

-u 显示 user - oriented 的 process 状况 。

-x 显示包括没有 terminal 控制的 process 状况 。

-w 使用较宽的显示模式来显示 process 状况 。

我们可以经由 ps 取得目前 processes 的状况,如 pid , running state 等。

2. kill

kill 指令的用途是送一个 signal 给某一个 process 。因为大部份送的都是用来杀掉 process 的 SIGKILL 或 SIGHUP,因此称为kill 。

kill 的用法为:

kill [ -SIGNAL ] pid ...

kill -l

SIGNAL 为一个 singal 的数字,从 0 到 31 ,其中 9 是 SIGKILL ,也就是一般用来杀掉一些无法正常 terminate 的信号。

也可以用 kill -l 来察看可代替 signal 号码的数字。

关于字符串处理的指令:

1. echo

echo 是用来显示一字符串在标准输出上。echo -n 则表示当显示完之后不执行换行操作。

2. grep

grep 为一过滤器,它可自一个或多个文件中过滤出具有某个字符串的行,或是从标准输入过滤出具有某个字符串的行。

grep的用法如下:

grep [-nv] match_pattern file1 file2 ....

-n 把所找到的行在行前加上行号列出

-v 把不包含 match_pattern 的行列出

match_pattern 所要搜寻的字符串

-f 以 pattern_file 存放所要搜寻的字符串

联机查询的指令:

1. man

man 是手册 ( manual ) 的意思。 UNIX 提供线上辅助( on-line help )的功能,man 就是用来让使用者在使用时查询指令、系统调用、标准库函数、各种表格等的使用所用的。

man 的用法如下:

man [-M path] [[section] title ] .....

man [-M path] -k keyword ...

-M path man 所需要的 manual database 的路径。我们也可以用设定环境变数 MANPATH 的方式来取代 -M 选项。

title 这是所要查询的目标。

section 用一个数字表示 manual 的分类,通常 1 代表可执行指令,2 代表系统调用( system call ) ,3 代表标准库函数,等等。

man 在 UNIX 上是一项非常重要的指令,我们在这里所描述的用法仅仅只是一个大家比较常用的用法以及简单的说明,真正详细的用法与说明还是要通过使用 man 来得到。

2. who

who 指令是用来查询目前有那些人在线上。

3. info

      info的查询与man类似。

网络运用指令:

1. telnet

telnet 是一个提供 user 通过网络连到 remote host。

telnet 的 格式如下:

telnet [ hostname | ip-address ] [ port ]

hostname 为一个像 ccsun1 或是 ccsun1.cc.nctu.edu.tw 的 name address,ip-address 则为一个由四个小于 255 的数字组成的 ip address ,

2. ftp

ftp 的意义是 File Transfer Program ,是一个很常用的在网路文件传输的软件。

ftp 的格式如下:

ftp [ hostname | ip-address ]

其中 hostname | ip-address 的意义跟 telnet 中的相同。

在进入 ftp 之后,如果与 remote host 连接上了,它将会询问你 username 与密码,如果输入对了就可以开始进行文件传输。

在 ftp 中有许多的命令,这里仅列出较常用的 cd , lcd , mkdir , put , mput , get , mget , binary , ascii , prompt , help 与 quit 的使用方式。

ascii 将传输模式设为 ascii 模式。通常用於传送文字文件。

binary 将传输模式设为 binary 模式,通常用于传送执行文件,压缩文件与影像文件等。

cd remote-directory 将 remote host 上的工作目录改变。

lcd [ directory ] 更改 local host 的工作目录。

ls [ remote-directory ] [ local-file ] 列出 remote host 上的文件。

get remote-file [ local-file ] 取得登陆机的文件。

mget remote-files 可使用通用字符一次取得多个文件。

put local-file [ remote-file] 将 local host 的文件送到 remote host。

mput local-files 可使用通用字符一次将多个文件放到 remote host 上。

help [ command ] 线上辅助指令。

mkdir directory-name 在 remote host 新建一个目录。

prompt 更改交谈模式,若为 on 则在 mput 与 mget 时每做一个文件传输时均会询问。

Exit/quit离开ftp

3.rlogin命令

rlogin 是“remote login”(远程登录)的缩写。该命令与telnet命令很相似,允许用户启动远程系统上的交互命令会话。

rlogin 的一般格式是:

rlogin [ -8EKLdx ] [ -e char ] [-k realm ] [ - l username ] host

一般最常用的格式是:

rlogin host

该命令中各选项的含义为:

  -8 此选项始终允许8位输入数据通道。该选项允许发送格式化的ANSI字符和其他的特殊代码。如果不用这个选项,除非远端的终止和启动字符不是或,否则就去掉奇偶校验位。

-E 停止把任何字符当作转义字符。当和-8选项一起使用时,它提供一个完全的透明连接。

-K 关闭所有的Kerberos确认。只有与使用Kerberos 确认协议的主机连接时才使用这个选项。

-L 允许rlogin会话在litout模式中运行。要了解更多信息,请查阅tty联机帮助。

-d 打开与远程主机进行通信的TCP sockets的socket调试。要了解更多信息,请查阅setsockopt的联机帮助。

-e 为rlogin会话设置转义字符,默认的转义字符是“~”,用户可以指定一个文字字符或一个\\\\nnn形式的八进制数。

-k 请求rlogin获得在指定区域内的远程主机的Kerberos许可,而不是获得由krb_realmofhost(3)确定的远程主机区域内的远程主机的Kerberos 许可。

-x 为所有通过rlogin会话传送的数据打开DES加密。这会影响响应时间和CPU利用率,但是可以提高安全性。

4.rsh命令

rsh是“remote shell”(远程 shell)的缩写。 该命令在指定的远程主机上启动一个shell并执行用户在rsh命令行中指定的命令。如果用户没有给出要执行的命令,rsh就用rlogin命令使用户登录到远程机上。

rsh命令的一般格式是:

         rsh [-Kdnx] [-k realm] [-l username] host [command]

一般常用的格式是:

        rsh host [command ]

        command可以是从shell提示符下键人的任何Linux命令。

     rsh命令中各选项的含义如下:

   -K 关闭所有的Kerbero确认。该选项只在与使用Kerbero确认的主机连接时才使用。

   -d 打开与远程主机进行通信的TCP sockets的socket调试。要了解更多的信息,请查阅setsockopt的联机帮助。

   -k 请求rsh获得在指定区域内的远程主机的Kerberos许可,而不是获得由krb_relmofhost(3)确定的远程主机区域内的远程主机的Kerberos许可。

   -l 缺省情况下,远程用户名与本地用户名相同。本选项允许指定远程用户名,如果指定了远程用户名,则使用Kerberos 确认,与在rlogin命令中一样。

   -n 重定向来自特殊设备/dev/null的输入。

-x 为传送的所有数据打开DES加密。这会影响响应时间和CPU利用率,但是可以提高安全性。

Linux把标准输入放入rsh命令中,并把它拷贝到要远程执行的命令的标准输入中。它把远程命令的标准输出拷贝到rsh的标准输出中。它还把远程标准错误拷贝到本地标准错误文件中。任何退出、中止和中断信号都被送到远程命令中。当远程命令终止了,rsh也就终止了。

5.rcp命令

rcp代表“remote file copy”(远程文件拷贝)。该命令用于在计算机之间拷贝文件。

rcp命令有两种格式。第一种格式用于文件到文件的拷贝;第二种格式用于把文件或目录拷贝到另一个目录中。

  rcp命令的一般格式是:

           rcp [-px] [-k realm] file1 file2 rcp [-px] [-r] [-k realm] file

  directory 每个文件或目录参数既可以是远程文件名也可以是本地文件名。远程文件名具有如下形式:rname@rhost:path,其中rname是远程用户名,rhost是远程计算机名,path是这个文件的路径。

  rcp命令的各选项含义如下:

  -r 递归地把源目录中的所有内容拷贝到目的目录中。要使用这个选项,目的必须是一个目录。

    -p 试图保留源文件的修改时间和模式,忽略umask。

  -k 请求rcp获得在指定区域内的远程主机的Kerberos 许可,而不是获得由krb_relmofhost(3)确定的远程主机区域内的远程主机的Kerberos许可。

    -x 为传送的所有数据打开DES加密。这会影响响应时间和CPU利用率,但是可以提高安全性。 如果在文件名中指定的路径不是完整的路径名,那么这个路径被解释为相对远程机上同名用户的主目录。如果没有给出远程用户名,就使用当前用户名。如果远程机上的路径包含特殊shell字符,需要用反斜线(\\\\)、双引号(”)或单引号(’)括起来,使所有的shell元字符都能被远程地解释。 需要说明的是,rcp不提示输入口令,它通过rsh命令来执行拷贝。

Vi常用技巧

l        取消命令

在vi中,只要没有把修改结果存入磁盘文件中,那么就可以通过“取消”来撤销最近的操作或对缓冲区的修改。

假设你无意删除了一行文本、改变了一些你不应该改变的内容或增加了一些不正确的文本,可以按改变到命令模式中,然后按,则文件内容恢复到修改前的样子。

l        保存到文件名为filename的文件中

发出写命令格式:   :w filename

l        不使用小键盘来定位光标

vi用、、、键来定位光标。其中、键表示光标的左右移动,、键表示光标的上下移动,在某些没有或不能使用小键盘的情况下这四个键是很有用的。

下面是其他一些用于移动光标的键:

n        按空格键或向右移动光标一个位置

n        按将光标移动到下一行的行首

n        使用键将光标移动到下一行的当前位置或行末

n        按<->将光标移动到上一行行首

n        使用键将光标移动到上一行的当前位置或行末

n        按将光标向左移动一个字符

n        按<0>(零)将光标移动到一行的行首

n        按<$>将光标移动到一行的行末

l        大范围移动键

可快速定位光标到屏幕的顶部、中部和底部:

n        按将光标移到屏幕的第一行,有时称为home位置

n        按将光标移到现在屏幕显示的各行的中间一行

n        按将光标移到屏幕的最后一行

n        按向前移动一屏

n        按向后移动一屏

n        要移动到缓冲区中指定的行中,在按前键入行号(注意,这里的行号不是当前屏幕中的相对行号,而是绝对行号)

l        删除文本

n        删除光标处的字符

n         删除从当前字的光标处到下一个字的开始处之间的内容

n        <$> 删除从光标处到行尾之间的内容

n         同<$>,删除当前行的剩余部分

n         删除整行,不管光标在该行的位置

n        通过在上述命令之前键入一个整数,可将这些命令应用到几个对象中,例如:<4>删除4个字符;<8> 删除8行

l        添加文本

n        使用在光标位置前插入文本

n        使用使你进入输入模式并且在当前行行首插入文本

n        使用在光标位置后插入文本

n        使用使你进入输入模式并且在当前行末尾插入文本

n        使用在当前行的下面打开一行以添加文本

n        使用在当前行的上面打开一行以添加文本

l        使vi显示行号

按键以确保你在命令模式中,然后输入:se number。要关闭行号,输入:se nonumber

l        查找

n        /string     在缓冲区中向前查找字符串string

n        ?string    在缓冲区中向后查找字符串string

n                以当前的方向再次查找

n        以相反的方向再次查找

n        注意,查找的字符串中若含有特殊字符的,要使用\\来进行转意

l        修改和替换文本

n         替换单个字符

n        替换一个字符序列

n        修改当前字,从光标处到这个字的字尾

n        修改当前字,从光标处到这个字的字尾(与相同)

n        修改当前字,从该字的字头到光标以前的那些字符

n        <$>修改一行,从光标处到该行的行尾

n        修改一行,从光标处到该行的行尾(与<$>相同)

n        修改整行

n        注意,这些命令的每个命令都使之进入了输入模式。除使用来替换单个字符外,必须按键来完成所作的修改并返回命令模式

n        要修改几个字,在按之前使用一个整数

l        拷贝、剪切和粘贴

n        拷贝从当前字的光标处到下一个字的开始处之间的内容

n        <$>拷贝从光标处到行尾之间的内容

n        拷贝当前行的剩余部分(与<$>相同)

n        拷贝整个当前行

n        通过在这些命令前键入整数,所有这些命令都可以用于多个对象。

n        当删除或剪切或拷贝时,删除或拷贝的对象被保存在通用缓冲区中,可以使用

或命令将这个缓冲区中的内容粘贴到光标位置。

n        

命令将对象粘贴到光标位置右边或光标位置后面

n        命令将对象粘贴到光标位置左边或光标位置前面

l        重复命令

可以按< . >来重复改变缓冲区的最后一个命令