本文主要是总结下 sed 的一些常规用法,主要参考酷壳的这篇文章“sed简明教程”,还有 sed 的官方手册。sed 是 stream editor 的缩写,也即是流编辑器,将输入流按照命令做相应的编辑变换的工作。本文主要是作为学习 sed 的一个总结,帮助记忆,挂一漏万是难免的了。

sed命令的格式一般如下:

sed [命令参数]  [脚本]  [输入流(如文件名)]

如下,就是将文件 my.txt 中的第1到3行所有 my 替换为 your,第3行到文件尾的 This 替换为 That,并最后将替换后结果写回文件 my.txt 中。

$ sed -i '1,3s/my/your/g; 3,$s/This/That/g' my.txt

命令参数

几个常用的选项有:

-i 直接编辑原文件
-n 安静模式,不输出

脚本部分

脚本部分用单引号或双引号包围,内部可以分为多个命令,用分号隔开,如下:

命令块1;命令块2;...;命令块n

其中每个命令块的格式如下:

[匹配范围][!][sed命令 [命令参数]]

匹配范围

可以指定单行或者指定一个范围,可以直接写行号,如指定第1行 "1",或者从第3行到文件尾 "3,$";还可以通过形式 "/pattern/" 来指定找到该模式的行,如 "/dog/,/cat/" 代表从含有单词 dog 的行开始,到其后第一个含有单词 cat 的行结束,如果没有找到含有 dog 的行则后面的命令不被执行,如果有开始但是没有找到含有 cat 的行则命令会从开始行执行到文件尾。匹配范围还可以指定相对行数,如 "/dog/,+3" 表示的范围是第一个含有 dog 的行及其后3行。

如果匹配范围省略则命令对输入流的所有行都执行。

sed 命令

在 sed 命令前加上感叹号代表在指定的匹配范围内不执行该命令,其他行则执行。

a 命令,即在找到的行后 append 后面的内容,如下就是将第2到第5行每行后都加入一行“---***---”

$ sed "2,5a ---***---" my.txt
This is my cat
my cat's name is betty
---***---
This is my dog
---***---
my dog's name is frank
---***---
This is my fish
---***---
my fish's name is george
This is my goat
my goat's name is adam

i 命令,与 a 命令类似即在找到的行前 insert 进后面的内容。
c 命令,即将找到的行替换为后面的内容,如下,就将第2到第5行替换为“---***---”

$ sed "2,5c ---***---" my.txt
This is my cat
---***---
my fish's name is george
This is my goat
my goat's name is adam

d 命令,即将找到的行 delete。
p 命令,即将找到的行 print 出来。
N 命令,把下一行的内容纳入缓冲区做匹配。
s 命令,这个应该是最重要也是最常用的命令,substitute 替换,一般的使用格式如下:

s/pattern-source/pattern-destination/pattern-flag

其主要就是将匹配到的 pattern-source 的内容替换为 pattern-destination 的内容,如果熟悉 vim 的就会发现此处和 vim 中的s替换是一致的,也使用正则表达式匹配,也一样可以使用圆括号匹配。pattern-flag 可以使用 "/g" 代表所有能匹配上的都进行替换 globally,如果不加此则每行只替换第一个匹配上的模式,还可以使用 "/I" 代表匹配不区分大小写 Ignore Case。下面是关于 s 命令的一些例子。

$ cat my.txt
This is my cat
  my cat's name is betty
This is my dog
  my dog's name is frank
This is my fish
  my fish's name is george
This is my goat
  my goat's name is adam
$ sed "s/s/S/2" my.txt  #只替换每一行的第2个s
This iS my cat
  my cat's name iS betty
This iS my dog
  my dog's name iS frank
This iS my fish
  my fish'S name is george
This iS my goat
  my goat's name iS adam
$ sed "N;s/This is my \([^\n]*\)\n.*is \(.*\)/\1:\2/g" my.txt
cat:betty
dog:frank
fish:george
goat:adam

进阶部分

这一部分主要是关于 pattern space 和 hold space 的,酷壳的文章中有很好的图例解释,sed 手册里关于这两个概念是这样解释的:

sed 保持有两个数据缓存:当前的 pattern space 和辅助用的 hold space。初始都为空。

sed 对输入的每一行都执行如下的循环,首先,sed 读入一行,并把它放入 pattern space 中,执行命令。命令执行完后,若不是有指定 "-n" 参数则将当前的 pattern space 的内容输出到输出流中,至此完成了关于输入流中一行的循环。

关于这两个数据缓存有几个相关的命令:

  • g: 将hold space中的内容拷贝到pattern space中,原来pattern space里的内容清除
  • G: 将hold space中的内容append到pattern space\n后
  • h: 将pattern space中的内容拷贝到hold space中,原来的hold space里的内容被清除
  • H: 将pattern space中的内容append到hold space\n后
  • x: 交换pattern space和hold space的内容

