Changing Ethernet device names in Suse 10
Recently I put a second, faster network card in a server. On booting OpenSuse 10 assigned the new card the name eth2 and the existing, built-in Ethernet device eth0. A number of applications, for example Samba and dnsmasq, typically bind to an Ethernet name rather than a specific IP or MAC address. It is possible to change the individual configuration files for each of these services but this is a little ugly considering my goal was to install the new hardware and disable the existing device, leaving everything else untouched.
A tidier solution is to assign eth0 to the new card and eth1 to the older (unused) device. Figuring out how to do this is a little confusing, there is no Yast option to configure network names and manually editing /etc/sysconfig/network/ifcfg-eth(mac address) provides no help either. Instead you must edit the file /etc/udev/rules.d/30-net_persistent_names.rules and change the device name associated to the relevant network MAC address. In a two card setup the file will look a little like this (each network device entry is on a single line):
SUBSYSTEM=="net", ACTION=="add", SYSFS{address}=="MAC", IMPORT="/sbin/rename_netiface %k eth0"
SUBSYSTEM=="net", ACTION=="add", SYSFS{address}=="MAC", IMPORT="/sbin/rename_netiface %k eth1"
Where SYSFS{address} is set to the MAC addresses of each ethernet device. Change this file so that the correct names are associated to the appropriate MAC devices and reboot. Afterwards you will find your network cards are named in the order that you desire and all the applications that refer to eth0 work as if nothing has changed. It is a simple change and something that really should figure as part of the advanced network device options in Yast.
Export sms for wm6(with filter),短信导出工具
最近入手一台wm6.5的山寨机, 待机时间很长, 一般能达到4天左右, 运行速度也很快, 大体上比较满意, 唯一遗憾的地方是短信功能较弱, 阅读和删除都不方便, 更为变/态的是几乎所有第三方短信软件都无法正常运行,接管不了系统的短信. 我原来购买此机的初衷就是希望可以创建短信文件夹并自动归类,因为公司的报警短信太多, 以致于经常把个人短信淹没.
多次尝试无果后, 最终打算自己写个小程序来解决此问题, 今天花了一整天时间终于搞定, 原来是计划用POOM来读写短信的,后来发现好象没有对应的API,最终使用了MAPI .net来访问短信.开发环境是Visual Studio 2008(C#) + Windows Mobile Sdk 6.0,运行时需要在wm6.5上面安装.NET CF 3.5
此软件的作用是将系统短信导出到文本文件,可导出所有短信,也可以根据多个发件人来导出,导出后可删除原短信.
以下是截图和功能说明:
a.第一个区域,用于需导出的发件人列表,默认只导出这些号码发送的短信
b.第二个区域,用于配置导出的目的文件夹, 文件以YYYYMMDDHHmmss.txt命名
c.勾选"导出所有短信",由忽略第一个区域的发件人配置.
有需要的朋友,可以在本站下载,如果不会使用或对源码有兴趣的,可以给我发邮件.
理论上应该支持wm6.0, 但我本人并没有测试.
[file]attachment/201006/smsexport.zip[/file]
Export sms for wm6(with filter).
题外话:Visual Studio + C# + MSDN确实不错,几年没用也很容易上手.
Linux Daemon Writing HOWTO
Linux Daemon Writing HOWTO
Devin Watson
v1.0, May 2004
This document shows how to write a daemon in Linux using GCC. Knowledge of Linux and a familiarity with C are necessary to use this document. This HOWTO is Copyright by Devin Watson, under the terms of the BSD License.
1. Introduction: What is a Daemon?
2. Getting Started
3. Planning Your Daemon
3.1 What Is It Going To Do?
3.2 How Much Interaction?
4. Basic Daemon Structure
4.1 Forking The Parent Process
4.2 Changing The File Mode Mask (Umask)
4.3 Opening Logs For Writing
4.4 Creating a Unique Session ID (SID)
4.5 Changing The Working Directory
4.6 Closing Standard File Descriptors
5. Writing the Daemon Code
5.1 Initialization
5.2 The Big Loop
6. Putting It All Together
6.1 Complete Sample
1. Introduction: What is a Daemon?
A daemon (or service) is a background process that is designed to run autonomously,with little or not user intervention. The Apache web server http daemon (httpd) is one such example of a daemon. It waits in the background listening on specific ports, and serves up pages or processes scripts, based on the type of request.
Creating a daemon in Linux uses a specific set of rules in a given order. Knowing how they work will help you understand how daemons operate in userland Linux, but can operate with calls to the kernel also. In fact, a few daemons interface with kernel modules that work with hardware devices, such as external controller boards, printers,and PDAs. They are one of the fundamental building blocks in Linux that give it incredible flexibility and power.
Throughout this HOWTO, a very simple daemon will be built in C. As we go along, more code will be added, showing the proper order of execution required to get a daemon up and running.
2. Getting Started
First off, you'll need the following packages installed on your Linux machine to develop daemons, specifically:
GCC 3.2.2 or higher
Linux Development headers and libraries
If your system does not already have these installed (not likely, but check anyway), you'll need them to develop the examples in this HOWTO. To find out what version of GCC you have installed, use:
gcc --version
3. Planning Your Daemon
3.1 What Is It Going To Do?
A daemon should do one thing, and do it well. That one thing may be as complex as managing hundreds of mailboxes on multiple domains, or as simple as writing a report and calling sendmail to mail it out to an admin.
In any case, you should have a good plan going in what the daemon should do. If it is going to interoperate with some other daemons that you may or may not be writing, this is something else to consider as well.
3.2 How Much Interaction?
Daemons should never have direct communication with a user through a terminal. In fact, a daemon shouldn't communicate directly with a user at all. All communication should pass through some sort of interface (which you may or may not have to write), which can be as complex as a GTK+ GUI, or as simple as a signal set.
4. Basic Daemon Structure
When a daemon starts up, it has to do some low-level housework to get itself ready for its real job. This involves a few steps:
Fork off the parent process
Change file mode mask (umask)
Open any logs for writing
Create a unique Session ID (SID)
Change the current working directory to a safe place
Close standard file descriptors
Enter actual daemon code
4.1 Forking The Parent Process
A daemon is started either by the system itself or a user in a terminal or script. When it does start, the process is just like any other executable on the system. To make it truly autonomous, a child process must be created where the actual code is executed. This is known as forking, and it uses the fork() function:
pid_t pid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
Notice the error check right after the call to fork(). When writing a daemon, you will have to code as defensively as possible. In fact, a good percentage of the total code in a daemon consists of nothing but error checking.
The fork() function returns either the process id (PID) of the child process (not equal to zero), or -1 on failure. If the process cannot fork a child, then the daemon should terminate right here.
If the PID returned from fork() did succeed, the parent process must exit gracefully. This may seem strange to anyone who hasn't seen it, but by forking, the child process continues the execution from here on out in the code.
4.2 Changing The File Mode Mask (Umask)
In order to write to any files (including logs) created by the daemon, the file mode mask (umask) must be changed to ensure that they can be written to or read from properly. This is similar to running umask from the command line, but we do it programmatically here. We can use the umask() function to accomplish this:
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
/* Log failure (use syslog if possible) */
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
By setting the umask to 0, we will have full access to the files generated by the daemon. Even if you aren't planning on using any files, it is a good idea to set the umask here anyway, just in case you will be accessing files on the filesystem.
4.3 Opening Logs For Writing
This part is optional, but it is recommended that you open a log file somewhere in the system for writing. This may be the only place you can look for debug information about your daemon.
4.4 Creating a Unique Session ID (SID)
From here, the child process must get a unique SID from the kernel in order to operate. Otherwise, the child process becomes an orphan in the system. The pid_t type, declared in the previous section, is also used to create a new SID for the child process:
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log any failure */
exit(EXIT_FAILURE);
}
Again, the setsid() function has the same return type as fork(). We can apply the same error-checking routine here to see if the function created the SID for the child process.
4.5 Changing The Working Directory
The current working directory should be changed to some place that is guaranteed to always be there. Since many Linux distributions do not completely follow the Linux Filesystem Hierarchy standard, the only directory that is guaranteed to be there is the root (/). We can do this using the chdir() function:
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log any failure here */
exit(EXIT_FAILURE);
}
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log any failure here */
exit(EXIT_FAILURE);
}
Once again, you can see the defensive coding taking place. The chdir() function returns -1 on failure, so be sure to check for that after changing to the root directory within the daemon.
4.6 Closing Standard File Descriptors
One of the last steps in setting up a daemon is closing out the standard file descriptors (STDIN, STDOUT, STDERR). Since a daemon cannot use the terminal, these file descriptors are redundant and a potential security hazard.
The close() function can handle this for us:
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log any failure here */
exit(EXIT_FAILURE);
}
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log any failure here */
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
It's a good idea to stick with the constants defined for the file descriptors, for the greatest portability between system versions.
5. Writing the Daemon Code
5.1 Initialization
At this point, you have basically told Linux that you're a daemon, so now it's time to write the actual daemon code. Initialization is the first step here. Since there can be a multitude of different functions that can be called here to set up your daemon's task, I won't go too deep into here.
The big point here is that, when initializing anything in a daemon, the same defensive coding guidelines apply here. Be as verbose as possible when writing either to the syslog or your own logs. Debugging a daemon can be quite difficult when there isn't enough information available as to the status of the daemon.
5.2 The Big Loop
A daemon's main code is typically inside of an infinite loop. Technically, it isn't an infinite loop, but it is structured as one:
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log any failures here */
exit(EXIT_FAILURE);
}
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log any failures here */
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
/* Daemon-specific initialization goes here */
/* The Big Loop */
while (1) {
/* Do some task here ... */
sleep(30); /* wait 30 seconds */
}
This typical loop is usually a while loop that has an infinite terminating condition, with a call to sleep in there to make it run at specified intervals.
Think of it like a heartbeat: when your heart beats, it performs a few tasks, then waits until the next beat takes place. Many daemons follow this same methodology.
6. Putting It All Together
6.1 Complete Sample
Listed below is a complete sample daemon that shows all of the steps necessary for setup and execution. To run this, simply compile using gcc, and start execution from the command line. To terminate, use the kill command after finding its PID.
I've also put in the correct include statements for interfacing with the syslog, which is recommended at the very least for sending start/stop/pause/die log statements, in addition to using your own logs with the fopen()/fwrite()/fclose() function calls.
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
int main(void) {
/* Our process ID and Session ID */
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
/* Daemon-specific initialization goes here */
/* The Big Loop */
while (1) {
/* Do some task here ... */
sleep(30); /* wait 30 seconds */
}
exit(EXIT_SUCCESS);
}
From here, you can use this skeleton to write your own daemons. Be sure to add in your own logging (or use the syslog facility), and code defensively, code defensively, code defensively!
SSH2下面配置基于key的ssh登录
User configuration of SSH2 becomes smarter than that of SSH1. Now public keys are stored in separate files and one can have multiple host-specific identifications (i.e., private keys). Read the ssh manual page for details. Here I describe most basic usage of SSH2. When you want to login to a remote host (Remote) from a local computer (Local) using SSH2, you do:
1. Create private & public keys of Local, by executing
ssh-keygen (ssh-keygen2) on Local.
Local> ssh-keygen
Generating 1024-bit dsa key pair
9 o.oOo..oOo.o
Key generated.
1024-bit dsa, created by ymmt@Local Wed Sep 23 07:11:02 1998
Passphrase :
Again :
Private key saved to /home/ymmt/.ssh2/id_dsa_1024_a
Public key saved to /home/ymmt/.ssh2/id_dsa_1024_a.pub
ssh-keygen will ask you a passphrase for new key. Enter a
sequence of any ordinal character (white spaces are OK) of proper
length (20 characters or so). ssh-keygen creates a ".ssh2"
directory in your home directory, and stores a new
authentication key in two separate files. One is your private
key and thus it must NOT be opened to anyone but you. In above
example, it is id_dsa_1024_a. The other (id_dsa_1024_a.pub) is
a public key that is safe to be opened and to be distributed
to other computers.
2. Create an "identification" file in your ".ssh2" directory on Local.
Local> cd ~/.ssh2
Local> echo "IdKey id_dsa_1024_a" > identification
This will create a file "identification" in your ".ssh2" directory, which has one line that denotes which file contains your identification. An identification corresponds a passphrase (see above). You can create multiple identifications by executing ssh-keygen again, but rarely you should.
3. Do the same thing (1, and optionally 2) on Remote.
This is needed just to setup ".ssh2" directory on Remote. Passphrase may be different.
4. Copy your public key of Local (id_dsa_1024_a.pub) to ".ssh2"
directory of Remote under the name, say, "Local.pub".
".ssh2" on Remote now contains:
Remote>ls -F ~/.ssh2
Local.pub
authorization
hostkeys/
id_dsa_1024_a
id_dsa_1024_a.pub
identification
random_seed
5. Create an "authorization" file in your ".ssh2" directory on Remote. Add the following one line to "authorization",
Key Local.pub
which directs SSH server to see Local.pub when authorizing your login. If you want to login to Remote from other hosts, create authorization keys on the hosts (step 1 and 2) and repeat step 4 and 5 on Remote.
6. Now you can login to Remote from Local using SSH2!
Try to login:
Local>ssh Remote
Passphrase for key "/home/ymmt/.ssh2/id_dsa1024_a" with
comment "1024-bit dsa, created by ymmt@Local Mon Sep 21
17:53:01 1998":
Enter your passphrase on Local, good luck!
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
bash变量赋值及字符串的操作
1.用户定义的变量
用户定义的变量有字母数字及下划线组成,并且变量名的第一个字符不能为数字.
与其它UNIX名字一样,变量名是大小写敏感的.
对于变量,用户可按如下方式赋值:
name = value
在引用变量时,需在前面加$符号,用户也可以在变量间进行相互赋值,如:
(前面的$是命令提示符)
$ JOHN = john
$ NAME = $JOHN
$ echo Hello $NAME
Hello john
也可以用变量和其他字符组成新的字,这时可能需要把变量用{}括起,如:
$ SAT = Satur
$ echo Today is ${SAT}day
Today is Saturday
对于未赋值的变量,Bash以空值对待,用户也可以用unset命令清除给变量
赋的值.
Bash中还可以使用数组变量,其赋值有两种:
(1) name[index] = value
(2) name = (value1 ... valuen) 此时下标从0开始
数组下标的范围没有任何限制,同时也不必使用连续的分量.
Bash中关于变量的内建命令有:
(1) declare和typeset.两者具有一样的功能.其选项有:
[-/+]a 设置/撤消变量的数组属性
[-/+]i 设置/撤消变量的整数属性
[-/+]r 设置/撤消变量的只读属性
[-/+]x 设置/撤消变量的输出属性
-p var 显示变量属性
(2) export和local.
export把变量输出到环境中,用法为:
export name
export name = value
这里需要简单介绍一下export的作用:当Bash shell执行一个
程序时,将首先为该程序建立一个新的执行环境,称为子shell,
在Bash Shell中变量都是局部的,即它们只是在创建它们的子
Shell中是有意义的,使用export后,变量被设置为全局变量,这
时可以被其它子Shell所识别
local标记变量为局部的(如只能被函数内部使用),用法为:
local name
local name = value
(3) readonly.
指定变量为只读,执行后,改变量不能被再次赋值,用法为:
readonly name
2.位置变量或Shell参数
Bash Shell在解释用户命令时,将把命令行的第一个子作为命令,而其它字作为
参数通过位置变量传递给程序.$1,...,$9分别代表第一,...,九个参数.其中1-9
是真正的参数名,"$"符只是用来标识变量的替换.
位置变量$0指命令对应的可执行名.
其它的还有:
$# 送给命令的参数个数
$@ 所有的参数,每个用双括号括起
$* 所有的参数,用双括号括起
3.与Shell有关的变量
(1) Shell自身设置的一些常用变量:
LINENO 正在执行的命令在脚本中的行号
PWD 用户当前目录的全名
OLDPWD 最近一次执行cd之前,用户当前目录的全名
PPID 父进程ID
$ 当前进程ID
RANDOM 随机数(范围0-32767)
SECONDS Bash Shell的运行时间,单位是秒
REPLY select和read命令使用,以后会讲到
OPTARG
ORTIND 这两个变量由getopt命令设置
UID 当前用户的User ID
_ 上一条命令使用的最后一个参数
(2) 影响Shell行为的一些常用环境变量:
PATH 命令搜索路径,以冒号为分隔符.注意与DOS下不同的是,
当前目录不在系统路径里
HOME 用户home目录的路径名,是cd命令的默认参数
COLUMNS 定义了命令编辑模式下可使用命令行的长度
EDITOR 默认的行编辑器
VISUAL 默认的可视编辑器
FCEDIT 命令fc使用的编辑器
HISTFILE 命令历史文件
HISTSIZE 命令历史文件中最多可包含的命令条数
HISTFILESIZE 命令历史文件中包含的最大行数
IFS 定义SHELL使用的分隔符
LOGNAME 用户登录名
MAIL 指向一个需要SHELL监视其修改时间的文件.当该文件修改后,
SHELL将发消息You hava mail给用户
MAILCHECK SHELL检查MAIL文件的周期,单位是秒
MAILPATH 功能与MAIL类似.但可以用一组文件,以冒号分隔,每个文件后
可跟一个问号和一条发向用户的消息
SHELL SHELL的路径名
TERM 终端类型
TMOUT SHELL自动退出的时间,单位为秒,若设为0则禁止SHELL自动退出
PROMPT_COMMAND 指定在主命令提示符前应执行的命令
PS1 主命令提示符
PS2 二级命令提示符,命令执行过程中要求输入数据时用
PS3 select的命令提示符
PS4 调试命令提示符
MANPATH 寻找手册页的路径,以冒号分隔
LD_LIBRARY_PATH 寻找库的路径,以冒号分隔
bash变量赋值
例子:经常在configure脚本中,会出现以下类似的语句,都表示什么意思呢?
if test -n "${ZSH_VERSION+set}"; then ac_env_build_alias_set=${build_alias+set} test "${ac_configure_args0+set}" != set |
答案及扩展:
变量赋值方式 | str 没有赋值 | str 为空字符串 | str 为非空字符串 | 备注 |
var=${str-expr} | var=expr | var=$str | 对var进行设置 | |
var=${str+expr} | var=$str | var=expr | 对var进行设置,var与str要不都取值,要不都不取值。虽然取值不同 | |
var=${str=expr} | str=expr var=expr | str 不变 var=$str | 对var与str进行设置, var 与str保持一致 | |
var=${str?expr} | expr 输出至 stderr | var=str | 对var进行设置 | |
var=${str:-expr} | var=expr | var=$str | ||
var=${str:+expr} | var= | var=expr | ||
var=${str:=expr} | str=expr var=expr | str 不变 var=$str | ||
var=${str:?expr} | expr 输出至 stderr |
Bash字符串的操作
(一)字符串的替换
(1)
${变量1/查找字符/替换字符}
(说明一下,这个操作中除了第一个参数是变量外其它两个都是字符;还有一点就是这个操作并不是把“变量1”中的字符替换了,详见例子)
例:
str1=abcABCabc123ABC
echo ${str1/bcA/aaa}#这里的abc和aaa都是字符串,而str1是变量,并且这个操作过后str1里的字符串长度不会减少,只是产生了一个 新的字串。
(2)
${变量1/#查找字符/替换字符}
(说明一下,这个操作上和面的是一样的,只不过是从左边开始匹配,并且必须从左边第一个字符开始)
例:
echo ${str1/#bcA/aaa}#这个例子中并不会把bcA换成aaa因为b不是左边第一个开头字符
echo ${str1/#abc/aaa}#这样才行
(3)
${变量1/%查找字符/替换字符}
(与(2)相反,是结尾最后一个字符要匹配才行)
例:
echo ${str1/%3ABC/aaa}#abcABCabc12aaa
(3)
${变量1//查找字符/替换字符}
全部替换
(二)取子串
(1)${变量1:位置}
(说明一下,默认是从左边开始,如果“位置”为负数,则是从右边的第“位置”个字符开始,并且第一个位置为0;从“位置”开始取子串到最后)
例:
str1=abcABCabc123ABC
echo ${str1:(-3)}#会输出ABC
(2)${变量1:开始位置:结束位置}
(3)如果“变量1”为“*”或“@”,“位置”所表示的是第几个参数。
(三)字符串移动
(1)${字串#匹配字串}
(说明一下,这个是从左边第一个开始匹配,剥去最短“匹配字串”)
例:
str1=abcABCabc123
echo ${str1#a*c}#输出ABCabc123
(2)${字串##匹配字串}
(说明一下,这个是从左边第一个开始匹配,剥去最长“匹配字串”)
str1=abcABCabc123
echo ${str1#a*c}#输出123
echo ${str1#b*c}#输出abcABCabc123,因为没有从第一个开始匹配
(3)${字串%匹配字串}
(4)${字串%%匹配字串}
(说明一下,这与上面的(1)(2)是正好相反的,是从最后一个开始匹配的)
(四)字符串长度
${#字串}
例:
str=abcdefg
echo ${#str}#输出7
[转]VIM 文件编码识别与乱码处理
在 Vim 中, 有四个与编码有关的选项, 它们是: fileencodings、 fileencoding、 encoding 和 termencoding。 在实际使用中, 任何一个选项出现错误, 都会导致出现乱码。 因此, 每一个 Vim 用户都应该明确这四个选项的含义。 下面, 我们详细介绍一下这四个选项的含义和作用。
1 encoding
encoding 是 Vim 内部使用的字符编码方式。 当我们设置了 encoding 之后, Vim 内部所有的 buffer、 寄存器、 脚本中的字符串等, 全都使用这个编码。 Vim 在工作的时候, 如果编码方式与它的内部编码不一致, 它会先把编码转换成内部编码。 如果工作用的编码中含有无法转换为内部编码的字符, 在这些字符就会丢失。 因此,在选择 Vim 的内部编码的时候, 一定要使用一种表现能力足够强的编码, 以免影响正常工作。
由于 encoding 选项涉及到 Vim 中所有字符的内部表示, 因此只能在 Vim 启动的时候设置一次。 在 Vim 工作过程中修改 encoding 会造成非常多的问题。 如果没有特别的理由, 请始终将 encoding 设置为 utf-8。 为了避免在非 UTF-8 的系统如 Windows 下, 菜单和系统提示出现乱码, 可同时做这几项设置:
set langmenu=zh_CN.UTF-8
language message zh_CN.UTF-8
2 termencoding
termencoding 是 Vim 用于屏幕显示的编码, 在显示的时候, Vim 会把内部编码转换为屏幕编码, 再用于输出。 内部编码中含有无法转换为屏幕编码的字符时, 该字符会变成问号, 但不会影响对它的编辑操作。 如果 termencoding 没有设置, 则直接使用 encoding 不进行转换。
举个例子, 当你在 Windows 下通过 telnet 登录 Linux 工作站时, 由于 Windows 的 telnet 是 GBK 编码的, 而 Linux 下使用 UTF-8 编码, 你在 telnet 下的 Vim 中就会乱码。 此时有两种消除乱码的方式: 一是把 Vim 的 encoding 改为 gbk, 另一种方法是保持 encoding 为 utf-8, 把 termencoding 改为 gbk, 让 Vim 在显示的时候转码。 显然, 使用前一种方法时, 如果遇到编辑的文件中含有 GBK 无法表示的字符时, 这些字符就会丢失。 但如果使用后一种方法, 虽然由于终端所限, 这些字符无法显示, 但在编辑过程中这些字符是不会丢失的。
对于图形界面下的 GVim, 它的显示不依赖 TERM, 因此 termencoding 对于它没有意义。 在 GTK2 下的 GVim 中, termencoding 永远是 utf-8, 并且不能修改。 而 Windows 下的 GVim 则忽略 termencoding 的存在。
3 fileencoding
当 Vim 从磁盘上读取文件的时候, 会对文件的编码进行探测。 如果文件的编码方式和 Vim 的内部编码方式不同, Vim 就会对编码进行转换。 转换完毕后, Vim 会将 fileencoding 选项设置为文件的编码。 当 Vim 存盘的时候, 如果 encoding 和 fileencoding 不一样, Vim 就会进行编码转换。 因此, 通过打开文件后设置 fileencoding, 我们可以将文件由一种编码转换为另一种编码。 但是, 由前面的介绍可以看出, fileencoding 是在打开文件的时候, 由 Vim 进行探测后自动设置的。 因此, 如果出现乱码, 我们无法通过在打开文件后重新设置 fileencoding 来纠正乱码。
4 fileencodings
编码的自动识别是通过设置 fileencodings 实现的, 注意是复数形式。 fileencodings 是一个用逗号分隔的列表, 列表中的每一项是一种编码的名称。 当我们打开文件的时候, VIM 按顺序使用 fileencodings 中的编码进行尝试解码, 如果成功的话, 就使用该编码方式进行解码, 并将 fileencoding 设置为这个值, 如果失败的话, 就继续试验下一个编码。
因此, 我们在设置 fileencodings 的时候, 一定要把要求严格的、 当文件不是这个编码的时候更容易出现解码失败的编码方式放在前面, 把宽松的编码方式放在后面。
例如, latin1 是一种非常宽松的编码方式, 任何一种编码方式得到的文本, 用 latin1 进行解码, 都不会发生解码失败 —— 当然, 解码得到的结果自然也就是理所当然的“乱码”。 因此, 如果你把 latin1 放到了 fileencodings 的第一位的话, 打开任何中文文件都是乱码也就是理所当然的了。
以下是滇狐推荐的一个 fileencodings 设置:
其中, ucs-bom 是一种非常严格的编码, 非该编码的文件几乎没有可能被误判为 ucs-bom, 因此放在第一位。
utf-8 也相当严格, 除了很短的文件外 (例如许多人津津乐道的 GBK 编码的“联通”被误判为 UTF-8 编码的经典错误), 现实生活中一般文件是几乎不可能被误判的, 因此放在第二位。
接下来是 cp936 和 gb18030, 这两种编码相对宽松, 如果放前面的话, 会出现大量误判, 所以就让它们靠后一些。 cp936 的编码空间比 gb18030 小, 所以把 cp936 放在 gb18030 前面。
至于 big5、euc-jp 和 euc-kr, 它们的严格程度和 cp936 差不多, 把它们放在后面, 在编辑这些编码的文件的时候必然出现大量误判, 但这是 Vim 内置编码探测机制没有办法解决的事。 由于中国用户很少有机会编辑这些编码的文件, 因此我们还是决定把 cp936 和 gb18030 前提以保证这些编码的识别。
最后就是 latin1 了。 它是一种极其宽松的编码, 以至于我们不得不把它放在最后一位。 不过可惜的是, 当你碰到一个真的 latin1 编码的文件时, 绝大部分情况下, 它没有机会 fall-back 到 latin1, 往往在前面的编码中就被误判了。 不过, 正如前面所说的, 中国用户没有太多机会接触这样的文件。
如果编码被误判了, 解码后的结果就无法被人类识别, 于是我们就说, 这个文件乱码了。 此时, 如果你知道这个文件的正确编码的话, 可以在打开文件的时候使用 ++enc=encoding 的方式来打开文件, 如:
:e ++enc=utf-8 myfile.txt
5 fencview
根据前面的介绍, 我们知道, 通过 Vim 内置的编码识别机制, 识别率是很低的, 尤其是对于简体中文 (GBK/GB18030)、 繁体中文 (Big5)、 日文 (euc-jp) 和韩文 (euc-kr) 之间的识别。 而对于普通用户而言, 肉眼看出一个文件的编码方式也是很不现实的事情。 因此, 滇狐强烈推荐水木社区的 mbbill 开发的 fencview 插件。 该插件使用词频统计的方式识别编码, 正确率非常高。 点击 这里 下载。
The correct way to add IP aliases (multiple IPs) on one device in SuSe Linux 10 (and below)
I've always been used to the Redhat/Fedora way of setting up aliases in /etc/sysconfig/network-scripts-ifcfg-: but SuSe/Novell Linux is slightly different. I see a lot of hackish examples/bad advice on the internet on how to set it up on SuSe/Novell Linux so I figured I'd make a note of it here.
To do it "right" under SuSe, you modify the main configuration file for your ethernet adapter.
For example, 'ifcfg-eth-id-de:ad:co:ed:ba:be' where the de:ad:co:ed:ba:be is replaced by a real MAC address.
As an example, you want the main IP of the system to be statically set to 192.168.100.1, and the other IPs to be 192.168.100.100, 192.168.100.101, and 192.168.100.102
Your original configuration will probably look something like this:
BOOTPROTO='static'
BROADCAST='192.168.100.255'
IPADDR='192.168.100.1'
MTU=1500
NAME='My awesome no-name PCI \"bus-mastering\" NE-2000 clone $1.99 CPU-cycle-hogging wondercard'
NETMASK='255.255.255.0'
NETWORK='192.168.100.0'
REMOTE_IPADDR=''
STARTMODE='onboot'
USERCONTROL='no'
_nm_name='bus-pci-0000:01:04.0'
You will want to change it to look like this:
BOOTPROTO='static'
BROADCAST='192.168.100.255'
IPADDR='192.168.100.1'
MTU=1500
NAME='My awesome no-name PCI \"bus-mastering\" NE-2000 clone $1.99 wondercard'
NETMASK='255.255.252.0'
NETWORK='192.168.100.0'
REMOTE_IPADDR=''
STARTMODE='onboot'
USERCONTROL='no'
_nm_name='bus-pci-0000:01:04.0'
IPADDR1='192.168.100.100'
NETMASK1='255.255.255.0'
LABEL1='0'
IPADDR2='192.168.100.101'
NETMASK2='255.255.255.0'
LABEL2='1'
IPADDR3='192.168.100.102'
NETMASK3='255.255.255.0'
LABEL3='2'
Save that and restart networking with '/etc/init.d/network restart' and you are good to go with:
eth0 set to 192.168.100.1
eth0:0 set to 192.168.100.100
eth0:1 set to 192.168.100.101
eth0:2 set to 192.168.100.102
The 'LABELx' settings are just setting the alias label you see after the 'eth0:'
You can get away with leaving out the 'LABELx' statements. One side effect is that you won't see the aliases under 'ifconfig'.
I know you can do this with 'yast' and other utilities but this seems more direct and easier to do on a high latency SSH console.
鱼漂(www.eit.name)提醒:
1. 只需在原来的ifcfg-ethx配置文件中,增加类似配置:
IPADDR_N=
NETMASK_N=
LABEL_N=
2. "man ifcfg"的Multiple addresses章节有详细的解释。
ie8 ewebeditor编辑器不管用的解决办法
昨天ie8正式发布了,偶也去下载了一个,感觉很爽, 还在美的时候,突然发现很多网页都出问题,更可气的是自己的网站编辑器也不管用了,所有的按钮都不管用,即使是别的浏览器也都不管用,我这叫一个汗。于是就去360论坛找人问,因为我用的360浏览器以为是360浏览器的问题。问了半天也没有人回答我于是我就去网上找,由于今天才出的ie8所以网上根本也没有这方面的解决办法,于是到了第二天,网上终于有了这样的贴子,所以我也找了找解决办法。
在一个哥们的贴子中是这样写的,打开include下面的editor.js文件,有这样的段代码:
if (element.YUSERONCLICK) eval(element.YUSERONCLICK + "anonymous()");
说是因为ie8屏蔽了anonymous方法 所以要改成click方法,于是就改成这样,但是我又有了一个惊人的发现,那就是改成这样之后在ie7下编辑器就不管用了,痛苦之余我又去网上查,网上果然高水如云,果然在csdn上找到了答案:
if(navigator.appVersion.match(/8./i)=='8.')
{
if (element.YUSERONCLICK) eval(element.YUSERONCLICK + "onclick(event)");
}
else
{
if (element.YUSERONCLICK) eval(element.YUSERONCLICK + "anonymous()");
}
把代码改成这样之后在ie7和ie8下就都管用了。
以下为鱼漂优化后的代码:
上面的代码,在类似遨游之类的IE内核浏览器就无法工作了,因为遨游修改了UserAgent。为简化浏览器的判断,鱼漂优化了一下这个代码,分别执行这两条语句,不过用try catch捕获一下异常,以免中断执行:
try {
if (element.YUSERONCLICK) eval(element.YUSERONCLICK + "onclick(event)");
} catch (err) {
}
try {
if (element.YUSERONCLICK) eval(element.YUSERONCLICK + "anonymous()");
} catch (err) {
}
除此之外,ewebeditor的浏览器兼容性做得比较差,个人强烈推荐FCKEditor。
JSON官方的java扩展未对汉字未进行ascii转义的问题
近日某同事在用java发布一个API时,使用Json进行数据编码,发现JSON官方的java扩展在处理汉字时未进行ascii转义,如下所示:
Java源码:
AppObject ao=new AppObject(20,"鱼漂");
try {
JSONObject jo=new JSONObject(ao);
System.out.println(jo.toString());
} catch (Exception e) {
System.out.println("json encode error.");
}
输出为:
{"name":"鱼漂","age":10}
其中的name仍然显示中文,未进行ascii转义,通常情况下是可以正常使用的,但在不同的系统互相调用时,可能需要编码转换,比如从GBK转到UTF8。
通过查看源代码发现,在JSONObject.java的1138-1139行,只将\u0080-\u00a0, \u2000-\u2100的字符转成\u的格式。汉字的unicode编码不在这两个范围之内,所以不会转换。
找到原因后,修改就比较简单,我们只需简单的将\u0080以上的所有字符,转成\u格式即可,将1138-1139行改为:
if (c < ' ' || c >= '\u0080') {
再次测试,输出结果为:
{"name":"\u9c7c\u6f02","age":10}
注:
1. JSON官方的java扩展可以在 http://www.json.org/java/ 找到
2. PHP自带的json扩展没此问题
3. 本文原载于 http://www.eit.name/
4. 关于汉字在unicode的位段,可参考前一篇转载的博文.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
完整的CJK Unicode范围(5.0版)
因为FontRouter新版本开发的需要,在网上搜索了一下汉字的Unicode范围,普遍给出了“U+4E00..U+9FA5”。但事实上这个范围是不完整的,甚至连基本的全角(中文)标点也未包含在内。为此,我特地查询了Unicode官方的Code Charts数据库,并根据最新的Unicode 5.0版整理如下:
注:在绝大多数应用场合中,我们可以仅用(1)、(2)、(3)、(4)、(5)的集合作为CJK判断的依据。
1)标准CJK文字
http://www.unicode.org/Public/UNIDATA/Unihan.html
Code point range | Block name | Release |
---|---|---|
U+3400..U+4DB5 | CJK Unified Ideographs Extension A | 3.0 |
U+4E00..U+9FA5 | CJK Unified Ideographs | 1.1 |
U+9FA6..U+9FBB | CJK Unified Ideographs | 4.1 |
U+F900..U+FA2D | CJK Compatibility Ideographs | 1.1 |
U+FA30..U+FA6A | CJK Compatibility Ideographs | 3.2 |
U+FA70..U+FAD9 | CJK Compatibility Ideographs | 4.1 |
U+20000..U+2A6D6 | CJK Unified Ideographs Extension B | 3.1 |
U+2F800..U+2FA1D | CJK Compatibility Supplement | 3.1 |
2)全角ASCII、全角中英文标点、半宽片假名、半宽平假名、半宽韩文字母:FF00-FFEF
http://www.unicode.org/charts/PDF/UFF00.pdf
3)CJK部首补充:2E80-2EFF
http://www.unicode.org/charts/PDF/U2E80.pdf
4)CJK标点符号:3000-303F
http://www.unicode.org/charts/PDF/U3000.pdf
5)CJK笔划:31C0-31EF
http://www.unicode.org/charts/PDF/U31C0.pdf
6)康熙部首:2F00-2FDF
http://www.unicode.org/charts/PDF/U2F00.pdf
7)汉字结构描述字符:2FF0-2FFF
http://www.unicode.org/charts/PDF/U2FF0.pdf
8)注音符号:3100-312F
http://www.unicode.org/charts/PDF/U3100.pdf
9)注音符号(闽南语、客家语扩展):31A0-31BF
http://www.unicode.org/charts/PDF/U31A0.pdf
10)日文平假名:3040-309F
http://www.unicode.org/charts/PDF/U3040.pdf
11)日文片假名:30A0-30FF
http://www.unicode.org/charts/PDF/U30A0.pdf
12)日文片假名拼音扩展:31F0-31FF
http://www.unicode.org/charts/PDF/U31F0.pdf
13)韩文拼音:AC00-D7AF
http://www.unicode.org/charts/PDF/UAC00.pdf
14)韩文字母:1100-11FF
http://www.unicode.org/charts/PDF/U1100.pdf
15)韩文兼容字母:3130-318F
http://www.unicode.org/charts/PDF/U3130.pdf
16)太玄经符号:1D300-1D35F
http://www.unicode.org/charts/PDF/U1D300.pdf
17)易经六十四卦象:4DC0-4DFF
http://www.unicode.org/charts/PDF/U4DC0.pdf
18)彝文音节:A000-A48F
http://www.unicode.org/charts/PDF/UA000.pdf
19)彝文部首:A490-A4CF
http://www.unicode.org/charts/PDF/UA490.pdf
20)盲文符号:2800-28FF
http://www.unicode.org/charts/PDF/U2800.pdf
21)CJK字母及月份:3200-32FF
http://www.unicode.org/charts/PDF/U3200.pdf
22)CJK特殊符号(日期合并):3300-33FF
http://www.unicode.org/charts/PDF/U3300.pdf
23)装饰符号(非CJK专用):2700-27BF
http://www.unicode.org/charts/PDF/U2700.pdf
24)杂项符号(非CJK专用):2600-26FF
http://www.unicode.org/charts/PDF/U2600.pdf
25)中文竖排标点:FE10-FE1F
http://www.unicode.org/charts/PDF/UFE10.pdf
26)CJK兼容符号(竖排变体、下划线、顿号):FE30-FE4F
http://www.unicode.org/charts/PDF/UFE30.pdf
以上翻译自Unicode官方网站,部分译法可能不够准确,还望大家予以指正!如有疏漏、错误之处也请一并指出,多谢!