单个文件
加密和解密:
# openssl des -salt -in file -out file.des
# openssl des -d -salt -in file.des -out file
那个 file 可以是归档文件(tar archive)。
归档并加密整个目录
# tar -cf - directory | openssl des -salt -out directory.tar.des # 加密
# openssl des -d -salt -in directory.tar.des | tar -x # 解密
压缩归档并加密整个目录
# tar -zcf - directory | openssl des -salt -out directory.tar.gz.des # 加密
# openssl des -d -salt -in directory.tar.gz.des | tar -xz # 解密
- 在使用-k mysecretpassword后,des会取消交互式的密码请求。不过,这非常不安全。
- 使用des3代替des来获得更强的加密(Triple-DES Cipher)。这同样会消耗更多的CPU。
GPG
GnuPG 是众所周知的对邮件或任何数据进行加密和签名的软件。此外,gpg 还提供高级密钥管理系统。此章节只涵盖了文件加密,没有邮件加密、签名或者信任网络(Web-Of-Trust)。
单纯的加密是一个对称式的加密算法(symmetric cipher)。在本例中,文件是用一个秘密来加密的,任何人知道了这个密码都可以对其进行解密,因此就不需要密钥。Gpg 添加后缀 ".gpg" 到已加密的文件名。
# gpg -c file # 使用密码加密文件
# gpg file.gpg # 文件解密(选项 -o 其他文件)
使用密钥
对于更详细的请看
GPG 快速上手 和
GPG/PGP 基础,特别是
gnupg 文档。
密钥对(私钥,公钥)为非对称加密技术。 要点如下:
首先生成密钥对。使用默认就行,但你至少要输入你的全名、邮件地址和可选注释。该注释对于创建相同的名字和邮件地址的多个密钥来说非常有用。此外,你应该使用"口令(passphrase)",而不是简单的密码。
# gpg --gen-key # 这需要一些时间
在 Unix 上密钥存储在 ~/.gnupg/ 中,在 Windows 上通常存储在
C:/Documents and Settings/%USERNAME%/Application Data/gnupg/ 中。
~/.gnupg/pubring.gpg # 包含你的公钥和所有其他导入的信息
~/.gnupg/secring.gpg # 可包含多个私钥
常用选项的简短描述:
- -e 加密数据
- -d 解密数据
- -r 为某个收件者加密('全名' 或者 'email@domain')
- -a 输出经过 ascii 封装的密钥
- -o 指定输出文件
本实例使用'Your Name' 和 'Alice' 作为密钥的 email 或 全名 或 部分名字的参考。举个例子,我可以使用 'Colin' or 'c@cb.vu' 给我的密钥 [Colin Barschel (cb.vu) <c@cb.vu>]。
只用于个人的加密
不需要导出/导入任何密钥,因为你都已经有了。
# gpg -e -r 'Your Name' file # 使用你的公钥加密
# gpg -o file -d file.gpg # 解密。使用 -o 指定输出文件
用密钥加密-解密
首先你需要导出给别人使用的公钥。并且你需要导入来自 Alice 她所加密文件的公钥。你可以用简单的 ascii 文档或者使用公钥服务器来保存这些密钥。
举个例子,Alice 导出她的公钥,然后你导入它,之后你就可以加密一个文件给她。这个加密文件只有 Alice 可以解密。
# gpg -a -o alicekey.asc --export 'Alice' # Alice 导出她的公钥到 ascii 文件中
# gpg --send-keys --keyserver subkeys.pgp.net KEYID # Alice 把她的公钥放入一个服务器
# gpg --import alicekey.asc # 你导入她的密钥到你的公钥环(pubring)中
# gpg --search-keys --keyserver subkeys.pgp.net 'Alice' # 或者从一个服务器中获取他的公钥
一旦这些公钥导入后,加密或解密一个文件会非常简单:
# gpg -e -r 'Alice' file # 给 Alice 加密文件
# gpg -d file.gpg -o file # 解密 Alice 给你的加密文件
密钥管理
# gpg --list-keys # 列出所有公钥并查看其 KEYID
KEYID 跟在 '/' 后面 比如:pub 1024D/D12B77CE 它的 KEYID 是 D12B77CE
# gpg --gen-revoke 'Your Name' # 产生一份撤销密钥证书
# gpg --list-secret-keys # 列出所有私钥
# gpg --delete-keys NAME # 从本的密钥环中删除一个公钥
# gpg --delete-secret-key NAME # 从本的密钥环中删除一个私钥
# gpg --fingerprint KEYID # 显示 KIYID 这个密钥的指纹
# gpg --edit-key KEYID # 编辑密钥(比如签名或者添加/删除 email)
有(许多)其他替代方法来加密磁盘,我只呈现我所知道和使用的方法。请记住,安全只是系统还未经过实际考验而已。入侵者可以轻易通过键盘事件记录密码。此外,当已经加载了分区,其数据是可以自由访问的,并不会阻止入侵者去访问它。
Linux
这部分我们使用可用于 2.6 内核的 Linux dm-crypt (device-mapper)。在这个实例中,让我们加密
/dev/sdc1 分区,它可为任何其他分区、磁盘、USB或者用
losetup 创建的基于文件的分区。对于基于文件的分区,我们使用
/dev/loop0。看
镜像文件分区。Device mapper 利用标签来标识一个分区。我们使用
sdc1作为此标签,但可以为任何字符串。
dm-crypt with LUKS
LUKS 和 dm-crypt 是较好的加密技术,并且可为同一个分区设置多个口令,更改密码也很方便。可简单输入
# cryptsetup --help 来测试 LUKS 是否可用。如果没有显示任何关于 LUKS 的信息,可看下面
Without LUKS 的介绍。第一步如果需要的话创建一个分区:
fdisk /dev/sdc。
创建加密分区
# dd if=/dev/urandom of=/dev/sdc1 # 可选
# cryptsetup -y luksFormat /dev/sdc1 # 这破坏了在 sdc1 上的数据
# cryptsetup luksOpen /dev/sdc1 sdc1
# mkfs.ext3 /dev/mapper/sdc1 # 创建 ext3 文件系统
# mount -t ext3 /dev/mapper/sdc1 /mnt
# umount /mnt
# cryptsetup luksClose sdc1 # Detach 已加密的分区
Attach
# cryptsetup luksOpen /dev/sdc1 sdc1
# mount -t ext3 /dev/mapper/sdc1 /mnt
Detach
# umount /mnt
# cryptsetup luksClose sdc1
dm-crypt without LUKS
# cryptsetup -y create sdc1 /dev/sdc1 # 或任何其他分区像 /dev/loop0
# dmsetup ls # 检查一下,将显示:sdc1 (254, 0)
# mkfs.ext3 /dev/mapper/sdc1 # 只有第一次要这么做!
# mount -t ext3 /dev/mapper/sdc1 /mnt
# umount /mnt/
# cryptsetup remove sdc1 # Detach 已加密的分区
这样做等同于(非 mkfs 部分) re-attach 分区。如果密码不正确,mount 命令将会失败。对于这个例子,只要简单的移除 sdc1 (
cryptsetup remove sdc1)并重建即可。
FreeBSD
两个流行的 FreeBSD 磁盘加密模块为
gbde 和
geli。我现在使用
geli 原因是它够快并且它使用加解密硬件加速设备。详情可看
FreeBSD 使用手册 18.6。
geli模块必须已编译或加载进内核:
options GEOM_ELI
device crypto # 内核配置文件中加入这两行
# echo 'geom_eli_load="YES"' >> /boot/loader.conf # 也可以在系统引导时加载或者做:kldload geom_eli
使用密码和密钥
我为一个典型的磁盘加密使用这些设置,其使用了一个口令和一个加密主密钥(master key)的密钥。这意味着你需要密码和生产的密钥
/root/ad1.key来 attach 分区。主密钥存储在这个加密分区中并且不可见。看下面为 USB 或 映像文件的加密设置。
创建加密分区
# dd if=/dev/random of=/root/ad1.key bs=64 count=1 # 加密主密钥的密钥
# geli init -s 4096 -K /root/ad1.key /dev/ad1 # 对于磁盘也可用 -s 8192
# geli attach -k /root/ad1.key /dev/ad1 # 将 /dev/ad1 与所生成的密钥 /root/ad1.key 关联
# dd if=/dev/random of=/dev/ad1.eli bs=1m # 可选,需要很长时间
# newfs /dev/ad1.eli # 创建文件系统
# mount /dev/ad1.eli /mnt
Attach
# geli attach -k /root/ad1.key /dev/ad1
# fsck -ny -t ffs /dev/ad1.eli # 检查文件系统
# mount /dev/ad1.eli /mnt
Detach
Detach 步骤会在关机时自动完成。
# umount /mnt
# geli detach /dev/ad1.eli
/etc/fstab
加密分区在 /etc/fstab 中配置成自动加载。系统启动时会询问加密分区的密码。对于本例下列设置是必须的:
# grep geli /etc/rc.conf
geli_devices="ad1"
geli_ad1_flags="-k /root/ad1.key"
# grep geli /etc/fstab
/dev/ad1.eli /home/private ufs rw 0 0
仅用密码
加密一个 USB stick 或者映像文件使用密码而不是密钥来得更方便。这种情况下,没有必要随身携带额外的密钥文件。所做步骤同上面非常相似,只是不需要密钥文件。让我们来加密一个 1 GB 的映像文件
/cryptedfile。
# dd if=/dev/zero of=/cryptedfile bs=1M count=1000 # 1 GB 文件
# mdconfig -at vnode -f /cryptedfile
# geli init /dev/md0 # 仅用密码加密
# geli attach /dev/md0
# newfs -U -m 0 /dev/md0.eli
# mount /dev/md0.eli /mnt
# umount /dev/md0.eli
# geli detach md0.eli
现在可以把这个映像文件加载成仅需密码的文件系统。
# mdconfig -at vnode -f /cryptedfile
# geli attach /dev/md0
# mount /dev/md0.eli /mnt
所谓的 SSL/TLS 认证是加密的公钥认证,它由一个公用密钥和私用密钥组成。证书用来认证终端和加密数据的。例如,用在 web 服务器(https)或者邮件服务器(imaps)。
步骤
- 我们需要一个证书颁发机构来签署我们的证书。这一步通常由供应商提供,如 Thawte、Verisign等。不过,我们也可以创建我们自己的。
- 创建一个证书签发申请(signing request)。这个申请需要一个已经包含所有必需的信息的未签署证书(公共部分)。该证书申请通常发送到认证供应商去签署。这一步同样也在本地机器上创建了私钥。
- 证书颁发机构签署证书。
- 如果有需要,加入证书和密钥到单个文件来给应用程序使用(web 服务器、邮件服务器等)。
配置 OpenSSL
我们使用 /usr/local/certs 作为这个例子的目录或者根据你的设置相应的编辑 /etc/ssl/openssl.cnf 文件,因此你知道文件将创建在哪里。以下是 openssl.cnf 的相关部分:
[ CA_default ]
dir = /usr/local/certs/CA # 保存所有信息的文件夹
certs = $dir/certs # 已生成证书的默认保存目录
crl_dir = $dir/crl # 生成的证书撤销列表(CRL)的默认保存目录
database = $dir/index.txt # 保存已签发证书的文本数据库文件
确保所有目录已经创建
# mkdir -p /usr/local/certs/CA
# cd /usr/local/certs/CA
# mkdir certs crl newcerts private
# echo "01" > serial # 仅当 serial 不存在时
# touch index.txt
创建一个认证授权
如果你没有来自供应商的认证授权,你必须创建你自己的。如果打算去供应商签署申请,那么这个步骤不是必须的。创建认证授权 (CA):
# openssl req -new -x509 -days 730 -config /etc/ssl/openssl.cnf \
-keyout CA/private/cakey.pem -out CA/cacert.pem
创建证书签发申请
要创建一个新证书(比如给邮件服务器或 web 服务器),首先用其私钥创建证书申请。如果你的应用程序不支持加密的私钥(比如 UW-IMAP 就不支持),那么就用
-nodes来禁用加密。
# openssl req -new -keyout newkey.pem -out newreq.pem \
-config /etc/ssl/openssl.cnf
# openssl req -nodes -new -keyout newkey.pem -out newreq.pem \
-config /etc/ssl/openssl.cnf # 不对这个密钥加密
签署证书
该证书申请由 CA 签发确认,这个步骤通常由供应商完成。
注意:在下面命令中替换 "servername" 成你的服务器名称。
# cat newreq.pem newkey.pem > new.pem
# openssl ca -policy policy_anything -out servernamecert.pem \
-config /etc/ssl/openssl.cnf -infiles new.pem
# mv newkey.pem servernamekey.pem
现在,servernamekey.pem 就是私钥,servernamecert.pem 就为服务器的证书。
创建联合认证(united certificate)
IMAP 服务器想要私钥和服务器证书在同一个文件中。通常,这还是比较容易处理的,但是该文件要保证安全! Apache 也可以处理好它。创建一个包含证书和密钥的文件 servername.pem 。
- 用文本编辑器打开私钥文件(servernamekey.pem),并拷贝私钥到 "servername.pem" 文件中去。
- 服务器证书(servernamecert.pem)也做同样的动作。
最后 servername.pem 文件应该看起来像这样:
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDutWy+o/XZ/[...]qK5LqQgT3c9dU6fcR+WuSs6aejdEDDqBRQ
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIERzCCA7CgAwIBAgIBBDANB[...]iG9w0BAQQFADCBxTELMAkGA1UEBhMCREUx
-----END CERTIFICATE-----
现在我们的 /usr/local/certs/ 目录中有了这些;
- CA/private/cakey.pem (CA 服务器私钥)
- CA/cacert.pem (CA 服务器公钥)
- certs/servernamekey.pem (服务器私钥)
- certs/servernamecert.pem (服务器已签署的证书)
- certs/servername.pem (私钥和服务器证书)
要保证私钥的安全!
查看证书信息
要查看证书信息,只要这么做:
# openssl x509 -text -in servernamecert.pem # 显示证书信息
# openssl req -noout -text -in server.csr # 显示申请信息
# openssl s_client -connect cb.vu:443 # 检查 web 服务器认证信息
服务器设置
CVS 环境初始化
决定主 repository 将要创建和重置的 cvs 根目录。比如 /usr/local/cvs (根):
# mkdir -p /usr/local/cvs
# setenv CVSROOT /usr/local/cvs # 设置 CVSROOT 环境变量(本地)
# cvs init # 创建所有初始化 CVS 配置文件
# cd /root
# cvs checkout CVSROOT # 签出配置文件来修改他们
# cd CVSROOT
edit config ( fine as it is)
# cvs commit config
cat >> writers # 创建 writers 文件 (也可为 readers)
colin
^D # 使用 [Control][D] 退出编辑
# cvs add writers # 添加文件 writers 进 repository
# cvs edit checkoutlist
# cat >> checkoutlist
writers
^D # 使用 [Control][D] 退出编辑
# cvs commit # 提交所有配置更改
添加一个 readers 文件,如果你要区分读写权限的话。
注意: 不要在主 cvs 中直接编辑文件,而应该签出要编辑的文件,修改完成后再签入。我们所做的文件 writers 用来定义可写权限。
下面有三种流行的方式去访问 CVS。前两个不需要任何进一步的配置。看
CVSROOT部分的实例了解如何使用它们:
- 直接本的访问文件系统。用户需要有足够的权限来直接访问 CVS,除了要登录到操作系统,没有进一步的验证。然而这仅对本地 repository 才有用。
- 使用 ext 协议通过 ssh 来远程访问。任何有 ssh shell 账户和在 CVS 服务器上可读写权限的都可直接使用 ext 协议通过 ssh 来访问 CVS,而不需要任何额外的隧道。没有服务器来处理运行在 CVS 上的验证工作。ssh 登录会去验证。
- 用 pserver 来远程访问。这是对于有较大用户量的首选方法,用户由 CVS 的 pserver 通过一个专门的密码数据库来验证,因此不需要本地用户帐户。这种设置在下面会有说明。
用 inetd 设置网络
如果不需要网络访问,CVS 可以运行于本地。对于远程访问,在 /etc/inetd.conf (Suse 为 /etc/xinetd.d/cvs)中配置如下行,可让守护进程 inetd 启动 pserver:
cvspserver stream tcp nowait cvs /usr/bin/cvs cvs \
--allow-root=/usr/local/cvs pserver
这是个用来阻断从 internet 访问 cvs 端口的好方法,可使用 ssh 隧道来远程的访问 repository。
单独认证
CVS 用户可能不是操作系统的一部分(即不是本地用户)。这其实可从安全的角度去看。简单的添加一个叫 passwd (in the CVSROOT directory) 的文件,其包含 crypt 格式的用户登录名和密码。这也可以使用 apache 的 htpasswd 工具来完成。
注意:这个 passwd 文件仅仅是文件,可以在 CVSROOT 中直接编辑。它不能被签出。更多信息请用 htpasswd --help
# htpasswd -cb passwd user1 password1 # -c 创建文件
# htpasswd -b passwd user2 password2
现在添加
:cvs到每行的结尾处,用来告诉 cvs 服务器更改用户到 cvs (或任何你正在运行的 cvs 服务器下)。它看起来像这样:
# cat passwd
user1:xsFjhU22u8Fuo:cvs
user2:vnefJOsnnvToM:cvs
测试它
测试作为一般用户登录(比如我)
# cvs -d :pserver:colin@192.168.50.254:/usr/local/cvs login
Logging in to :pserver:colin@192.168.50.254:2401/usr/local/cvs
CVS password:
CVSROOT 变量
这是个环境变量用来指定 repository 的位置。对于本地使用,该变量只需设置成 repository 的目录。对于通过网络使用,传输协议必须指定。使用
setenv CVSROOT string(csh, tcsh shell) 或者
export CVSROOT=string( sh, bash shell) 设置 CVSROOT 环境变量。
# setenv CVSROOT :pserver:<username>@<host>:/cvsdirectory
For example:
# setenv CVSROOT /usr/local/cvs # 仅限本的使用
# setenv CVSROOT :local:/usr/local/cvs # 同上
# setenv CVSROOT :ext:user@cvsserver:/usr/local/cvs # 通过 SSH 直接访问
# setenv CVS_RSH ssh # ext 协议访问
# setenv CVSROOT :pserver:user@cvsserver.254:/usr/local/cvs # 通过 pserver 网络访问
一旦登录成功就可导入一个新项目进 repository:cd 进入你的项目根目录
cvs import <module name> <vendor tag> <initial tag>
cvs -d :pserver:colin@192.168.50.254:/usr/local/cvs import MyProject MyCompany START
在 repository 中有个名叫 MyProject 新项目(之后用来签出)。CVS 会导入当前目录的内容进新项目。
签出:
# cvs -d :pserver:colin@192.168.50.254:/usr/local/cvs checkout MyProject
或者
# setenv CVSROOT :pserver:colin@192.168.50.254:/usr/local/cvs
# cvs checkout MyProject
通过 SSH 隧道访问 CVS
我们需要两个 shell 来做这个。在第一个 shell 中,我们连接到 cvs 服务器并对 cvs 连接进行端口转发(port-forward)。在第二个 shell 中,我们就像在本地一样使用 cvs。
在 shell 1:
# ssh -L2401:localhost:2401 colin@cvs_server # 直接连接到 cvs 服务器。或:
# ssh -L2401:cvs_server:2401 colin@gateway # 使用一个网关间接连接到 cvs 服务器
在 shell 2:
# setenv CVSROOT :pserver:colin@localhost:/usr/local/cvs
# cvs login
Logging in to :pserver:colin@localhost:2401/usr/local/cvs
CVS password:
# cvs checkout MyProject/src
CVS 命令及其使用
导入
该 import 命令用来添加整个目录,它必须运行于要导入的目录中。比如,目录 /devel/ 包含的所有文件和子目录要导入。该目录名在 CVS 中(模块)将会称为 "myapp"。
# cvs import [options] directory-name vendor-tag release-tag
# cd /devel # 必须在该目录中来导入
# cvs import myapp Company R1_0 # 修订(release)标签可以为任何单个单词
在添加了一个新目录 "/devel/tools/" 后,也可这么导入。
# cd /devel/tools
# cvs import myapp/tools Company R1_0
签出、更新和提交
# cvs co myapp/tools # 仅会签出 tools 目录
# cvs co -r R1_1 myapp # 签出修订版本为 R1_1 的 myapp (sticky)
# cvs -q -d update -P # 典型的 CVS 更新
# cvs update -A # 重置所有 sticky 标签(或日期、选项)
# cvs add newfile # 添加一个新文件
# cvs add -kb newfile # 添加一个二进制文件
# cvs commit file1 file2 # 仅提交这两个文件
# cvs commit -m "message" # 提交所有更改并为这个更改添加日志消息
创建一个 patch
It is best to create and apply a patch from the working development directory related to the project, or from within the source directory.
# cd /devel/project
# diff -Naur olddir newdir > patchfile # Create a patch from a directory or a file
# diff -Naur oldfile newfile > patchfile
应用一个 patch
Sometimes it is necessary to strip a directory level from the patch, depending how it was created. In case of difficulties, simply look at the first lines of the patch and try -p0, -p1 or -p2.
# cd /devel/project
# patch --dry-run -p0 < patchfile # Test the path without applying it
# patch -p0 < patchfile
# patch -p1 < patchfile # strip off the 1st level from the path
SVN
Server setup | SVN+SSH | SVN over http | SVN usage
Subversion (SVN) is a version control system designed to be the successor of CVS (Concurrent Versions System). The concept is similar to CVS, but many shortcomings where improved. See also the
SVN book.
Server setupThe initiation of the repository is fairly simple (here for example
/home/svn/must exist):
# svnadmin create --fs-type fsfs /home/svn/project1
Now the access to the repository is made possible with:
- file:// Direct file system access with the svn client with. This requires local permissions on the file system.
- svn:// or svn+ssh:// Remote access with the svnserve server (also over SSH). This requires local permissions on the file system.
- http:// Remote access with webdav using apache. No local users are necessary for this method.
Using the local file system, it is now possible to import and then check out an existing project. Unlike with CVS it is not necessary to cd into the project directory, simply give the full path:
# svn import /project1/ file:///home/svn/project1/trunk -m 'Initial import'
# svn checkout file:///home/svn/project1
The new directory "trunk" is only a convention, this is not required.
Remote access with ssh
No special setup is required to access the repository via ssh, simply replace
file:// with
svn+ssh/hostname. For example:
# svn checkout svn+ssh://hostname/home/svn/project1
As with the local file access, every user needs an ssh access to the server (with a local account) and also read/write access. This method might be suitable for a small group. All users could belong to a subversion group which owns the repository, for example:
# groupadd subversion
# groupmod -A user1 subversion
# chown -R root:subversion /home/svn
# chmod -R 770 /home/svn
Remote access with http (apache)
Remote access over http (https) is the only good solution for a larger user group. This method uses the apache authentication, not the local accounts. This is a typical but small apache configuration:
LoadModule dav_module modules/mod_dav.so
LoadModule dav_svn_module modules/mod_dav_svn.so
LoadModule authz_svn_module modules/mod_authz_svn.so # Only for access control
<Location /svn>
DAV svn
# any "/svn/foo" URL will map to a repository /home/svn/foo
SVNParentPath /home/svn
AuthType Basic
AuthName "Subversion repository"
AuthzSVNAccessFile /etc/apache2/svn.acl
AuthUserFile /etc/apache2/svn-passwd
Require valid-user
</Location>
The apache server needs full access to the repository:
# chown -R www:www /home/svn
Create a user with htpasswd2:
# htpasswd -c /etc/svn-passwd user1 # -c creates the file
Access control svn.acl example
# Default it read access. "* =" would be default no access
[/]
* = r
[groups]
project1-developers = joe, jack, jane
# Give write access to the developers
[project1:]
@project1-developers = rw
SVN commands and usageSee also the
Subversion Quick Reference Card.
Tortoise SVNis a nice Windows interface.
Import
A new project, that is a directory with some files, is imported into the repository with the
importcommand. Import is also used to add a directory with its content to an existing project.
# svn help import # Get help for any command
# Add a new directory (with content) into the src dir on project1
# svn import /project1/newdir http://host.url/svn/project1/trunk/src -m 'add newdir'
Typical SVN commands
# svn co http://host.url/svn/project1/trunk # Checkout the most recent version
# Tags and branches are created by copying
# svn mkdir http://host.url/svn/project1/tags/ # Create the tags directory
# svn copy -m "Tag rc1 rel." http://host.url/svn/project1/trunk \
http://host.url/svn/project1/tags/1.0rc1
# svn status [--verbose] # Check files status into working dir
# svn add src/file.h src/file.cpp # Add two files
# svn commit -m 'Added new class file' # Commit the changes with a message
# svn ls http://host.url/svn/project1/tags/ # List all tags
# svn move foo.c bar.c # Move (rename) files
# svn delete some_old_file # Delete files
less
less命令用来在控制台中分屏显示文本文档。它在许多发行版中可用。
# less unixtoolbox.xhtml
一些重要指令(^N 代表 [control]-[N]):
- h H 显示指令的汇总列表
- f ^F ^V SPACE 向前滚动一屏(或者 N 行)
- b ^B ESC-v 向后滚动一屏(或者 N 行)
- F 向前滚动;类似于"tail -f"
- /pattern 向前搜索匹配该模式的行
- ?pattern 向后搜索匹配该模式的行
- n 重复之前的搜索
- N 反方向重复之前的搜索
- q 退出
vi
Vi 在任何 Linux/Unix 发行安装版(gentoo 没有?)上都存在。因此,我们有必要了解一些基本的命令。Vi 有两个模式:命令模式和插入模式。使用 [ESC] 键可进入命令模式,使用 i 键可进入插入模式。如果你迷失了,可在命令模式下键入
: help。
编辑器
nano 和
pico通常也都可用,而且更容易(IMHO)使用。
Quit
- :w newfilename 保存文件为 newfilename
- :wq or :x 保存并退出
- :q! 退出但不保存
移动和查找
- /string 向前查找 string
- ?string 向后查找 string
- n 同方向重复上一次搜索命令
- N 反方向重复上一次搜索命令
- { 光标移至段落结尾
- } 光标移至段落开头
- 1G 光标移至文件的第一行首
- nG 光标移至文件的第 n 行首
- G 光标移至文件的最后一行首
- :%s/OLD/NEW/g 替换所有查找到的 OLD 为 NEW
删除文本
- dd 删除当前行
- D 删除光标到当前行末尾的字符
- dw 删除单词
- x 删除字符
- u 回复上一次操作
- U 回复所有此行的更改
mail
mail命令是一个读取和发送邮件的应用程序,她通常已安装。要发送一封邮件,可以简单的输入 "mail user@domain"。其第一行为主题,然后是邮件内容。在一个新行中使用单个点(.)来结束并发送邮件。例子:
# mail c@cb.vu
Subject: Your text is full of typos
"For a moment, nothing happened. Then, after a second or so,
nothing continued to happen."
.
EOT
#
这同样可用于管道:
# echo "This is the mail body" | mail c@cb.vu
也是测试邮件服务器的简单方法。
tar
命令
tar(磁带存档) 可以为文件和目录创建档案。归档文件 .tar 是未压缩的,一个压缩过的归档文件的后缀是 .tgz 或 .tar.gz (zip) 或者 .tbz (bzip2)。不要使用绝对路径建立一个归档文件,你可能要解开这个归档文件到某个地方。一些常用命令如下:
创建
# cd /
# tar -cf home.tar home/ # 归档整个 /home 目录(c 为创建)
# tar -czf home.tgz home/ # 等同于 zip 压缩
# tar -cjf home.tbz home/ # 等同于 bzip2 压缩
从一个目录树中只包含一个(或2个)目录,并保持相对目录结构。举个例子,/usr/local/etc 和 /usr/local/www,它们在归档文件中的第一层目录是 local/。
# tar -C /usr -czf local.tgz local/etc local/www
# tar -C /usr -xzf local.tgz # 释放 local 目录到 /usr
# cd /usr; tar -xzf local.tgz # 同上面一样
释放(Extract)
# tar -tzf home.tgz # 列出归档文件中的所有文件,并不释放
# tar -xf home.tar # 释放归档文件(x 为释放)
# tar -xzf home.tgz # 等同于 zip 压缩
# tar -xjf home.tgz # 等同于 bzip2 压缩
# tar -xjf home.tgz home/colin/file.txt # 释放单个文件
更高级的
# tar c dir/ | gzip | ssh user@remote 'dd of=dir.tgz' # 归档压缩 dir/ 目录并存储到远程主机上
# tar cvf - `find . -print` > backup.tar # 归档当前目录
# tar -cf - -C /etc . | tar xpf - -C /backup/etc # 拷贝目录
# tar -cf - -C /etc . | ssh user@remote tar xpf - -C /backup/etc # 远程拷贝
# tar -czf home.tgz --exclude '*.o' --exclude 'tmp/' home/
dd
程序
dd (磁盘备份(disk dump) 或 destroy disk,也可看
dd 的含义) 用来拷贝分区、磁盘或者其它拷贝。通常这么用:
# dd if=<source> of=<target> bs=<byte size> conv=<conversion>
重要的 conv 选项:
- notrunc 不截短输出文件
- noerror 出错时不停止处理(e.g. 坏扇区)
- sync 把每个输入块填充到ibs个字节,不足部分用空(NUL)字符补齐
默认字节大小为 512 (一个扇区)。MBR 处于磁盘的第一个扇区,之后的 63 个扇区是空的。较大的字节大小可以加快拷贝速度但也需要更多的内存。
备份和恢复
# dd if=/dev/hda of=/dev/hdc bs=16065b # 拷贝磁盘到磁盘(相同大小)
# dd if=/dev/sda7 of /home/root.img bs=4096 conv=notrunc,noerror # 备份 /
# dd if /home/root.img of=/dev/sda7 bs=4096 conv=notrunc,noerror # 恢复 /
# dd bs=1M if=/dev/ad4s3e | gzip -c > ad4s3e.gz # 压缩备份
# gunzip -dc ad4s3e.gz | dd of=/dev/ad0s3e bs=1M # 解压恢复
# dd bs=1M if=/dev/ad4s3e | gzip | ssh eedcoba@fry 'dd of=ad4s3e.gz' # 也可为远程的
# gunzip -dc ad4s3e.gz | ssh eedcoba@host 'dd of=/dev/ad0s3e bs=1M'
# dd if=/dev/ad0 of=/dev/ad2 skip=1 seek=1 bs=4k conv=noerror # 忽略 MBR
# 如果目标(ad2)比较小,这是必须的。
恢复
该
dd 命令会读取分区的
每一个区块,即所有区块。对于有问题的区块,最好使用
conv=sync,noerror 选项,dd 将会跳过坏的区块并入 0。因此,这就是设置块大小等于或小于磁盘块大小的重要性。1k 大小似乎安全,用
bs=1k 来设置它。假如一个磁盘有坏扇区并且有个分区的数据要恢复,那么用 dd 工具创建一个镜像文件,挂载这个镜像文件,然后拷贝内容到新的磁盘中。如果用了
noerror选项,dd 会跳过坏扇区并写入 0,也即坏扇区中的内容会丢失。
# dd if=/dev/hda of=/dev/null bs=1m # 检查坏扇区
# dd bs=1k if=/dev/hda1 conv=sync,noerror,notrunc | gzip | ssh \ # 发送到远程
root@fry 'dd of=hda1.gz bs=1k'
# dd bs=1k if=/dev/hda1 conv=sync,noerror,notrunc of=hda1.img # 存储为一个映像文件
# mount -o loop /hda1.img /mnt #
挂载这个映像文件# rsync -ax /mnt/ /newdisk/ # 拷贝到一个新磁盘
# dd if=/dev/hda of=/dev/hda # 刷新磁状态
# 上面的命令对于刷新磁盘(refresh disk)很有用。这绝对安全,但必须先卸载磁盘。
删除
# dd if=/dev/zero of=/dev/hdc # 删除全部数据
# dd if=/dev/urandom of=/dev/hdc # 更好的删除全部数据
# kill -USR1 PID # 查看 dd 进度(仅Linux!)
MBR 技巧
MBR 包含了引导程序和分区表,它的大小为 512 字节。前 446 字节为引导程序,446 到 512 字节为分区表。
# dd if=/dev/sda of=/mbr_sda.bak bs=512 count=1 # 完全备份 MBR
# dd if=/dev/zero of=/dev/sda bs=512 count=1 # 删除 MBR 和分区表
# dd if=/mbr_sda.bak of=/dev/sda bs=512 count=1 # 完全恢复MBR
# dd if=/mbr_sda.bak of=/dev/sda bs=446 count=1 # 仅回复引导程序
# dd if=/mbr_sda.bak of=/dev/sda bs=1 count=64 skip=446 seek=446 # 恢复分区表
screen
Screen 提供了两个主要功能:
- 在一个终端内运行多个终端会话(terminal session)。
- 一个已启动的程序与运行它的真实终端分离的,因此可运行于后台。真实的终端可以被关闭,还可以在稍后再重新接上(reattached)。
简短实例
开启 screen:
# screen
在 screen 会话中,我们可以开启一个长时间运行的程序(如 top)。Detach 这个终端,之后可以从其他机器 reattach 这个相同的终端(比如通过 ssh)。
# top
现在用 Ctrl-a Ctrl-d来 detach。Reattach 终端:
# screen -r
或更好的:
# screen -R -D
现在 attach 到这里。具体意思是:先试图恢复离线的 screen 会话。若找不到离线的 screen 会话,即建立新的 screen 会话给用户。
Screen 命令 (在 screen 中)
所有命令都以 Ctrl-a开始。
- Ctrl-a ? 各功能的帮助摘要
- Ctrl-a c 创建一个新的 window (终端)
- Ctrl-a Ctrl-n 和 Ctrl-a Ctrl-p 切换到下一个或前一个 window
- Ctrl-a Ctrl-N N 为 0 到 9 的数字,用来切换到相对应的 window
- Ctrl-a " 获取所有正在运行的 window 的可导航的列表
- Ctrl-a a 清楚错误的 Ctrl-a
- Ctrl-a Ctrl-d 断开所有会话,会话中所有任务运行于后台
- Ctrl-a x 用密码锁柱 screen 终端
当程序内部运行终端关闭并且你登出该终端时,该 screen 会话就会被终止。
Find
一些重要选项:
- -x (BSD) -xdev (Linux) 留于同一文件系统 (fstab 中的 dev)
- -exec cmd {} \; 执行命令并用全路径替换 {}
- -iname 同 -name 一样,但不区分大小写
- -ls 显示关于文件的信息(同 ls -la)
- -size n n 为 +-n (k M G T P)
- -cmin n 查找系统中最后 n 分钟改变文件状态的文件
# find . -type f ! -perm -444 # 寻找所有无法读取的文件
# find . -type d ! -perm -111 # 寻找所有无法访问的目录
# find /home/user/ -cmin 10 -print # 寻找最后 10 分钟创建或修改的文件
# find . -name '*.[ch]' | xargs grep -E 'expr' # 在当前目录及子目录搜索 'expr' 表达式
# find / -name "*.core" | xargs rm # 寻找 core 垃圾并删除它们(也可试试 core.*)
# find / -name "*.core" -print -exec rm {} \; # 另一种语法
# 寻找图像文件并创建一个归档文件,iname 为不区分大小写。-r 为附加
# find . \( -iname "*.png" -o -iname "*.jpg" \) -print -exec tar -rf images.tar {} \;
# find . -type f -name "*.txt" ! -name README.txt -print # 除 README.txt 的文件
# find /var/ -size +10M -exec ls -lh {} \; # 查找 > 10 MB 的文件
# find /var/ -size +10M -ls # 这个更简单
# find . -size +10M -size -50M -print
# find /usr/ports/ -name work -type d -print -exec rm -rf {} \; # 清理 port
# 以 SUID 查找文件;这些文件很脆弱,必须保持安全。
# find / -type f -user root -perm -4000 -exec ls -l {} \;
小心 xarg 或 exec,因为当文件或目录中包含空格时可能会返回错误的结果。在有疑惑时用 "-print0 | xargs -0" 代替 "| xargs"。选项 -print0 必须在 find 命令的最后。看这个不错的
find 迷你教程.
# find . -type f | xargs ls -l # 不能工作于有空格的名字
# find . -type f -print0 | xargs -0 ls -l # 可工作于有空格的名字
# find . -type f -exec ls -l '{}' \; # 或使用用于 -exec 的引用 '{}'
混杂的
# which command # 显示命令的全路径名
# time command # 显示一个命令执行完成所用的时间
# time cat # 使用 time 作为秒表,用 Ctrl-c 来停止
# set | grep $USER # 列显当前环境变量
# cal -3 # 显示三个月日历
# date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]
# date 10022155 # 设置日期和时间
# whatis grep # 显示命令的简短信息
# whereis java # 查询命令的的路径和标准目录
# setenv varname value # 设置环境变量,设置变量 varname 的值为 value (csh/tcsh)
# export varname="value" # 设置环境变量,设置变量 varname 的值为 value (sh/ksh/bash)
# pwd # 显示当前工作目录
# mkdir -p /path/to/dir # 如果存在不显示错误,建立所需的上级目录
# mkdir -p project/{bin,src,obj,doc/{html,man,pdf},debug/some/more/dirs}
# rmdir /path/to/dir # 移除目录
# rm -rf /path/to/dir # 移除目录和其内容(强制)
# cp -la /dir1 /dir2 # 存档、硬连接目录所有文件,用来替代拷贝
# cp -lpR /dir1 /dir2 # 同上 (FreeBSD)
# cp unixtoolbox.xhtml{,.bak} # 拷贝文件成新扩展名的快速方法
# mv /dir1 /dir2 # 修改目录名
列出已安装过的软件包
# rpm -qa # 列出已安装过的软件包(RH, SuSE, 基于 RPM 的)
# dpkg -l # Debian, Ubuntu
# pkg_info # 列出所有已安装过的软件包(FreeBSD)
# pkg_info -W smbd # 查看 smbd 安装了那些软件包(FreeBSD)
# pkginfo # Solaris
添加/删除软件
前端界面:SuSE 为 yast2/yast,Red Hat 为 redhat-config-packages。
# rpm -i pkgname.rpm # 安装软件包(RH, SuSE, 基于 RPM 的)
# rpm -e pkgname # 删除软件包
Debian
# apt-get update # 更新源列表
# apt-get install emacs # 安装 emacs 软件包
# dpkg --remove emacs # 删除 emacs 软件包
# dpkg -S file # 查找拥有该 file 的软件包
Gentoo
Gentoo 使用 emerge 作为 "Portage" 软件包管理系统的核心。
# emerge --sync # 同步更新本地 protage 树
# emerge -u packagename # 安装或更新一个软件包
# emerge -C packagename # 删除软件包
# revdep-rebuild # 修复依赖关系的缺失
Solaris
< cdrom> 路径通常为
/cdrom/cdrom0.
# pkgadd -d <cdrom>/Solaris_9/Product SUNWgtar
# pkgadd -d SUNWgtar # 添加下载的软件包(先要 bunzip2)
# pkgrm SUNWgtar # 删除软件包
FreeBSD
# pkg_add -r rsync # 获取并安装 rsync
# pkg_delete /var/db/pkg/rsync-xx # 删除 rsync 软件包
可使用
PACKAGESITE环境变量来设置哪里可以获取软件包。举个例子:
# export PACKAGESITE=ftp://ftp.freebsd.org/pub/FreeBSD/ports/i386/packages/Latest/
# or ftp://ftp.freebsd.org/pub/FreeBSD/ports/i386/packages-6-stable/Latest/
FreeBSD ports
Port 树
/usr/ports/ 是一个准备编译和安装的软件集。可用
portsnap工具来跟新 port。
# portsnap fetch extract # 当第一次运行这个命令,会创建 port 树
# portsnap fetch update # 跟新 port 树
# cd /usr/ports/net/rsync/ # 选择软件安装目录
# make install distclean # 安装并清理(也可看 man ports)
# make package # Make 一个二进制软件包
库路径
由于复杂的依赖关系和运行时链接,程序难于分发或拷贝到其他系统。不过对于较少依赖关系的小程序,缺失的库可被拷贝过去。运行时库(即缺失的库)可用
ldd 和
ldconfig来检查和管理。
# ldd /usr/bin/rsync # 列出所有所需的运行时库
# ldconfig -n /path/to/libs/ # 添加一个路径到共享库目录Add a path to the shared libraries directories
# ldconfig -m /path/to/libs/ # FreeBSD
# LD_LIBRARY_PATH # 设置连接库路径的环境变量
有时候需要转换一个视频、音频文件或者文档成其他格式。
文本编码
文本编码可以得到完全错误的,特别是当语言需要某些特殊字符像 àäç。命令
iconv可以从一个编码转换成另一个编码。
# iconv -f <from_encoding> -t <to_encoding> <input_file>
# iconv -f ISO8859-1 -t UTF-8 -o file.input > file_utf8
# iconv -l # 列显系统所支持的字符编码
若文档显示良好,通常都可不使用 -f 选项,iconv 会使用本地字符集(char-set)。
Unix - DOS 新行
在 Unix Shell 中转换 DOS (CR/LF) 到 Unix (LF) 新行格式。也可使用
dos2unix 和
unix2dos工具,如果你有它们的话。
# sed 's/.$//' dosfile.txt > unixfile.txt
在 Windows 环境中转换 Unix 到 Dos 新行格式。需要在 mingw 或 cygwin 中使用 sed。
# sed -n p unixfile.txt > dosfile.txt
PDF 转换成 Jpeg 和 连接一串 PDF 文件
用
gs (GhostScript) 工具转换 PDF 文档的每一页成 jpeg (或 png)图像。也可以使用更短的
convert(来自 ImageMagick 或 GraphicsMagick 工具) 命令。
# gs -dBATCH -dNOPAUSE -sDEVICE=jpeg -r150 -dTextAlphaBits=4 -dGraphicsAlphaBits=4 \
-dMaxStripSize=8192 -sOutputFile=unixtoolbox_%d.jpg unixtoolbox.pdf
# convert unixtoolbox.pdf unixtoolbox-%03d.png
# convert *.jpeg images.pdf # 把所有图片转换成一份简单的 PDF 文档
Ghostscript 同样可连接多个 pdf 文件成一份 PDF 文档。这仅可工作于这些 PDF 文件都 "呈现一致(well behaved)" 的情况下。
# gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=all.pdf \
file1.pdf file2.pdf ... # 在 Windows 上使用 '#' 代替 '='
视频转换
使用 mpeg4 编码压缩佳能数码相机视频并修复无用音质。
# mencoder -o videoout.avi -oac mp3lame -ovc lavc -srate 11025 \
-channels 1 -af-adv force=1 -lameopts preset=medium -lavcopts \
vcodec=msmpeg4v2:vbitrate=600 -mc 0 vidoein.AVI
对于声音的处理可看
sox。
拷贝音频光盘
程序
cdparanoia 可以保存音轨(FreeBSD port 在 audio/cdparanoia/),
oggenc 可编码 Ogg Vorbis 格式,
lame可转换成 mp3。
# cdparanoia -B # 拷贝音轨成 wav 文件到当前目录列表(dir)
# lame -b 256 in.wav out.mp3 # 编码成 256 kb/s 的 mp3
# for i in *.wav; do lame -b 256 $i `basename $i .wav`.mp3; done
# oggenc in.wav -b 256 out.ogg # 编码成 256 kb/s 的 Ogg Vorbis
打印命令 lpr
# lpr unixtoolbox.ps # 用默认打印机打印
# export PRINTER=hp4600 # 更改默认打印机
# lpr -Php4500 #2 unixtoolbox.ps # 指定打印机 hp4500 并打印 2 份
# lpr -o Duplex=DuplexNoTumble ... # 启用双面打印
# lpr -o PageSize=A4,Duplex=DuplexNoTumble ...
# lpq # 查看默认打印机的队列
# lpq -l -Php4500 # 详细显示打印机队列信息
# lprm - # 删除所有打印机内的用户打印作业
# lprm -Php4500 3186 # 删除作业 3186。可使用 lpq 查看作业号
# lpc status # 列印所有可用打印机
# lpc status hp4500 # 如果打印机在线,查看其状态和列队长度
当要打印 PDF 文件时,一些打印设备不具备处理 postscript 的能力。可以这样解决:
# gs -dSAFER -dNOPAUSE -sDEVICE=deskjet -sOutputFile=\|lpr file.pdf
PostgreSQL
更改 root 用户或其它用户的密码
# psql -d template1 -U pgsql
> alter user pgsql with password 'pgsql_password'; # pgsql 为需要更改密码的用户名
创建用户和数据库
命令
createuser,
dropuser,
createdb 和
dropdb等同于 SQL 命令的快捷方式。我们创建一个新用户叫 bob 和一个数据库叫 bobdb;使用数据库的超级用户 pgsql 来创建:
# createuser -U pgsql -P bob # -P 会请求一个秘密
# createdb -U pgsql -O bob bobdb # 新数据库 bobdn 的所有者是 bob
# dropdb bobdb # 删除数据库 bobdb
# dropuser bob # 删除用户 bob
一般数据库认证机制配置在 pg_hba.conf 文件中。
允许远程访问
文件
$PGSQL_DATA_D/postgresql.conf 可指定绑定地址。对于 Postgres 8.x 通常为
listen_addresses = '*'。
文件
$PGSQL_DATA_D/pg_hba.conf定义了访问控制。举例:
# TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD
host bobdb bob 212.117.81.42 255.255.255.255 password
host all all 0.0.0.0/0 password
备份和恢复
使用 pgsql 或 postgres 用户来完成备份与恢复。下面是备份和恢复单个数据库:
# pg_dump --clean dbname > dbname_sql.dump
# psql dbname < dbname_sql.dump
备份和恢复所有数据库(包括用户):
# pg_dumpall --clean > full.dump
# psql -f full.dump postgres
在这个例子中,你可以声明任意现有的数据库进行连接,但是如果你是向一个空的数据库集群装载,那么 postgres 应该是比较好的选择。
MySQL
更改 mysql root 用户或其它用户的密码
方法 1
# /etc/init.d/mysql stop
or
# killall mysqld
# mysqld --skip-grant-tables
# mysqladmin -u root password 'newpasswd'
# /etc/init.d/mysql start
方法 2
# mysql -u root mysql
mysql> UPDATE USER SET PASSWORD=PASSWORD("newpassword") where user='root';
mysql> FLUSH PRIVILEGES; # 使用用户名替代"root"
mysql> quit
创建用户和数据库
# mysql -u root mysql
mysql> CREATE DATABASE bobdb;
mysql> GRANT ALL ON *.* TO 'bob'@'%' IDENTIFIED BY 'pwd';
# 使用 localhost 替代 % 来限制网络访问
mysql> DROP DATABASE bobdb; # 删除数据库 bobdb
mysql> DROP USER bob; # 删除用户 bob
mysql> DELETE FROM mysql.user WHERE user='bob and host='hostname';
# 删除 mysql 数据库 user 表中 user=bob,host=hostname 的记录
mysql> FLUSH PRIVILEGES;
允许远程访问
远程访问通常允许单个数据库,而不是所有的数据库。文件
/etc/my.cnf 包含约定的 IP 地址。通常为
bind-address =绑定地址。
# mysql -u root mysql
mysql> GRANT ALL ON bobdb.* TO bob@'xxx.xxx.xxx.xxx' IDENTIFIED BY 'PASSWORD';
mysql> REVOKE GRANT OPTION ON foo.* FROM bar@'xxx.xxx.xxx.xxx';
mysql> FLUSH PRIVILEGES; # 使用 'hostname' 也可为 '%' 来完全访问
备份和恢复
备份和恢复单个数据库:
# mysqldump -u root -psecret --add-drop-database dbname > dbname_sql.dump
# mysql -u root -psecret -D dbname < dbname_sql.dump
备份和恢复所有的数据库:
# mysqldump -u root -psecret --add-drop-database --all-databases > full.dump
# mysql -u root -psecret < full.dump
这里 mysql root 的密码为 "secret",-p 选项后面没有空格。当单独使用 -p 选项(不跟密码),命令行提示符后会要求输入密码。
SQLite
SQLite是一个小而强大的、独立的(self-contained)、无服务器的(serverless)、零配置的(zero-configuration) SQL 数据库。
备份和恢复
实用备份和恢复 SQLite 数据库命令。举个例子,你可以编辑备份文件来修改字段的属性和类型,然后再恢复这个数据库。这比使用 SQL 命令来得容易。对于 3.x 数据库可使用
sqlite3。
# sqlite database.db .dump > dump.sql # 备份
# sqlite database.db < dump.sql # 恢复
转换 2.x 到 3.x 数据库
sqlite database_v2.db .dump | sqlite3 database_v3.db
磁盘限额用来限制磁盘空间大小和/或用户(或用户组)可用的文件数。The quotas are allocated on a per-file system basis and are enforced by the kernel.
Linux 设置
Quota 工具包通常已安装,其包含一些命令行工具。
在 fstab 中激活用户配额并重新挂载分区。如果分区正在使用,关闭所有使用的文件,或者重启系统。添加
usrquota到 fstab 的挂载类型中,举个例子:
/dev/sda2 /home reiserfs rw,acl,user_xattr,usrquota 1 1
# mount -o remount /home
# mount # 检查 usrquota 已经激活,否则重启
用
quotacheck初始化 quota.user 文件。
# quotacheck -vum /home
# chmod 644 /home/aquota.user # 让用户检查自己的配额
用脚本(e.g. SuSE 的 /etc/init.d/quotad)或
quotaon来启用限额:
quotaon -vu /home
检查配额启用情况:
quota -v
FreeBSD 设置
Quota 工具是 FreeBSD 基本系统的一部分,然而内核需要 quota 选项。如果不存在,新增它并
重新编译内核。
options QUOTA
与 Linux 一样,添加限额到 fstab 选项(是 userquota,而不是 usrquota)中:
/dev/ad0s1d /home ufs rw,noatime,userquota 2 2
# mount /home # 重新挂载分区
在 /etc/rc.conf 中启用磁盘限额并开启 quota 服务。
# grep quotas /etc/rc.conf
enable_quotas="YES" # 在启动时打开限额(或者 "NO")
check_quotas="YES" # 在启动时检查限额(或者 "NO")
# /etc/rc.d/quota start
分配限额
磁盘限额默认并不限制(设置为0)。用
edquota 来对单用户进行限制。一个 quota 也可给许多用户复用。虽然 quota 实现之间的文件结构不同,但其原理是相同的:限制节点(inodes)数量以及使用者可以取用的磁盘区块数量。
Only change the values of soft and hard. 如果未指定,默认区块大小为 1k。使用
edquota -t设置 grace 时间。举个例子:
# edquota -u colin
Linux
Disk quotas for user colin (uid 1007):
Filesystem blocks soft hard inodes soft hard
/dev/sda8 108 1000 2000 1 0 0
FreeBSD
Quotas for user colin:
/home: kbytes in use: 504184, limits (soft = 700000, hard = 800000)
inodes in use: 1792, limits (soft = 0, hard = 0)
给许多用户分配限额
命令
edquota -p用来复用一个 quota 给其他用户。举个例子,复用所指用户的限额给所有用户:
# edquota -p refuser `awk -F: $3 > 499 {print $1}' /etc/passwd`
# edquota -p refuser user1 user2 # 复用给 2 个用户
检查
用户只需输入
quota(文件 quota.user 必须可读) 来可以检查他们的限额。Root 可以查看所有用户的限额。
# quota -u colin # 查看用户的限额
# repquota /home # 所有用户在这个分区上的限额情况
许多 Linux 发行版使用 BASH Shell,BSD 使用的是 tcsh,Bourne Shell 仅用于脚本。过滤器(Filter)非常有用并且可用于管道(pipe):
- grep 模式匹配
- sed 查找并替换字符串或字符
- cut 从一个标记开始打印所指定列数据
- sort 按字母或数字排序
- uniq 删除一个文件中重复行
举个例子,一次使用所有命令:
# ifconfig | sed 's/ / /g' | cut -d" " -f1 | uniq | grep -E "[a-z0-9]+" | sort -r
# ifconfig | sed '/.*inet addr:/!d;s///;s/ .*//'|sort -t. -k1,1n -k2,2n -k3,3n -k4,4n
sed 的模式字符串中的第一个字符是一个 tab。要在命令控制台中输入 tab,可以使用 ctrl-v ctrl-tab。
bash
Bash、sh 的重定向和管道:
# cmd 1> file # 重定向标准输出到 file。
# cmd 2> file # 重定向标准错误输出到 file。
# cmd 1>> file # 重定向标准输出并追加到 file。
# cmd &> file # 重定向标准输出和标准错误输出到 file。
# cmd >file 2>&1 # 重定向标准错误输出到标准输出然后重定向到 file。
# cmd1 | cmd2 # cmd1 的输出通过管道连接到 cmd2 的输入
# cmd1 2>&1 | cmd2 # cmd1 的输出和错误输出通过管道连接到 cmd2 的输入
修改你的配置文件 ~/.bashrc (也可以是 ~/.bash_profile)。下列条目非常有用,使用". .bashrc"重新加载。
# in .bashrc
bind '"\e[A"':history-search-backward # 使用上下键查找
bind '"\e[B"':history-search-forward # 历史命令。无价之宝!
set -o emacs # Set emacs mode in bash (看下面)
set bell-style visible # Do not beep, inverse colors
# 设置一个漂亮的提示符像 [user@host]/path/todir>
PS1="\[\033[1;30m\][\[\033[1;34m\]\u\[\033[1;30m\]"
PS1="$PS1@\[\033[0;33m\]\h\[\033[1;30m\]]\[\033[0;37m\]"
PS1="$PS1\w\[\033[1;30m\]>\[\033[0m\]"
# 要检查当前可用别名(alias),只需简单输入命令 alias
alias ls='ls -aF' # 添加指示符(*/=>@| 其中之一)
alias ll='ls -aFls' # 清单
alias la='ls -all'
alias ..='cd ..'
alias ...='cd ../..'
export HISTFILESIZE=5000 # 巨大的历史记录
export CLICOLOR=1 # 使用颜色(如果可用)
export LSCOLORS=ExGxFxdxCxDxDxBxBxExEx
tcsh
Tcsh、csh 的重定向和管道(> 和 >> 同 sh 中一样):
# cmd >& file # 重定向标准输出和标准错误输出到 file。
# cmd >>& file # 追加标准输出和标准错误输出到 file。
# cmd1 | cmd2 # cmd1 的输出通过管道连接到 cmd2 的输入
# cmd1 |& cmd2 # cmd1 的输出和错误输出通过管道连接到 cmd2 的输入
Csh/tcsh 的设置在
~/.cshrc中,使用"source .cshrc"来重新加载。例子:
# in .cshrc
alias ls 'ls -aF'
alias ll 'ls -aFls'
alias la 'ls -all'
alias .. 'cd ..'
alias ... 'cd ../..'
set prompt = "%B%n%b@%B%m%b%/> " # 像 user@host/path/todir>
set history = 5000
set savehist = ( 6000 merge )
set autolist # 控制命令补全和变量补全
set visiblebell # 使用闪动屏幕的方式来取代蜂鸣器鸣叫
# Bindkey 和颜色
bindkey -e Select Emacs bindings # 将命令行编辑器切换到emacs模式
bindkey -k up history-search-backward # 使用上下键来搜索
bindkey -k down history-search-forward
setenv CLICOLOR 1 # 使用颜色(可能的话)
setenv LSCOLORS ExGxFxdxCxDxDxBxBxExEx
该 emacs 模式将使用 emacs 快捷键来修改命令提示行。这是非常有用的(不单为 Emacs 用户)。最常用的命令如下:
- C-a 移动光标到行头
- C-e 移动光标到行尾
- M-b 移动光标到前一个单词
- M-f 移动光标到后一个单词
- M-d 剪切下一个单词
- C-w 剪切最后一个单词
- C-u 剪切光标前所有字符
- C-k 剪切光标后所有字符
- C-y 粘帖最后剪切的字符(简易的粘帖)
- C-_ 重做
注意: C- = 按住 control 键,M- = 按住 meta (它通常为 alt 或者 escape)键。
Bourne shell (/bin/sh) 存在于所有的 Unix 系统上,并且用她写的脚本是(完全)可移植的;
man 1 sh是一个好的参考。
基础
变量和参数
使用
variable=value的命令格式设置变量,其中 variable 是变量名称,value是打算赋给该变量的值。使用 $variable 获取变量值。
MESSAGE="Hello World" # 赋予一个字符串
PI=3.1415 # 赋予一个十进制小数
N=8
TWON=`expr $N * 2` # 算术表达式(只限整数)
TWON=$(($N * 2)) # 另一种语法
TWOPI=`echo "$PI * 2" | bc -l` # 使用 bc 进行浮点运算
ZERO=`echo "c($PI/4)-sqrt(2)/2" | bc -l`
命令行参数:
$0, $1, $2, ... # $0 命令本身
$# # 命令参数个数
$* # 所有参数(也可以是 $@)
一些特殊的变量
$$ # 当前进程 ID
$? # 最后命令退出状态码
command
if [ $? != 0 ]; then
echo "command failed"
fi
mypath=`pwd`
mypath=${mypath}/file.txt
echo ${mypath##*/} # 只显示文件名
echo ${mypath%%.*} # 除了扩展名的全路径
var2=${var:=string} # 如果var没有被赋值,则string值先赋值给var,
# 然后再赋值给var2
结构控制
for file in `ls`
do
echo $file
done
count=0
while [ $count -lt 5 ]; do
echo $count
sleep 1
count=$(($count + 1))
done
myfunction() {
find . -type f -name "*.$1" -print # $1 为方法的第一个参数
}
myfunction "txt"
产生一个文件
MYHOME=/home/colin
cat > testhome.sh << _EOF
# 所有_EOF前的代码都会进入到 testhome.sh 文件中去
if [ -d "$MYHOME" ] ; then
echo $MYHOME exists
else
echo $MYHOME does not exist
fi
_EOF
sh testhome.sh
Bourne 脚本实例
来一个小实例,此脚本从本 xhtml 文档创建一个 PDF 小册子:
#!/bin/sh
# 此脚本可以创建一份供双面打印机打印的 PDF 格式的书
if [ $# -ne 1 ]; then # 检查参数是否等于 1
echo 1>&2 "Usage: $0 HtmlFile"
exit 1 # 如果不等于1,非0退出
fi
file=$1 # 文件变量
fname=${file%.*} # 文件名变量
fext=${file#*.} # 文件扩展名变量
prince $file -o $fname.pdf # www.princexml.com
pdftops -paper A4 -noshrink $fname.pdf $fname.ps # 创建 postscript 小册子
cat $fname.ps |psbook|psnup -Pa4 -2 |pstops -b "2:0,1U(21cm,29.7cm)" > $fname.book.ps
ps2pdf13 -sPAPERSIZE=a4 -sAutoRotatePages=None $fname.book.ps $fname.book.pdf
# 在 Windows 上使用 #a4 和 #None!
exit 0 # exit 0 意为成功
一些 sed 命令
这里是
单行 sed 命令的金矿。还有一个很好的
sed 介绍及教程。
sed 's/string1/string2/g' # 替换 string1 为 string2
sed -i 's/wroong/wrong/g' *.txt # 用 g 替换所有返回的单词
sed 's/\(.*\)1/\12/g' # 修改 anystring1 为 anystring2
sed '/<p>/,/<\/p>/d' t.xhtml # 删除以 <p> 开始,以 </p> 结尾的行
sed '/ *#/d; /^ *$/d' # 删除注释和空行
sed 's/[ \t]*$//' # 删除行尾空格 (使用 tab 代替 \t)
sed 's/^[ \t]*//;s/[ \t]*$//' # 删除行头尾空格
sed 's/[^*]/[&]/' # 括住首字符 [] top -> [t]op
sed = file | sed 'N;s/\n/\t/' > file.num # 为文件添加行号
正则表达式
一些基本的正则表达式同样可用于 sed。作为一个良好的启蒙,可看
基本正则语法。
[\^$.|?*+() # 特殊字符,其他字符将匹配自身
\ # 转义特殊字符,当成普通字符对待
* # 重复前项 0 次或多次
. # 单个字符除换行符
.* # 匹配 0 个或多个字符
^ # 匹配字符串行开始处
$ # 匹配字符串行结尾处
.$ # 匹配字符串行最后一个字符
^ $ # 匹配单个空格的行
[^A-Z] # 匹配任何以 A-Z 字符开始的行
一些实用命令
下列命令对于包含于一个脚本或者单行命令来说很有用。
sort -t. -k1,1n -k2,2n -k3,3n -k4,4n # 排序 IPv4 格式的 IP 地址
echo 'Test' | tr '[:lower:]' '[:upper:]' # 转换成大写
echo foo.bar | cut -d . -f 1 # 返回 foo
PID=$(ps | grep script.sh | grep bin | awk '{print $1}') # 正在运行名为 script 脚本的 PID
PID=$(ps axww | grep [p]ing | awk '{print $1}') # ping 的 PID (w/o grep pid)
IP=$(ifconfig $INTERFACE | sed '/.*inet addr:/!d;s///;s/ .*//') # Linux
IP=$(ifconfig $INTERFACE | sed '/.*inet /!d;s///;s/ .*//') # FreeBSD
if [ `diff file1 file2 | wc -l` != 0 ]; then [...] fi # 文件改变了?
cat /etc/master.passwd | grep -v root | grep -v \*: | awk -F":" \ # 创建 http passwd
'{ printf("%s:%s\n", $1, $2) }' > /usr/local/etc/apache2/passwd
testuser=$(cat /usr/local/etc/apache2/passwd | grep -v \ # 查看 passwd 中的用户
root | grep -v \*: | awk -F":" '{ printf("%s\n", $1) }' | grep ^user$)
:(){ :|:& };: # bash fork 炸弹。会干掉你的机器
tail +2 file > file2 # 删除文件的第一行
我使用一种小伎俩来一次更改许多文件的扩展名。举个例子,从 .cxx 到 .cpp。排除最后的
| sh 先测试一下。你同样可以使用命令
rename来做这些,如果安装了的话。或者使用 bash 内建命令。
# ls *.cxx | awk -F. '{print "mv "$0" "$1".cpp"}' | sh
# ls *.c | sed "s/.*/cp & &.$(date "+%Y%m%d")/" | sh # 如 拷贝 *.c 成 *.c.20080401
# rename .cxx .cpp *.cxx # 重命名所有 .cxx 成 .cpp
# for i in *.cxx; do mv $i ${i%%.cxx}.cpp; done # bash 内建的
C 基础
strcpy(newstr,str) /* 拷贝 str 到 newstr */
expr1 ? expr2 : expr3 /* if (expr1) expr2 else expr3 */
x = (y > z) ? y : z; /* if (y > z) x = y; else x = z; */
int a[]={0,1,2}; /* 初始化数组 (或者 a[3]={0,1,2}; */
int a[2][3]={{1,2,3},{4,5,6}}; /* 初始化二维数组 */
int i = 12345; /* 从 i 转换成 char str */
char str[10];
sprintf(str, "%d", i);
C 实例
一个最小化的 C 程式 simple.c:
#include <stdio.h>
main() {
int number=42;
printf("The answer is %i\n", number);
}
编译:
# gcc simple.c -o simple
# ./simple
The answer is 42
C++ 基础
*pointer // 指向对象的指针
&obj // 对象 obj 的地址
obj.x // 类(对象) obj 成员 x
pobj->x // 指针 pobj 指向类(对象)成员 x
// (*pobj).x 同 pobj->x
C++ 实例
来一个稍微现实一点的 C++ 程序,我们在一个头文件(IPv4.h)中创建一个类并且实现它(IPv4.cpp),然后创建一个程式来使用其功能。这个类的成员方法实现了 IP 地址从一串整数转换成我们熟知的点分格式。这是一个最小化的 C++ 程式和多源文件(multi-source)的编译。
IPv4 class
IPv4.h:
#ifndef IPV4_H
#define IPV4_H
#include <string>
namespace GenericUtils { // 创建 namespace
class IPv4 { // 类定义
public:
IPv4();
~IPv4();
std::string IPint_to_IPquad(unsigned long ip);// 成员方法接口
};
} //namespace GenericUtils
#endif // IPV4_H
IPv4.cpp:
#include "IPv4.h"
#include <string>
#include <sstream>
using namespace std; // 使用 namespace
using namespace GenericUtils;
IPv4::IPv4() {} // 默认构造/析构函数
IPv4::~IPv4() {}
string IPv4::IPint_to_IPquad(unsigned long ip) { // 成员方法实现
ostringstream ipstr; // 使用字符串流
ipstr << ((ip &0xff000000) >> 24) // 位右移
<< "." << ((ip &0x00ff0000) >> 16)
<< "." << ((ip &0x0000ff00) >> 8)
<< "." << ((ip &0x000000ff));
return ipstr.str();
}
程序 simplecpp.cpp
#include "IPv4.h"
#include <iostream>
#include <string>
using namespace std;
int main (int argc, char* argv[]) {
string ipstr; // 定义变量
unsigned long ipint = 1347861486; // 数字形式的 IP
GenericUtils::IPv4 iputils; // 创建一个类的对象
ipstr = iputils.IPint_to_IPquad(ipint); // 调研类的成员方法
cout << ipint << " = " << ipstr << endl; // 输出结果
return 0;
}
编译和执行:
# g++ -c IPv4.cpp simplecpp.cpp # 编译成目标文件
# g++ IPv4.o simplecpp.o -o simplecpp.exe # 连接目标代码,生成可执行文件
# ./simplecpp.exe
1347861486 = 80.86.187.238
使用
ldd脚本检查并列出可执行程序所依赖的共享库文件。这个命令同样可以用来检查共享库的丢失。
# ldd /sbin/ifconfig
简单的 Makefile
相应的最小化多源文件(multi-source)编译 Makefile 显示如下。每一个命令行必须以
tab开始!可以将一个较长行使用反斜线"\"来分解为多行。
CC = g++
CFLAGS = -O
OBJS = IPv4.o simplecpp.o
simplecpp: ${OBJS}
${CC} -o simplecpp ${CFLAGS} ${OBJS}
clean:
rm -f ${TARGET} ${OBJS}
文档
Linux 文档 en.tldp.org
Linux Man Pages www.linuxmanpages.com
Linux 命令目录 www.oreillynet.com/linux/cmd
Linux doc man howtos linux.die.net
FreeBSD 手册 www.freebsd.org/handbook
FreeBSD Man Pages www.freebsd.org/cgi/man.cgi
FreeBSD 用户 wiki www.freebsdwiki.net
Solaris Man Pages docs.sun.com/app/docs/coll/40.10
其他 Unix/Linux 参考
Rosetta Stone for Unix bhami.com/rosetta.html (a Unix command translator)
Unix guide cross reference unixguide.net/unixguide.shtml
Linux 命令行列表 www.linuxguide.it/commands_list.php
Short Linux reference www.pixelbeat.org/cmdline.html