几个例子:

$ cat t.txt
one
two
three
$ sed 'H;g' t.txt

one

one
two

one
two
three
$ sed '1!G;h;$!d' t.txt # 反序一个文件的行
three
two
one

现在有两台 Linux 服务器,A(用户名a) 和 B(用户名b),现在我们想从 A 服务器通过 SSH 访问服务器 B 而无需输入密码。

首先,A 和 B 上都需安装有 SSH,用以下命令安装

sudo yum install openssh

同时修改两台服务器上的文件 /etc/ssh/sshd_config,将下面这三行取消注释:

RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

更改配置文件后重启 SSH 服务

service sshd restart

在服务器 A 上用 RSA 算法生成公钥和私钥,并且将 A 的公钥拷贝到 B 中,设置文件和文件夹适当的权限即可,具体过程如下。

[a@A ~]$ ssh-keygen -t rsa -P ''
Generating public/private rsa key pair.
Enter file in which to save the key (/home/a/.ssh/id_rsa):      
    #此处不用输入,直接回车即可
Created directory '/home/a/.ssh'.
Your identification has been saved in /home/a/.ssh/id_rsa.
Your public key has been saved in /home/a/.ssh/id_rsa.pub.

如果 b@B 的 ~ 文件夹下无 .ssh 文件夹则建一个,否则跳过此步。

[a@A ~]$ ssh b@B mkdir -p .ssh
b@B's password:

将 A 的公钥拷入 B 中。

[a@A ~]$ cat .ssh/id_rsa.pub | ssh b@B 'cat >> .ssh/authorized_keys'
b@B's password:

设置文件夹和文件权限。

[b@B ~]$ chmod 700 ~/.ssh
[b@B ~]$ chmod 600 ~/.ssh/authorized_keys

P.S. 服务器  B 需要有本地回环接口,也即是正确配置了 /etc/sysconfig/network-scripts/ifcfg-lo 文件,可以参考这里

最后讲一下这种访问的原理。

RSA 密码体制是一种非对称密码体制,也就是说它的密码有两个,一个公开的称为公钥,另一个不公开的称为私钥,它假设人们一般无法根据公钥推算出私钥(这个由数学计算上的难度来保证)。假设 B 想通过网络发条消息 x 给 A,并且不想让让别人知道这个消息的内容,这时,B 应该在本地上用 A 的公开的公钥加密消息 x 得到 y,然后通过网络将加密后的消息 y 发送给 A,然后 A 在本地用自己的私钥将消息 y 解密得到原消息 x。假设有个第三者 C,它截获了在网络上传输的消息 y,并且 C 也轻易地知道了 A 的公钥(因为这个公钥是相对公开的),但是 C 仍然无法知道这条消息的原文是什么,因为 C 并没有 A 的私钥无法解密消息。

现在回到最开始的两台服务器 A 与 B 的情形,我们想让 A 可以无需输入密码就可以登陆到 B 上。其实我们要做的就是让服务器 B 接受到登陆请求时确认发起请求的是服务器 A 即可,所以大致流程如下:

  • 服务器 B 接受到一个自称是服务器 A 的登陆请求;
  • B 在本地生成一个随机数 r,并且用 A 的公钥进行加密,得到 rc
  • B 将 rc 传回给请求登陆的服务器,并要求该服务器解密;

之后分为两种情况

  1. 若 B 收到请求登陆服务器传回的数值,并且等于 r,则认可该服务器的身份是 A,并允许其登陆;
  2. 若 B 在一定时间内都未收到回复,或者收到的数值不正确,都视为无法确认请求服务器的身份,并拒绝其登陆。

参考文章:SSH login without password

1991年10月5日Linus Torvalds第一次公开了现在成为Linux的操作系统内核,现在这个系统大致结构如下:

640px-Linux_OS.svg

Linux系统是由Linux内核和核心GNU工具链组成的,现在Linux上有几种主流的GUI界面,X Window是早期的图形界面,KDE提供了一种类Microsoft Windows的图形化界面,而GNOME则是现在Ubuntu上用的图形界面。

Shell

一些常见的环境变量

  • PS1: 控制默认命令行提示符的格式;
  • PS2: 控制后续命令行提示符的格式;
  • IFS: shell 用来分隔文本字符串的一列字符;
  • PATH: 冒号分隔的shell查找命令的目录列表。

命令的参数风格

  • Unix风格的参数,前面加单破折线;
  • BSD风格的参数,前面不加破折线(这个是Berkeley Software Distribution 发行的Unix版本下的写法);
  • GNU风格的参数,前面加双破折线;