2012年1月31日星期二

Perl System and `` and exec

system LIST
system PROGRAM LIST
Does exactly the same thing as exec LIST , except that a fork is done first and the parent process waits for the child process to exit.
-If there is more than one argument in LIST, or if LIST is an array with more than one value, starts the program given by the first element of the list with arguments given by the rest of the list.
-If there is only one scalar argument, the argument is checked for shell metacharacters, and if there are any, the entire argument is passed to the system's command shell for parsing (this is /bin/sh -c on Unix platforms, but varies on other platforms). If there are no shell metacharacters in the argument, it is split into words and passed directly to execvp , which is more efficient.

 `` use shell always

Process

http://www.win.tue.nl/~aeb/linux/lk/lk-10.html 

10.1 Processes

Creation

A new process is traditionally started using the fork() system call:
pid_t p;

p = fork();
if (p == (pid_t) -1)
        /* ERROR */
else if (p == 0)
        /* CHILD */
else
        /* PARENT */
This creates a child as a duplicate of its parent. Parent and child are identical in almost all respects. In the code they are distinguished by the fact that the parent learns the process ID of its child, while fork() returns 0 in the child. (It can find the process ID of its parent using the getppid() system call.)

Termination

Normal termination is when the process does
exit(n);
or
return n;
from its main() procedure. It returns the single byte n to its parent. Abnormal termination is usually caused by a signal.

Collecting the exit code of child process

The parent does
pid_t p;
int status;

p = wait(&status);
The parent will suspend till one of its child process terminates, and collects two bytes:

A process that has terminated but has not yet been waited for is a zombie. It need only store these two bytes: exit code and reason for termination.

On the other hand, if the parent dies first, init (process 1) inherits the child and becomes its parent.

Signals

Stopping

Some signals cause a process to stop: SIGSTOP (stop!), SIGTSTP (stop from tty: probably ^Z was typed), SIGTTIN (tty input asked by background process), SIGTTOU (tty output sent by background process, and this was disallowed by stty tostop).
Apart from ^Z there also is ^Y. The former stops the process when it is typed, the latter stops it when it is read.
Signals generated by typing the corresponding character on some tty are sent to all processes that are in the foreground process group of the session that has that tty as controlling tty. (Details below.)
If a process is being traced, every signal will stop it.

Continuing

SIGCONT: continue a stopped process.

Terminating

SIGKILL (die! now!), SIGTERM (please, go away), SIGHUP (modem hangup), SIGINT (^C), SIGQUIT (^\), etc. Many signals have as default action to kill the target. (Sometimes with an additional core dump, when such is allowed by rlimit.) The signals SIGCHLD and SIGWINCH are ignored by default. All except SIGKILL and SIGSTOP can be caught or ignored or blocked. For details, see signal(7).

10.2 Process groups

Every process is member of a unique process group, identified by its process group ID. (When the process is created, it becomes a member of the process group of its parent.) By convention, the process group ID of a process group equals the process ID of the first member of the process group, called the process group leader. A process finds the ID of its process group using the system call getpgrp(), or, equivalently, getpgid(0). One finds the process group ID of process p using getpgid(p).
One may use the command ps j to see PPID (parent process ID), PID (process ID), PGID (process group ID) and SID (session ID) of processes. With a shell that does not know about job control, like ash, each of its children will be in the same session and have the same process group as the shell. With a shell that knows about job control, like bash, the processes of one pipeline. like
% cat paper | ideal | pic | tbl | eqn | ditroff > out
form a single process group.

Creation

A process pid is put into the process group pgid by
setpgid(pid, pgid);
If pgid == pid or pgid == 0 then this creates a new process group with process group leader pid. Otherwise, this puts pid into the already existing process group pgid. A zero pid refers to the current process. The call setpgrp() is equivalent to setpgid(0,0).

Restrictions on setpgid()

The calling process must be pid itself, or its parent, and the parent can only do this before pid has done exec(), and only when both belong to the same session. It is an error if process pid is a session leader (and this call would change its pgid).

Typical sequence


p = fork();
if (p == (pid_t) -1) {
        /* ERROR */
} else if (p == 0) {    /* CHILD */
        setpgid(0, pgid);
        ...
} else {                /* PARENT */
        setpgid(p, pgid);
        ...
}
This ensures that regardless of whether parent or child is scheduled first, the process group setting is as expected by both.

Signalling and waiting

One can signal all members of a process group:
killpg(pgrp, sig);
One can wait for children in ones own process group:
waitpid(0, &status, ...);
or in a specified process group:
waitpid(-pgrp, &status, ...);

Foreground process group

Among the process groups in a session at most one can be the foreground process group of that session. The tty input and tty signals (signals generated by ^C, ^Z, etc.) go to processes in this foreground process group.
A process can determine the foreground process group in its session using tcgetpgrp(fd), where fd refers to its controlling tty. If there is none, this returns a random value larger than 1 that is not a process group ID.
A process can set the foreground process group in its session using tcsetpgrp(fd,pgrp), where fd refers to its controlling tty, and pgrp is a process group in the its session, and this session still is associated to the controlling tty of the calling process.
How does one get fd? By definition, /dev/tty refers to the controlling tty, entirely independent of redirects of standard input and output. (There is also the function ctermid() to get the name of the controlling terminal. On a POSIX standard system it will return /dev/tty.) Opening the name of the controlling tty gives a file descriptor fd.

Background process groups

All process groups in a session that are not foreground process group are background process groups. Since the user at the keyboard is interacting with foreground processes, background processes should stay away from it. When a background process reads from the terminal it gets a SIGTTIN signal. Normally, that will stop it, the job control shell notices and tells the user, who can say fg to continue this background process as a foreground process, and then this process can read from the terminal. But if the background process ignores or blocks the SIGTTIN signal, or if its process group is orphaned (see below), then the read() returns an EIO error, and no signal is sent. (Indeed, the idea is to tell the process that reading from the terminal is not allowed right now. If it wouldn't see the signal, then it will see the error return.)
When a background process writes to the terminal, it may get a SIGTTOU signal. May: namely, when the flag that this must happen is set (it is off by default). One can set the flag by
% stty tostop
and clear it again by
% stty -tostop
and inspect it by
% stty -a
Again, if TOSTOP is set but the background process ignores or blocks the SIGTTOU signal, or if its process group is orphaned (see below), then the write() returns an EIO error, and no signal is sent.

Orphaned process groups

The process group leader is the first member of the process group. It may terminate before the others, and then the process group is without leader.
A process group is called orphaned when the parent of every member is either in the process group or outside the session. In particular, the process group of the session leader is always orphaned.
If termination of a process causes a process group to become orphaned, and some member is stopped, then all are sent first SIGHUP and then SIGCONT.
The idea is that perhaps the parent of the process group leader is a job control shell. (In the same session but a different process group.) As long as this parent is alive, it can handle the stopping and starting of members in the process group. When it dies, there may be nobody to continue stopped processes. Therefore, these stopped processes are sent SIGHUP, so that they die unless they catch or ignore it, and then SIGCONT to continue them.
Note that the process group of the session leader is already orphaned, so no signals are sent when the session leader dies.
Note also that a process group can become orphaned in two ways by termination of a process: either it was a parent and not itself in the process group, or it was the last element of the process group with a parent outside but in the same session. Furthermore, that a process group can become orphaned other than by termination of a process, namely when some member is moved to a different process group.

10.3 Sessions

Every process group is in a unique session. (When the process is created, it becomes a member of the session of its parent.) By convention, the session ID of a session equals the process ID of the first member of the session, called the session leader. A process finds the ID of its session using the system call getsid().
Every session may have a controlling tty, that then also is called the controlling tty of each of its member processes. A file descriptor for the controlling tty is obtained by opening /dev/tty. (And when that fails, there was no controlling tty.) Given a file descriptor for the controlling tty, one may obtain the SID using tcgetsid(fd).
A session is often set up by a login process. The terminal on which one is logged in then becomes the controlling tty of the session. All processes that are descendants of the login process will in general be members of the session.

Creation

A new session is created by
pid = setsid();
This is allowed only when the current process is not a process group leader. In order to be sure of that we fork first:
p = fork();
if (p) exit(0);
pid = setsid();
The result is that the current process (with process ID pid) becomes session leader of a new session with session ID pid. Moreover, it becomes process group leader of a new process group. Both session and process group contain only the single process pid. Furthermore, this process has no controlling tty. The restriction that the current process must not be a process group leader is needed: otherwise its PID serves as PGID of some existing process group and cannot be used as the PGID of a new process group.

Getting a controlling tty

How does one get a controlling terminal? Nobody knows, this is a great mystery.
The System V approach is that the first tty opened by the process becomes its controlling tty.
The BSD approach is that one has to explicitly call
ioctl(fd, TIOCSCTTY, ...);
to get a controlling tty. Linux tries to be compatible with both, as always, and this results in a very obscure complex of conditions. Roughly:
The TIOCSCTTY ioctl will give us a controlling tty, provided that (i) the current process is a session leader, and (ii) it does not yet have a controlling tty, and (iii) maybe the tty should not already control some other session; if it does it is an error if we aren't root, or we steal the tty if we are all-powerful.
Opening some terminal will give us a controlling tty, provided that (i) the current process is a session leader, and (ii) it does not yet have a controlling tty, and (iii) the tty does not already control some other session, and (iv) the open did not have the O_NOCTTY flag, and (v) the tty is not the foreground VT, and (vi) the tty is not the console, and (vii) maybe the tty should not be master or slave pty.

Getting rid of a controlling tty

If a process wants to continue as a daemon, it must detach itself from its controlling tty. Above we saw that setsid() will remove the controlling tty. Also the ioctl TIOCNOTTY does this. Moreover, in order not to get a controlling tty again as soon as it opens a tty, the process has to fork once more, to assure that it is not a session leader. Typical code fragment:

        if ((fork()) != 0)
                exit(0);
        setsid();
        if ((fork()) != 0)
                exit(0);
See also daemon(3).

Disconnect

If the terminal goes away by modem hangup, and the line was not local, then a SIGHUP is sent to the session leader. Any further reads from the gone terminal return EOF. (Or possibly -1 with errno set to EIO.)
If the terminal is the slave side of a pseudotty, and the master side is closed (for the last time), then a SIGHUP is sent to the foreground process group of the slave side.
When the session leader dies, a SIGHUP is sent to all processes in the foreground process group. Moreover, the terminal stops being the controlling terminal of this session (so that it can become the controlling terminal of another session).
Thus, if the terminal goes away and the session leader is a job control shell, then it can handle things for its descendants, e.g. by sending them again a SIGHUP. If on the other hand the session leader is an innocent process that does not catch SIGHUP, it will die, and all foreground processes get a SIGHUP.

10.4 Threads

A process can have several threads. New threads (with the same PID as the parent thread) are started using the clone system call using the CLONE_THREAD flag. Threads are distinguished by a thread ID (TID). An ordinary process has a single thread with TID equal to PID. The system call gettid() returns the TID. The system call tkill() sends a signal to a single thread.
Example: a process with two threads. Both only print PID and TID and exit. (Linux 2.4.19 or later.)
% cat << EOF > gettid-demo.c
#include <unistd.h>
#include <sys/types.h>
#define CLONE_SIGHAND   0x00000800
#define CLONE_THREAD    0x00010000
#include <linux/unistd.h>
#include <errno.h>
_syscall0(pid_t,gettid)

int thread(void *p) {
        printf("thread: %d %d\n", gettid(), getpid());
}

main() {
        unsigned char stack[4096];
        int i;

        i = clone(thread, stack+2048, CLONE_THREAD | CLONE_SIGHAND, NULL);
        if (i == -1)
                perror("clone");
        else
                printf("clone returns %d\n", i);
        printf("parent: %d %d\n", gettid(), getpid());
}
EOF
% cc -o gettid-demo gettid-demo.c
% ./gettid-demo
clone returns 21826
parent: 21825 21825
thread: 21826 21825
%

Kill parent and child process

No, child processes are not necessarily killed when the parent is killed. On UNIX, there is no enforced relation between parent and child process's lifetimes. Process will only terminate when it calls exit() or receives unhandled signal for which default action is to terminate.

Exceptions:
1. If the child has a pipe open which it is writing to and the parent is reading from, it will get a SIGPIPE when it next tries to write to the pipe, for which the default action is to kill it. That is often what happens in practice.

2. Entire "foreground process group" in a "controlling terminal" can receive SIGINT, SIGQUIT, etc. signals when user hits ctrl-C, ctrl-\, etc. on that terminal. Specific behaviour is partly implemented by login shell (with help from tty driver), details may be quite complicated: look here and here





You don't say if the tree you want to kill is a single process group. (This is often the case if the tree is the result of forking from a server start or a shell command line.) You can discover process groups using GNU ps as follows:
 ps x -o  "%p %r %y %x %c "
If it is a process group you want to kill, just use the kill(1) command but instead of giving it a process number, give it the negation of the group number. For example to kill every process in group 5112, use kill -TERM -5112.
 To kill a process tree recursively, use killtree.sh:
#!/bin/bash

killtree() {
    local _pid=$1
    local _sig=${2-TERM}
    for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
        killtree ${_child} ${_sig}
    done
    kill -${_sig} ${_pid}
}

if [ $# -eq 0 -o $# -gt 2 ]; then
    echo "Usage: $(basename $0) <pid> [signal]"
    exit 1
fi

killtree $@

 

Perl Open File and Pipe

open(FH, "<", "input.txt")
1.FH="input.txt"
2.FH is <, which means FH is (file) source whose content provides input(similar as stdin) to current program
3.if >, which means FH is a file accepting the output of current program

open(FH, "-|", "input.sh") 
1.FH="input.txt"
2.FH is <, which means is a (command) source whose output provides input(similar to stdin) to current program
3.if |-, >, which means FH is a command whose stdin accepts the output of current program


 

Agile Testing: Running buildbot on various platforms

Agile Testing: Running buildbot on various platforms


Wednesday, March 08, 2006

Running buildbot on various platforms

I started to run a bunch of buildbot slaves at work, on various platforms. I encountered some issues that I want to document here for future reference. The version of buildbot I used in all scenarios was 0.7.2. The buildbot master is running on a RHEL3 server. Note that I'm not going to talk about the general buildbot setup -- if you need guidance in configuring buildbot, read this post of mine.

Before I discuss platform-specific issues, I want to mention the issue of timeouts. If you want to run a command that takes a long time on the buildbot slave, you need to increase the default timeout (which is 1200 sec. = 20 min.) for the ShellCommand definitions in the buildmaster's master.cfg file -- otherwise, the master will mark that command as failed after the timeout expires. To modify the default timeout, simply add a keyword argument such as timeout=3600 to the ShellCommand (or derived class) instantiation in master.cfg. I have for example this line in the builders section of my master.cfg file:

client_smoke_tests = s(ClientSmokeTests, command="%s/buildbot/run_smoke_tests.py" % BUILDBOT_PATH, timeout=3600)

where ClientSmokeTests is a class I derived from ShellCommand (if you need details on this, see again my previous post on buildbot.)


Buildbot on Windows

My setup: Windows 2003 server, Active Python 2.4.2

Issue with subprocess module: I couldn't use the subprocess module to run commands on the slave. I got errors such as these:

    p = Popen(arglist, stdout=PIPE, stderr=STDOUT)
File "C:\Python24\lib\subprocess.py", line 533, in __init__
(p2cread, p2cwrite,
File "C:\Python24\lib\subprocess.py", line 593, in _get_handles
p2cread = self._make_inheritable(p2cread)
File "C:\Python24\lib\subprocess.py", line 634, in _make_inheritable
DUPLICATE_SAME_ACCESS)
TypeError: an integer is required

I didn't have too much time to spend troubleshooting this, so I ended up replacing calls to subprocess to calls to popen2.popen3(). This solved the problem.

Also, I'm not currently running the buildbot process as a Windows service, although it's on my TODO list. I wrote a simple .bat file which I called startbot.bat:

buildbot start C:\qa\pylts\buildbot\QA

To start buildbot, I launched startbot.bat from the command prompt and I left it running.

Note that on Windows, the buildbot script gets installed in C:\Python24\scripts, and there is also a buildbot.bat batch file in the same scripts directory, which calls the buildbot script.

Issue with buildbot.bat: it contains a hardcoded path to Python23. I had to change that to Python24 so that it correctly finds the buildbot script in C:\Python24\scripts.

Buildbot on Solaris

My setup: one Solaris 9 SPARC server, one Solaris 10 SPARC server, both running Python 2.3.3

Issue with ZopeInterface on Solaris 10: when I tried to install ZopeInterface via 'easy_install http://www.zope.org/Products/ZopeInterface/3.1.0c1/ZopeInterface-3.1.0c1.tgz', a compilation step failed with:

/usr/include/sys/wait.h:86: error: parse error before "siginfo_t"

A google search revealed that this was a gcc-related issue specific to Solaris 10. Based on this post, I ran:

# cd /usr/local/lib/gcc-lib/sparc-sun-solaris2.10/3.3.2/install-tools
# ./mkheaders


After these steps, I was able to install ZopeInterface and the rest of the packages required by buildbot.

For reference, here is what I have on the Solaris 10 box in terms of gcc packages:

# pkginfo | grep -i gcc

system SFWgcc2 gcc-2 - GNU Compiler Collection
system SFWgcc2l gcc-2 - GNU Compiler Collection Runtime Libraries
system SFWgcc34 gcc-3.4.2 - GNU Compiler Collection
system SFWgcc34l gcc-3.4.2 - GNU Compiler Collection Runtime Libraries
application SMCgcc gcc
system SUNWgcc gcc - The GNU C compiler
system SUNWgccruntime GCC Runtime libraries

Here is what uname -a returns:

# uname -a
SunOS sunv2403 5.10 Generic sun4u sparc SUNW,Sun-Fire-V240

Issue with exit codes from child processes not intercepted correctly: on both Solaris 9 and Solaris 10, buildbot didn't seem to intercept correctly the exit code from the scripts which were running on the build slaves. I was able to check that I had the correct exit codes by running the scripts at the command line, but within buildbot the scripts just hung as if they hadn't finish.

Some searches on the buildbot-devel mailing list later, I found the solution via this post: I replaced usepty = 1 with usepty = 0 in buildbot.tac on the Solaris slaves, then I restarted the buildbot process on the slaves, and everything was fine.

Buildbot on AIX

My setup: AIX 5.2 on an IBM P510 server, Python 2.4.1

No problems here. Everything went smoothly.

Fedora 15 no scroll bar in user login screen

Use KDE's kdm as login greeter

To put kdm into effect, I put create and DISPLAYMANAGER into file /etc/sysconfig/desktop, and also set the default session manager by setting DESKTOP, as follows:

DISPLAYMANAGER="KDE"
DESKTOP="KDE"

2012年1月30日星期一

贝塔中的DBA » 编写健壮的Bash shell脚本

贝塔中的DBA » 编写健壮的Bash shell脚本

编写健壮的Bash shell脚本



许多人都能很快的码出一些shell代码来完成简单的任务,而且这种写法将会一直持续下去。问题是编写的shell脚本经常会包含着许多足以导致脚 本运行失败的细小的缺陷(subtle effects)。本文中我就将解释编写一个健壮的Bash脚本所需要的一些技术,告诉你是能做到将这些问题减少到最小的。


使用set -u

你是否会经常遇到因为变量没有赋值而导致脚本无法成功运行的情况呢?反正我是经常的遇到。

chroot=$1 ... rm -rf $chroot/usr/share/doc 

如果你在运行上述脚本的时候忘记了提供一个参数的话,最后的结果是你会把所有系统文档都删掉,而不是仅仅删除$chroot下指定的文档。那你该怎 么办呢?还好,Bash提供了一个选项set -u,使用这个选项可以使脚本在使用未初始化的变量时直接退出。这个选项的另一个可读性更强点的写法是set -o nounset。

david% bash /tmp/shrink-chroot.sh $chroot= david% bash -u /tmp/shrink-chroot.sh /tmp/shrink-chroot.sh: line 3: $1: unbound variable david% 


使用set -e

你应该在你编写的每个脚本上方都加上set -e选项,打开这个选项之后,脚本在运行时碰到返回值不为0的语句之后会直接退出。使用-e选项的好处是使你能及早的发现问题,而不是让错误越滚越大。同样这个选项也有另外一种可读性更强点的写法set -o errexit。

-e选项可以为你做免费的错误检查,如果你忘了检查的话,它会替你完成。不过不好的是你无法在使用$?来进行检查了,因为如果返回值不为0的话语句根本就执行不到检查$?的那一步的。解决方法是重写下代码:

command || { echo "command failed"; exit 1; } 

if ! command; then echo "command failed"; exit 1; fi 

替代

command if [ "$?"-ne 0]; then echo "command failed"; exit 1; fi 

如果你有一个命令它就是返回0或者是你根本就不关心返回值那怎么办呢?你可以使用command || true,或者假如你要对很长的一段代码都如此处理的话,你可以暂时关闭错误处理,不过我的建议是尽量少用。

set +e command1 command2 set -e 

另外有个和这个有点相关的说明:默认情况下Bash返回最后一个管道命令的执行结果,你可能不希望是这样,例如false | true执行返回值为是0,成功;如果你想它是失败的话,执行set -o pipefail就可以了。


防御型编程 – 未雨绸缪

你的脚本应该要考虑到应对一些比如文件不存在或者无法创建目录之类的异常情况,采取一些措施避免在碰到这些情况时发生错误。比如说如果在创建一个目 录时,如果上级目录不存在,mkdir就会返回错误,如果你在使用mkdir是加上-p选项的话就能一并创建所有不存在的上级目录了。另一个例子就是 rm,如果你要rm一个不存在的文件,那rm就会报错,脚本也会退出(应该在用-e选项了吧,对不?)你可以通过加上-f参数来解决这个,这个参数会保证 在文件不存在时安静的执行下一步。


注意文件名中的空格

有人总喜欢在文件名或者是命令行参数中使用空格,所以你在写脚本时一定要注意这点,特别注意要在变量上加上引号。

if [ $filename = "foo" ]; 

上述代码在$filename中包含空格时会失败,可以这样修正:

if [ "$filename" = "foo" ]; 

在使用$@变量时,总要记得使用双引号,这样才能保证当参数值中存在空格时不会被解析成单独的词。

david% foo() { for i in $@; do echo $i; done }; foo bar "baz quux" bar baz quux david% foo() { for i in "$@"; do echo $i; done }; foo bar "baz quux" bar baz quux 

我想不出有什么时候是需要你用$@代替”$@”的,所以当你存疑时,就加上双引号吧。


设置trap(Setting traps)

脚本在运行之中意外的退出经常会让文件系统处于一种不一致的状态,就像锁文件(lock file)、临时文件或者是你更新了一个文件却在更新另外一个文件的时候发生了错误,如果我们能有什么方法能解决这个问题好了,使得我们的程序运行出现问 题的时候能够删除掉锁文件或是回滚到一个已知的正常的状态。好在Bash提供了trap命令,这个命令能让运行中的脚本接受到unix信号的时候执行指定 的命令或是函数。

trap command signal [signal ...] 

trap命令能捕获很多的unix信号(可以通过kill -l来得到unix信号的清单),不过只为了清理的话只需要关注3个信号量就行了,这3个就是:INT, TERM和EXIT。你也可以使用-作为trap命令中的command参数重置trap的状态。

信号量 描述
INT 中断 – 使用CTRL+C组合键时会向脚本发送这个信号
TERM 终止 – kill命令使用这个信号来终止进程
EXIT 退出 – 这是个伪信号量(pseudo-signal),在脚本运行结束或者是使用exit命令退出脚本时都会被触发。

通常你可能会写出如下使用锁文件的脚本:

if [ ! -e $lockfile ]; then    touch $lockfile    critical-section    rm $lockfile else    echo "critical-section is already running" fi 

当脚本还在critical-section阶段运行时被别人杀掉会发生什么呢?锁文件会一直留在那里,而你的程序也无法再运行了,解决方法就是

if [ ! -e $lockfile ]; then    trap "rm -f $lockfile; exit" INT TERM EXIT    touch $lockfile    critical-section    rm $lockfile    trap - INT TERM EXIT else    echo "critical-section is already running" fi 

现在即使脚本运行中被杀掉,锁文件也能被正常的删除。注意我们要在trap命令执行之后使用exit退出,要不然程序将会从接收到信号量的那个地方恢复执行。

竞争条件
这里要指出来一下上面那个例子由于创建锁文件和检查锁文件的时间不一样会存在小的竞争条件的问题,一个可行的解决方案就是使用IO重定向加上使用Bash的noclobber模式,noclobber模式不允许重定向到一个存在的文件上,代码如下所示:
if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null; then    trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT     critical-section     rm -f "$lockfile"    trap - INT TERM EXIT else    echo "Failed to acquire lockfile: $lockfile."    echo "Held by $(cat $lockfile)" fi 

另一个更复杂点的问题就是当需要一次更新多个文件的时候,如果程序中途退出,你得让它退出的更加优雅些,就是要做到要改变的东西被正确的改变,或是做到像什么都没有发生一样。假设你用下面的脚本增加用户:

add_to_passwd $user cp -a /etc/skel /home/$user chown $user /home/$user -R 

当磁盘空间不足或是进程中途被杀的话就会有问题了,这种情况下你可能就希望这个用户以及相应的文件都清理掉:

rollback() {    del_from_passwd $user    if [ -e /home/$user ]; then       rm -rf /home/$user    fi    exit }  trap rollback INT TERM EXIT add_to_passwd $user cp -a /etc/skel /home/$user chown $user /home/$user -R trap - INT TERM EXIT 

这里必须在脚本的最后重置trap状态为默认值,要不然在脚本退出的时候rollback函数也会被执行的,然后所有的辛苦工作都白费了。


保持原子性(Be atomic)

有时你需要更新一个文件夹下面的多个文件,比如说你需要将网站的url从一个主机重写为另一个主机,你可能会写下如下脚本:

for file in $(find /var/www -type f -name "*.html"); do    perl -pi -e 's/www.example.net/www.example.com/' $file done 

现在如果脚本运行途中出现问题退出的话,很可能就会造成网站的一部分已经换成了www.example.com而另一部分还是指向www.example.net,你可以通过恢复备份或者是使用trap来修复这个问题,但是在替换的过程中还是会有不一致的问题的。

解决方法就是将整个更改当成一个(接近于)原子操作来执行。就是先将数据备份一份,然后在备份文件上面做变更,变更完以后接着将原文件移走,用变更 后的文件替换到原来位置上。在此过程中要确保新旧文件都存在于同一个分区上,这样就可以利用unix文件系统的快速移动文件夹的特性,因为这样需要更改的 只是目录的inode。

cp -a /var/www /var/www-tmp for file in $(find /var/www-tmp -type f -name "*.html"); do    perl -pi -e 's/www.example.net/www.example.com/' $file done mv /var/www /var/www-old mv /var/www-tmp /var/www 

这样做就可以保证一旦更改出现问题,当前运行的系统不会受到影响,同时受影响的时间也就是两个mv操作所花费的时间了,这个通常是非常快的,因为只需要更改目录的inode,不需要移动任何的文件。

这种做法的缺点一个是你需要两倍的磁盘空间,再一个是如果有的进程需要一直打开文件的话那么变换目录之后这些进程打开的还是旧文件,而非新文件,在 这种情况下你就需要重启这些进程了。如果使用apache的话这不会有问题,因为它在每次请求的时候都会重新打开文件,你也可以使用lsof命令来检查那 些文件正在被打开。好处就是你现在有一个变更前系统文件的备份了,这样一旦你后悔了还有回来的机会。


延伸阅读

Perl Timeout | 陈钢的博客

Timeout | 陈钢的博客

【 Perl 】给所有的shell程序全加上timeout参数

2011年3月15日
有些程序自带–timeout参数,比如rsync,这样很好。
但是有些程序就很不乖,比如ffmpeg,自己不会超时退出,这很不好。
让我们用Perl把所有的命令都加上超时退出的功能——
01#! /usr/bin/env perl
02use POSIX qw(strftime WNOHANG);
03
04#check input
05my $timeout = shift @ARGV;
06my ($secs) = $timeout =~ /--timeout=(\d+)$/;
07unless($secs)
08{
09    print "Usage: ./timeout --timeout=[SECONDS] [COMMAND] \n";
10    exit -1;
11}
12
13#fork and exec
14my $status = 0;
15$SIG{CHLD} = sub { while(waitpid(-1,WNOHANG)>0){ $status = -1 unless $? == 0; exit $status;} };
16$0 = 'timeout hacked ' . $ARGV[0];
17defined (my $child = fork);
18if($child == 0)
19{
20    my $cmd = join ' ', @ARGV;
21    exec($cmd);
22}
23$SIG{TERM} = sub { kill TERM => $child };
24$SIG{INT} = sub { kill INT => $child };
25
26#kill when timeout
27sleep $secs;
28$status = -1;
29kill TERM => $child;
30sleep 1 and kill INT => $child if kill 0 => $child;
31sleep 1 and kill KILL => $child if kill 0 => $child;
32exit $status;

然后如下就可以让任意的命令超时退出了(这里执行的命令是“sleep 500”)——
./timeout.pl --timeout=3 sleep 500 

【 Perl 】如何简单控制perl脚本超时

2009年11月3日
1eval
2{
3    local $SIG{ALRM} = sub { die "alarmn" };
4    alarm $config{'perl'}{'time_out'};
5    do $plugin;
6    alarm 0;
7};

Convert Java to EXE - Why, When, When Not and How

Convert Java to EXE - Why, When, When Not and How

浅析Java执行外部命令的几个要点(1)——简单的使用范例以及在Cygwin上的注意点 - loveapple——郝春利的个人博客 - 博客频道 - CSDN.NET

浅析Java执行外部命令的几个要点(1)——简单的使用范例以及在Cygwin上的注意点 - loveapple——郝春利的个人博客 - 博客频道 - CSDN.NET

转贴请注明出处:http://blog.csdn.net/froole
作者:郝春利

在Java语言中执行外部命令,到JDK1.4,一直都是使用java.lang.Runtime。从JDK1.5版本之后导入了java.lang.ProcessBuilder,并且是用起来同样非常方便。
java.lang.Runtime的例子在网上已经太多,这里不做重复,举一个java.lang.ProcessBuilder的范例,并简单说明几个要点。

1.Runtime VS ProcessBuilder
跟Runtime相比,ProcessBuilder有个特点,被执行的命令可以同ProcessBuilder一起被初始化。
例如事先定一一个启动文本编辑器的命令,可以如下:
new ProcessBuilder("notepad.exe", "test.txt");
Runtime是直接运行exeuct,所以,如果要需要执行的命令是不变的,使用ProcessBuilder对命令进行初始化,这要比使用Runtime更加方便,而且,从某种角度说,正确使用ProcessBuilder可以避免一些生成外部命令时容易发生的错误。
但是,需要注意的事,老版本和新版本的功能是没有变化的,具体用哪个,还需要具体情况具体分析。
以下是一个使用ProcessBuilder的例子:
  1. ProcessBuilder pb = new ProcessBuilder("notepad.exe", "test.txt");
  2. try {
  3. Process p = pb.start();
  4. int ret = p.waitFor();
  5. System.out.println("process exited with value : " + ret);
  6. } catch (IOException e) {
  7. // start()命令的执行处理失败
  8. e.printStackTrace();
  9. } catch (InterruptedException e) {
  10. // waitFor()处理失败
  11. e.printStackTrace();
  12. }



2.外部程序执行之后,提取执行结果
无论使用Runtime.exeuc还是ProcessBuilder.start执行外部命令,都会返回一个Process实现。
外部命令执行后,向OS输出的所有返回结果都可以通过Process来取得。其中,用来表示命令执行结束状态的出口值,可以通过调用exitValue()或者waitFor()方法来提取。
但是exitValue()跟waitFor()有一个本质区别,调用waitFor()之后,程序并不会马上返回出口状态,而是一直等到外部程序执行结束,调用exitValue(),如果子程序没有结束,就会抛出一个IllegalThreadStateException异常
所以,在大多数情况下,通过调用waitFor()来取得外部程序的执行结果更加安全。
waitFor()的确是好,但是有时候却事与愿违。
例如,外部程序的执行时间超出了预定的执行时间(Timeout);外部程序可以正常执行,但是,在Java执行就无缘无故停在半路不动了。
在实际的外部程序运行中将会出现很多状况,《浅析Java执行外部命令的几个要点》将在今后的部分中作具体的介绍。

3.在Cygwin上使用Sun JVM最需要注意的地方
Cygwin是一款跑在Windows上的Unix虚拟软件。
Cygwin上默认Unix格式的文件路径,例如:/usr/bin/find,当然,C:/WINDOWS这样的路径也可以使用。
虽然Cygwin是虚拟的Unix环境,但是,里面的命令归根结底还是Win32内核下的执行程序,所以Cygwin上可以执行Windows下的命令。
这就给在Cygwin下执行Java程序的混乱带来了隐患,因为,Cygwin下,默认Unix路径,而在Cygwin下执行的Java是在Windows上的JVM。
现象及解决方法:
  • 要 在Cygwin下,把/home/xx这个路径传给Java程序,Java得到这个路径之后,将会默认为[系统盘:/home/xx],可是,实际这个 [/home/xx]是在[$Cygwin/home/xx],这个时候,就出现了混乱,Java无法找到所指定的路径在什么地方。这种情况的解决方法, 就是把/home/xx这个路径,用Windows上的绝对路径替代,如:[C:/Cygwinpath/home/xx]
Java下无法认出Windows文件系统的路径分隔符。当然,这个问题在DOS下同样存在。
例 如在Cygwin下执行/usr/bin/find命令,并将输出结果通过xargs传给Java程序,这个时候Java程序会报错,找不到所指定的路 径。原因是被带入到Java中的路径格式如[C:/WINDOWS/xxx]中,“/”在Java中的字串变量中,是默认的escape字符,也就是说, 传给Java的是[C:/WINDOWS/xxx],到了Java里面[C:WINDOWSxxx]。
很多系统都是在Windows下开发,Unix下运用,特别是后台程序,如果遇到处理文件系统路径的问题,需特别注意这一点。如果程序最终在Unix下运行,测试的时候,不妨把路径中的“/”改成“/”,如果实在不行,就只能拿到Unix下去测试。


用Java执行外部命令非常简单,只要在带入参数的时候注意不要把参数弄错就可以了。
但是,在实际运用中,还有一个比较棘手的问题,就是外部命令执行的timeout。
特别是执行时间比较长的外部命令,如外部的后台处理程序。
当执行这些程序的时候不可能任由他们随便跑,大多数时候,都要事先设定一个外部命令的最大执行时限,也就是timeout,如果超过这个时间程序还没有执行完成,那么将强制杀死程序,并输出错误日志。
JDK的外部执行API没有提供设定timeout的接口,所以,实现此功能,只能自行解决。

在判断执行结果的时候,将会一直对Process的返回值进行判断,前面的部分已经介绍了Process的使用特点,这里将不再重复,只放出代码,如下:
  1. import java.io.File;
  2. import java.io.IOException;
  3. /**
  4. * 支持timeout的执行外部命令的类定义。
  5. *
  6. * @version 1.0
  7. * @author hao_shunri
  8. * @see http://blog.csdn.net/froole
  9. */
  10. public class CommandExec {
  11. /**
  12. * 用来执行外部命令的{@link Runtime}实现
  13. */
  14. private Runtime runtime;
  15. /**
  16. * 默认确认timeout的间隔时间,単位:毫秒
  17. */
  18. private static final int DEFAULT_TIMEOUT_INTERVAL = 500;
  19. /**
  20. * 默认timeout
  21. */
  22. private static final long DEFAULT_TIMEOUT = 60 * 1000;
  23. /**
  24. * Timout时间,単位:毫秒
  25. */
  26. private long timeout = -1;
  27. /**
  28. * 确认timeout的间隔时间,単位:毫秒
  29. */
  30. private long interval;
  31. /**
  32. *
  33. * @param runtime 用来执行外部命令的{@link Runtime}实现
  34. * @param timeout timeout时间
  35. * @param invterval 换算timeout的间隔时间
  36. */
  37. public CommandExec(Runtime runtime, long timeout, long invterval){
  38. this.timeout = timeout;
  39. this.interval = invterval;
  40. this.runtime = runtime;
  41. }
  42. /**
  43. *
  44. *
  45. * @param timeout timeout时间
  46. * @param invterval 换算timeout的间隔时间
  47. */
  48. public CommandExec(long timeout, long invterval){
  49. this(Runtime.getRuntime(), timeout, invterval);
  50. }
  51. /**
  52. *
  53. *
  54. * @param timeout timeout时间
  55. */
  56. public CommandExec(long timeout){
  57. this(timeout, DEFAULT_TIMEOUT_INTERVAL);
  58. }
  59. /**
  60. *
  61. *
  62. */
  63. public CommandExec(){
  64. this(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_INTERVAL);
  65. }
  66. /**
  67. *
  68. * 执行外部命令
  69. *
  70. * @param commands
  71. * 命令数组
  72. * @return
  73. * @throws IOException
  74. */
  75. public Process exec(String[] commands) throws IOException, InterruptedException {
  76. return exec(commands, null);
  77. }
  78. /**
  79. * 执行外部命令
  80. *
  81. * @param commands
  82. * 命令数组
  83. * @param dir
  84. * 临时目录
  85. * @return
  86. * @throws IOException
  87. */
  88. public Process exec(String[] commands, File dir) throws IOException, InterruptedException {
  89. return exec(commands, null, dir);
  90. }
  91. /**
  92. *
  93. * 执行外部命令
  94. *
  95. * @param commands
  96. * 命令数组
  97. * @param envp
  98. * 环境变量
  99. * @param dir
  100. * 临时目录
  101. * @return
  102. * @throws IOException
  103. * @throws IllegalThreadStateException
  104. */
  105. public Process exec(String[] commands, String[] envp, File dir) throws IOException, InterruptedException {
  106. if (commands == null) {
  107. throw new NullPointerException();
  108. }
  109. Process process = runtime.exec(commands, envp, dir);
  110. // 设定timeout
  111. long limitTime = timeout + System.currentTimeMillis();
  112. // 状态
  113. Integer status = null;
  114. do {
  115. try {
  116. status = process.exitValue();
  117. break;
  118. } catch (IllegalThreadStateException e) {
  119. try {
  120. Thread.sleep(getInterval());
  121. } catch (InterruptedException we) {
  122. return null;
  123. }
  124. }
  125. } while (System.currentTimeMillis() < limitTime);
  126. if (status == null) {
  127. process.destroy();
  128. try {
  129. status = process.waitFor();
  130. } catch (InterruptedException e) {
  131. throw e;
  132. }
  133. }
  134. return process;
  135. }
  136. /**
  137. * 设定Timout时间,単位:毫秒
  138. *
  139. * @param timeout
  140. * Timout时间,単位:毫秒
  141. */
  142. public void setTimeout(long timeout) {
  143. this.timeout = timeout;
  144. }
  145. /**
  146. * 提取Timout时间,単位:毫秒
  147. *
  148. * @return Timout时间,単位:毫秒
  149. */
  150. public long getTimeout() {
  151. return timeout;
  152. }
  153. /**
  154. * 提取确认timeout的间隔时间,単位:毫秒
  155. *
  156. * @return 确认timeout的间隔时间,単位:毫秒
  157. */
  158. public long getInterval() {
  159. return interval;
  160. }
  161. /**
  162. * 设定确认timeout的间隔时间,単位:毫秒
  163. *
  164. * @param interval
  165. * 确认timeout的间隔时间,単位:毫秒
  166. */
  167. public void setInterval(long interval) {
  168. this.interval = interval;
  169. }
  170. }


上一部分定义了一个支持超时控制功能的类——CommandExec
在这部分,将展示CommandExec的使用方法,并且向读者展示如何取得命令的执行结果并将他们打印出来。

环境描述:
假设执行环境为Windows,在C盘(系统盘)上有一个test.zip文件,里面压缩一个test.txt文本文件。
用系统自带的unzip -l命令可以显示出压缩文件的内容。
并且,系统还有sleep命令,用来暂停处理。

执行处理步骤:

  1. 生成一个新的CommandExec实现,timeout为6秒
  2. 执行系统命令[unzip -l C:/test.zip],确认其正常结束并打印执行结果;
  3. 执行[sleep 7],让处理暂停7秒,但是,到了6秒的时候程序会被强制终止,从出口值可以判断[sleep 7]的执行结果是失败的;
  4. 执行[unzip -l C:/test.zip],由于Java中将“C:/test.zip”认为“C:test.zip”,由于找不到文件执行将失败,并打印出错误信息。

以下是代码,有兴趣的读者可以在自己的电脑上测试一下。

  1. import java.io.IOException;
  2. import java.io.InputStream;

  3. public class CommandExecTest {

  4. /**
  5. *
  6. * 执行结果:
  7. *
  8. *
  9. * processSuccess Result:0
  10. * processTimeout Result:1
  11. * processFail Result:9
  12. * >>>>>>>Sucess process:
  13. * ==================
  14. * sucess std:
  15. * Archive: C:/test.zip
  16. * Length Date Time Name
  17. * -------- ---- ---- ----
  18. * 0 08/12/25 20:09 test/test.txt
  19. * -------- -------
  20. * 0 1 file
  21. * ==================
  22. * error std:
  23. * ==================
  24. * >>>>>>>Timeout process:
  25. * ==================
  26. * sucess std:
  27. * ==================
  28. * error std:
  29. * ==================
  30. * >>>>>>>Fail process:
  31. * ==================
  32. * sucess std:
  33. * ==================
  34. * error std:
  35. * unzip: cannot find either C: est.zip or C: est.zip.zip.
  36. * ==================
  37. *
  38. *
  39. * @param args
  40. * @throws Exception
  41. */
  42. public static void main(String[] args) throws Exception {
  43. Runtime runtime = Runtime.getRuntime();
  44. long timeout = 6 * 1000L;
  45. long invterval = 500L;
  46. CommandExec exec = new CommandExec(runtime, timeout, invterval);

  47. // 执行成功
  48. String[] command = new String[] { "unzip", "-l", "C:/test.zip" };
  49. Process processSuccess = exec.exec(command);
  50. System.out.println("processSuccess Result:" + processSuccess.exitValue());

  51. // 超时执行
  52. command = new String[] { "sleep", "7" };
  53. Process processTimeout = exec.exec(command);
  54. System.out.println("processTimeout Result:" + processTimeout.waitFor());

  55. // 执行失败
  56. command = new String[] { "unzip", "-l", "C:/test.zip" };
  57. Process processFail = exec.exec(command);
  58. System.out.println("processFail Result:" + processFail.waitFor());

  59. // 打印结果
  60. System.out.println(">>>>>>>Sucess process:");
  61. dispProcess(processSuccess);
  62. System.out.println(">>>>>>>Timeout process:");
  63. dispProcess(processTimeout);
  64. System.out.println(">>>>>>>Fail process:");
  65. dispProcess(processFail);
  66. }

  67. /**
  68. *
  69. * 打印输出结果
  70. *
  71. * @param target
  72. * @throws IOException
  73. */
  74. static void dispProcess(Process target) throws IOException {
  75. InputStream stdIn = target.getInputStream();
  76. InputStream stdErr = target.getErrorStream();
  77. //
  78. try {
  79. System.out.println("==================");
  80. System.out.println("sucess std:");
  81. int c1;
  82. while ((c1 = stdIn.read()) != -1) {
  83. System.out.print((char) c1);
  84. }
  85. System.out.println("==================");
  86. System.out.println("error std:");
  87. int c2;
  88. while ((c2 = stdErr.read()) != -1) {
  89. System.out.print((char) c2);
  90. }
  91. System.out.println("==================");
  92. } finally {
  93. try {
  94. if (stdIn != null) {
  95. stdIn.close();
  96. }
  97. if (stdErr != null) {
  98. stdErr.close();
  99. }
  100. } catch (Exception e) {
  101. }
  102. }
  103. }
  104. }


在上一章已经验证了CommandExec可以很好的支持超时功能,通过它可以更方便的执行外部命令。
但是,这里还有一点需要注意——那就是shell(DOS)中的特殊符号。
因为用Java作为后台程序的系统,多运行于Unix/Linux,以下的介绍将基于如何shell来展开讨论。

假设,系统需要通过提取某个命令的标准输出,来进行某项处理。例如,搜索/路径下的xml文件并对结果处理,如下:

find / -name "*.xml" -type f | xargs java 相应程序
如果按照常规的方式,将以上的几个字串直接传递给Runtime.exec的参数,通过CommandExec的实现方法如下:
  1. CommandExec exec = new CommandExec();
  2. Process process = exec.exec(new String(){"find", "/", "-name", "/"*.xml/"", "-type", "f", "|", "xargs", "java", "相应程序"});

但是,实际结果将事与愿违,因为,在解析"|"的时候是以特殊符号来解析,而JVM将把"|"作为普通的字符串来处理。
在通常的处理中,这种功能可以屏蔽命令溢出的漏洞,但是在特殊需求的时候,就不方便了。
当然,在特殊需求下也有解决方法,可以如下执行以下命令:
  1. CommandExec exec = new CommandExec();
  2. Process process = exec.exec(new String(){"shell", "-c", "/"find / -name '*.xml' -type f | xargs java 相应程序/""});

也就是把要执行的命令作为shell的一个参数让Runtime.exec执行,为shell命令加上-c参数就可以正常执行目标命令了。

细心的读者已经看出,用上面的方法,有一个缺点就是,如果动态生成命令,很容易发生命令溢出的漏洞。
这里有一个解决方法,就是设定一个前提,被执行命令,文字串都必须以单引号包含,那样就比较容易escape了。
escape的方法可以如下定义,并添加到CommandExec当中。
  1. /**
  2. *
  3. * 对shell字串进行escape
  4. *
  5. * @param s 对象字串
  6. * @return 返回escape之后的shell字串
  7. */
  8. public static String escapeShellSpecialCharacters(String s) {

  9. StringBuilder sb = new StringBuilder(s.length() + 128);

  10. sb.append('/'');
  11. for (int i = 0; s != null && i < s.length(); i++) {
  12. char c = s.charAt(i);
  13. if (c == '/'') {
  14. sb.append('//');
  15. }
  16. sb.append(c);
  17. }
  18. sb.append('/'');
  19. return sb.toString();
  20. }
JVM执行外部命令功能的漏洞
到此为止,在Java中执行外部命令的方法以及注意点基本叙述完毕。但是,最后还有一点必须注意的就是JVM本身存在的一个漏洞。

这个漏洞的现象是,当被执行的外部命令,标准输出的量比较大的时候,程序会无故锁死。
特别是在JDK1.3版本尤为明显,在新版本中仍旧存在此漏洞。
不过,通过外部命令的执行方式,可以避免此漏洞的出现——通过重定向避免标准输出。
CommandExec的执行方法如下:
  1. CommandExec exec = new CommandExec();
  2. Process process = exec.exec(new String(){"shell", "-c", "/"find / -name '*.xml' -type f | xargs java 相应程序 > /dev/null/""});

到此结束,欢迎拍砖!



CI Feature Matrix - CruiseControl - Confluence

CI Feature Matrix - CruiseControl - Confluence

在软件测试过程中使用Python做软件持续构建

在软件测试过程中使用Python做软件持续构建

buildbot的master.cfg 文件分析__ 那个路上,比较孤独啊_百度空间

buildbot的master.cfg 文件分析__ 那个路上,比较孤独啊_百度空间

使用BuildBot搭建持续集成环境 - Tony Bai - 博客大巴

使用BuildBot搭建持续集成环境 - Tony Bai - 博客大巴

解决Unsupported major.minor version 51.0错误 | 先有法而后有信,信而后有礼,礼后义,义后仁,德道皆不失也

解决Unsupported major.minor version 51.0错误 | 先有法而后有信,信而后有礼,礼后义,义后仁,德道皆不失也

在项目或者包处,点击右键,选择Properties,选择左面的Java Compiler,在Compiler compliance level,里选择,你的电脑的JDK的版本。如下图:

Center Tables :: Scott Granneman

Center Tables :: Scott Granneman

Center a table with CSS

The old way to center a table was easy:

       ...   

The "align" attribute has been deprecated, however, in favor of CSS (Cascading Style Sheets), and this is a good thing. However, it's not so obvious how to center a table using CSS.

The obvious way might appear to use the CSS "text-align: center;" somewhere, maybe like one of these:

  

OR

  
...

OR, if you get really desperate,

              ...     

None of these will work. The table itself will be left-aligned, but all the content in the table cells will be centered.

Why? Because "text-align" applies to inline content, not to a block-level element like "table".

Method 1

To center a table, you need to set the margins, like this:

  table.center {     margin-left:auto;      margin-right:auto;   } 

And then do this:

       ...   

At this point, Mozilla and Opera will center your table. Internet Explorer 5.5 and up, however, needs you to add this to your CSS as well:

  body {text-align:center;} 

2012年1月28日星期六

《一些你所意想不到的沟通技巧》 第1节 易读

《一些你所意想不到的沟通技巧》 第1节 易读

《一些你所意想不到的沟通技巧》 第1节
作者: zpp_first
  日期:2011-12-12 16:18:00
  一些你所意想不到的沟通技巧
  往往耳熟能详的东西我们却未必真的熟悉,熟悉未必真的能够灵活运用,下面谈谈我所总结的一些你意想不到的沟通技巧。当然文中的很多案例都是本人的假象,希望各位带着怀疑评判地心看,别盲目使用。
  第一个:避开选择法。
  所谓避开选择法就是面对选择时,为了保护自己的利益,为了避开选择带来的风险而使用的方法。

  例题1,问:“你今天晚上有空吗?”答:“有事吗?”
  解析:如果你说没空,他说本来今天请你吃饭的,你惨了;如果你说有空,他说他刚刚搬家,想请人帮忙,你又惨了。
  例题2,问:“你觉得变形金刚好看吗?”答:“你看呢?”
  解析:如果你说好看,他告诉你好看个屁,剧情空洞,你很没脸面;如果你说不好看,他告诉你不看后悔,画面超精彩,你又没脸面。
  例题3,问:“你觉得刘备这人怎么样?”答:“人家说他很仁义。”
  解析:如果你说仁义,他说虚伪;如果你说虚伪,他告你仁义。借用“人家”

  这个词来保护自己比较好。如果对方回说刘备虚伪,你可以继续说我也这么觉得,如果对方也回说仁义,你也可以这么回答,完胜。
  总结:避开选择法就是当你面对难以回答的选择时,通过“你看呢”“有事吗”“人家说”等词汇破解掉。
  注意点:这种方法不适合最亲密的人,我们只是为了保护个人的脸面或者权利,而不是用来耍手段。在那些好为表现的人面前用最好。
  特殊题 1,领导电话来:“现在在忙吗?”答:“我就来”
   解析:如果你说在忙,领导心想我打你电话你还敢说忙;如果你说不忙,领导心想那你整天在干嘛;如果你按上面的说“有事吗”,不是最好的答案,毕竟是领 导。“我就来”是最好的答案,既不正面回答他的问题,也表达出自己服从的意图。其实“我就来”是技巧一与投石问路法的结合使用,下面会再分析。

  特殊题2,刚认识的女友:“晚上有空吗” 你说:“没空”。她说:“你有事?”
  你说:“是啊!有人找我约会啊!”她开始紧张但又假装平静:“谁啊?” 你可以继续玩笑:“一个漂亮的MM”她更加紧张:“真的?”你说:“是啊,晚上你难道不是要请我吃饭吗?”她假装很气:“你真臭美”。
  特殊题2 是我自己假象的对话,以后有机会详说其中技巧。
  练习题:有人问你:“苹果你吃皮吗?” 参考答案:“你呢”
  解析:你说吃皮,他告诉你苹果皮有农药;你说不吃,他告诉你苹果皮全是营养。因此使用技巧一,回答“你呢”。

  第二个:投石问路法。
  投石问路原指夜间潜入某处前,先投以石子,看看有无反应,借以探测情况。所谓投石问路法就是试探法。
  例题1,有人:“这些东西是送给你的” 你答:“不用了”。该人继续:“真的是特意带给你的呢”你答:“家里还有呢,你自己留着用吧”。该人再继续:“跟我还客气啥?拿着!”你答:“那就太谢谢了”。
  解析:这种对话,中国人都看懂,小时候我觉得太烦人,要就要,不要就不要。这里使用了两次试探,试探的目的就是考察对方是否真心愿意把礼物送给你。如果只用一次,就不真诚,耍手段,使用两次比较适合,最多三次,再多就真拒绝了。
   例题2,领导:“你知道物联网吗?”小王略有所知,但说:“不太了解”。一种情况,领导开心地讲起来:“物联网啊是这样子的。。。”小王一听原来自己都 知道,但说:“哦,您懂的真多,我还真没大听说,今天终于明白了”。领导很得意。另一种情况,领导疑问地说:“你也不知道啊,今天有人说起,我还没听说 过”,小王现在知道领导是真的想了解,于是说:“前些日子听人家说物联网是。。。。”领导很满意,说:“小伙子可以啊,懂的蛮多。”

  解析: 这里小王的测试主要是由于他不太清楚领导是真的想问他,还是领导只是借此发挥了一下个人的知识面。
   例题3,刚认识的朋友:“小明,一起过来打牌吧” 小明:“我不太会打牌啊” 朋友:“没事,两把就会了”小明:“我的技术不行,还不如直接让我请你们吃东西呢” 于是朋友就不为难,小明在一旁观看。玩了许久,小明发现他们的技术完全不如自己,其中一个朋友说:“小明你来吧,试试”。小明假装笨拙的拿着扑克,但是内 心计算非常精确,小小胜了那些朋友,一边说这个完全是运气,一边把赢的钱拿出来请客,大家则一边称赞小明聪明,一边感慨小明义气。

  解析:小明面对刚刚认识的人,保持着保护自我的心态,不想输钱太多,采用了试探的方法,即先说自己不会玩。这样以后玩的时候,如果赢了,那继续玩,就当运气;如果输了,可以用那个不会玩的借口,退出来,别人也不会勉强。
   例题4,领导召开小会议:“关于这个方案你们什么看法?你们一个一个说”甲说:“这个问题还没考虑周全”乙心里感到快意,居然甲不会,得意的说:“这个 方案。。。”丙关于此方案研究几天,心想问到我其他的我可能不知道,问到我这个我恰恰精通,哇啦哇啦地讲:“这个方案。。。。”由于甲研究了一个礼拜,再 加上又听了乙和丙的叙述,综合起来回答:“对了,我刚刚突然想到。。。”技压群雄,领导颇为满意。

  解析:这个例子我挣扎要不要放上 面,因为给人感觉好像阴谋诡诈,这里甲感到自己第一个说话必然会不周全,会被后面的人补充,因此心生一计,采用测试的方法,先看别人。给各位一个观点:真 正懂的人不敢说出来,越是不懂的人越是很快说出来。当然各位别拿这句话来将我军啊!该例子慎用,只能偶尔,会伤感情,小心。
  例题5,回看特殊题1。
  解析:“我就来”就是一种测试,此人心想领导来电话正常情况必有意图,但是又不知道什么意图,(当然不知道,比较安全),才用测试的方法,把决策权交还给领导,领导让他去就去,领导说不用去,就可以不去。
   例题6,张经理的部门走了一个人,需要补缺。张经理就跑去自己的直接上司周总那,说:“咱部门走了一个人,您看?”周总说:“哦,招人啊,你让人事部去 办就是啦”。张经理想到了周总的侄儿小周,说:“我看没那么麻烦了,我觉得您侄儿就很合适调过来。”周总说:“他啊,不学无术,基层再练练”。张经理又想 到了周总器重的小王,说:“我看小王最近表现也不错,不如调他来帮忙吧”。周总说:“小王能力是不错,只是太年轻,你的意思我懂,招人还是按流程来”。此 时张经理更加佩服周总,果然大公无私,周总更加信任张经理,果然心中有我。

  解析:张经理这里的做法相当的谨慎,往往一般的人不会这么 仔细,好点的做法让人事部去处理,更坏的做法就是安插自己的人。即使不这么做,周总也会心生怀疑,居然招人的事我知都不知道。虽然周总平时嘴上说大公无 私,但是张经理真的不知道周总是真心还是假意,于是通过两次测试的方法,测试那些周总最希望的人选,假如周总真的让他侄儿去了,张经理也就更加明白了周总 的为人,以后就更加清楚如何与周总互动了。因此这个案例是张经理测试周总。

总结:投石问路法有这样几个要点:第一,一个人首先要养成隐藏自己意图的修养。第二,当你不知道对方意图的时候,尤其不知道对方是否真心,你需要通过测 试。第三,测试所使用的手段往往先说对自己不利,对他人有利的方案。一般先说不要,不会,不懂,没有意见。我个人以为投石问路法可以用于识别他人,例题6 就是一个经典的识别领导的做法。其难点是要有意识去揣摩他人的想法,尤其领导的想法。因此给各位一个建议:无论何时何地,你必须心系家庭、领导,任何决策 都要提醒自己是否考虑他们的想法,在中国社会我相信你一定会平步青云!

  练习题:你最近表现不错,领导把你招到办公室,想让你升为科长,你需要提醒自己什么,你又怎么回答?
   参考答案:领导说:“你最近表现不错啊” 你说:“哪里哪里,只是按照您的决策做吧了”他又说:“哎,别来给我戴高帽,我知道你最近特别辛苦,现在科长之位空缺着,就由你来做吧。”你说:“这个万 万不行啊,我现在经验还不足,还是在基层多锻炼锻炼。”他继续说:“我相信你的能力,哪有人天生就有经验啊”你再说:“这个真不行的,我刚来公司不久,就 突然升迁,怕别人不服啊。”他接着说:“怎么会的,公司用人凭能力,这个你放心。”你最后说:“既然这样,那就谢谢您的栽培,暂且代理此职了,不过如果您 有更合适的人选,我随时可以让出来。”瞬间,领导感慨涕零,心想真是好青年啊!

  解析:很多人可能很反感我这样啰嗦的对话,可是你要知 道这样一来一回有两个好处:其一,你通过测试终于知道领导是真心的,当然或许也是领导再测试你;其二,你之后做科长再也不用担心能力经验的问题,下面人不 服的问题,因为你已经在之前说了。聪明的人会在升迁之前把困难都说出来,表现出不想当但是因为推卸不掉而勉强当的感觉。

  未完待续。。。。
  日期:2011-12-21 23:15:53
  第三种:主观假设法。
  主观假设其实有点像三十六计中的无中生有,也有点像心理学中的冷读术。
   例题1 小李是某学院学生会主席,正在筹办一个迎新晚会,时间预计本月20号就要到期,但是已经都10号了,得知部分节目还没有筹划好,小李感到特别着急,但是却 稳住脾气,看到一个正在晃啊晃的小张。小李跟他微笑地说:“你们最近都忙得辛苦了,看来节目都完成得差不多了吧。”小张突然被带上高帽,防备心下降一半, 知道自己刚才的行为好像不好,开始解释。

  解析:现实中的很多人感到时间来不及的时候,就开始压抑不住自己的情绪,看到还有人在闲晃, 大声喊道:“你们都在忙什么啊,怎么到现在还没有弄好啊”那么很容易就可以推出的是,学生会的成员肯定就想,“你算老几啊,我们忙到现在,你就会指手画 脚。”关于此案例,以后会展开分析,这里到此为止。
  例题2 比如,看到一个不太熟的朋友说,“老兄啊,看你神采飞扬,最近走桃花运了吧!”。比如,一个女孩走出图书馆看到一个心仪的男孩,在想怎么搭讪,看到他手中有本书,便说:“Hi,你拿本书的样子看上去特别文质彬彬,你的学习成绩肯定很棒吧!”
  解析:这里的主观假设就是无话题找话题,其实也是一个测试,再探视对方此时心情如何,看能否继续聊下去;另外,假设一般都是赞美的话,让对方瞬间卸下防备。
  总结:主观假设法有两种作用:其一,用于管人,一般假设对方是积极向上的,稳定对方情绪,让对方良心发现,因为人性吃软不吃硬,我们什么都不怕,就怕被别人夸奖,被别人关心。其二,用于增加聊天的内容,拉近人之间的关系。
  练习题1:你是一个父亲,今天碰巧晚上回家比较早,突然看到你的儿子正在看电视,你非常生气,你该怎么办?
   参考答案:你和蔼地对你孩子说:“哎,今天你作业做的倒挺快的呢?在看啥好看的啊?”于是坐到他旁边一起看会儿,他真以为你不在乎他看电视,说:“作业 还没写完呢,现在正是篮球比赛呢,什么队跟什么队”。如果你立刻发火,那么前功尽弃。你继续说:“这个大概放多久啊?”他说:“两个小时。”你接着说: “那什么时候睡觉呢?”他说:“做完作业。”你继续说:“这样,要是太晚了话,看完电视就早点睡,作业的事明天我跟老师说下。”他一听,感觉不对,说: “千万别,作业我看完就做”那你就开始发火说:“到现在还不把作业做好,居然在这里看电视。”他很不情愿地走进房间,耷拉着脑袋。你接着说:“看你也不能 用心学习,今天就不为难你了,看完早点做作业啊。”他此时绽放笑容:“爸爸,万岁。”

  点评:这个案例由于对话较多,可能未必真实,还请各位指教。我的观点以为,这里先通过主观假设稳定情绪,然后通过提问给对方压力,不断点他,给他足够面子,点不通适时也要发火,发完火之后再去火,恩威并济。
  练习题2:如果你是男生,女生的电脑坏了,请你帮她修电脑。你会有什么好的沟通呢?
  参考答案:你可以这样运用主观假设法,说:“电脑怎么了,你是不是看了什么不该看的网页啊!”如果她说她想借用你的电脑,你说“不行啊,这里面的资料你不适合看,少儿不宜啊。”或许她更加感兴趣。
  点评:多少男生得知女生请他修电脑,高兴得忙得不知所措,认认真真地修,接着就跟女生讲很多计算机的专业术语,告诉她电脑坏的原因,以显示自己的能力。可是对方一点不感兴趣,最多下次修电脑的时候再叫上你。
  第四种:完整回答法。

  完整回答法相当于数学里面的分类讨论。
   例题1:三个年轻人要去当和尚,大法师分别问他们:“你们为什么选择来当和尚”。第一个人说:“爸爸叫我来的”。大法师啪的一个下,说:“这么重要的事 居然自己不想清楚就来”。第二人看了之后,说:“我是自己认真考虑之后来的。”结果又是啪的一下,大法师说:“这么重要的事不听听爸爸的意见。”第三个人 傻了,心生一计,说:“我是受释迦摩尼召唤的”。啪的更重,大法师说:“我都这么多年了,还没有背召唤。”其实比较好的答案是:“一方面自己的想法,一方 面是爸爸的意思,做梦的时候又受到您的感召。”

  例题2:听说非诚勿扰有这样一期,一个哈佛耶鲁的男生问台上的女生说:“如果你有一千 万,你会怎么办?”每个女生各有各的回答,最后因为男生觉得没有人去捐款而感到痛心,因此放弃。当我听说到这个故事的时候,一方面也和大家一样觉得这男的 装逼,但是我又想,如果我回答的话,我一定是:“假如我有一千万,会拿一部分给父母,拿一部分给自己,拿一部分给需要借钱的亲戚朋友,再拿一部分捐出 去”。

  总结:完整回答法,意思很容易懂,尤其当面对对方突然的一个大问题,你怎么回答都会入陷阱,又不能避开选择。但是难点有二:第一,你必须意识到这是一个分类讨论问题。第二,你必须分析尽量完整。完整回答就是告诉我们应该有通盘考虑,全面考虑地思维,立于不败之地。
  练习题:给你一个面试题:如果公司有100万元,现在生产、管理、销售三个部门需要发年终奖,该如何分配?
   参考答案:第一,如果本年三个部门状况良好,那就按去年的分配方法;第二,如果本年生产部门状况最不好,那就增加生产部门的分配额,提高生产质量;第 三,如果管理部门状况最不好,那就增加管理部门的分配额,提高管理效率;第四,如果销售部门状况最不好,那就增加销售部门的分配额,提高销售业绩。
  第五种:不言行动法。
  不言行动法就是也是一种巧妙的沟通方式,为了表达自己的一些感受,此时说什么话都感觉不合时宜,行动比语言更重要。

例题1:有一次胡雪岩的老婆对他说:“我突然想念我已故去的双亲了”胡雪岩似乎表示漠不关心,可是当她接到胡雪岩去杭州的时候特地从她双亲坟地上带来的一些乡土的时候,备受感动。
  例题2:一对情侣吵完架,该男生没有选择一遍一遍的解释,一个一个的短信,而是略有霸气地打过去一个电话:“滚出来”,然后带着有脾气的女友出去吃饭,吃饭的过程中,拿出精心准备的鲜花,真诚而又温柔地说:“亲爱的,对不起。”
  例题3:公司成员甲一不小心说话伤害了成员乙,他感到很内疚,可是觉得怎么说都很尴尬,于是选择若无其事的一样,说:“哥们,今天我请大家吃饭啊”。饭局中,选择好的机会给乙敬酒,从此问题便化解了。
  总结:一个人为了向另一个人表达关心、爱护、歉意等等的感受,有时候选择实际行动,比言语来得更好。
  练习题:如果你和女友逛街,突然她说她好像感冒了,你该怎么办?

  参考答案:你装着漠不关心,但是等你看到药店的时候,你只需要说:“我们去逛个商场吧”,然后带到药店,买下药送给她。
   分析:很多人可能会选择问:“那要不要休息下?”“要不要去买药”“要不要回去”。我个人感觉表达关心通过询问效果是差的,应该通过自己的感觉,然后做 出实际的行动。不言行动往往与无所谓或者漠不关心结合起来威力无穷,通过前后形成对比,对方从对你失望到对你崇拜,只在一瞬间。
  未完待续。。。(看回帖效果了)
  日期:2011-12-24 21:00:49
  第六种:表里不一法。
  表里不一法,顾名思义就是说人表面的看法或意图与他实际的情况恰好相反,已达到他所希望的目的。常常用于领导管人。

  例题1:常常父母、老师、领导会在别人面前夸奖我们,但是当他们和我们独处的时候,他们却非常严厉地批评起来。
  点评:这是我们常常遇到的案例,长辈在外人夸奖我们,是为了给我们推销,让外人多给我们机会;长辈私下批评我们,是为了给我们警钟,让我们不断进步提高。
  例题2:张总是公司的新任总经理,于是想聘用自己信赖的干部,以取代那些倚老卖老的家伙。但是他却并没有立刻大刀阔斧,而是宣言一切照旧,实际上却是一个一个地换人。
   点评:这里的表里不一法运用非常好。表面上说一切照旧,主要是为了稳定众人的心理;实际上却一个一个换人,主要是为了公司的发展。如果张总一上任就说: “本公司有很多的弊端,很多干部倚老卖老,我会在人事上做些调整,你们心理做好准备”,那么可以预见的是干部的抵抗心理非常强,他们责怪新官上任三把火, 那些被辞退的人就会非常没面子,抵抗报复心就强烈。

  例题3:王经理召开部门例会,说:“各位谈谈咱部门有哪些需要改进的地方,都讨论 讨论?”突然一个新进员工,抢着发言,指出了部门非常多的不足。王经理一听心里非常满意,终于部门里有个用心做事的人了,可是他却非常严厉地指责那个新员 工:“你年纪轻轻,就说这里不好,那里不好,你懂的很多吗,多听听人家前辈们的说法。”会后,王经理特意请那个还在郁闷中的新员工到办公室,语重心长地 说:“以后部门就靠你了,我知道你最用心了,但是刚才是为了保护你,为了给别人面子。”

  点评:王经理这里运用了表里不一,表面上责备 新员工,实际上是非常赞成他的观点。王经理非常老道,控制了自己喜悦的心情,为了保护该员工而不得不责备他。试想,如果王经理当场就高兴地赞扬他,其他 人,尤其老员工,就会非常没有面子,以后他们会给新员工更多地阻碍。三国中就可以看出,刘备初得诸葛亮,说如鱼得水,关羽张飞就非常不满意,而且非常不配 合诸葛亮;曹操每次都当众夸奖小儿子曹冲,最后曹丕为了帝位不得不除掉潜在竞争者,其实杀害曹冲的人就是曹冲自己,因为他表现得过于优秀。当然,如果有员 工讲了很多,可是不符合领导的心意,优秀的领导者不会当场就批评对方,而是略带表扬地指出对方中有道理的地方,因为既然不采用对方的观点,不如肯定对方, 以鼓励对方以后更好的发言。

  例4:一天一个小伙子跟一群朋友聚会,聚会中突然见到一个心仪的大美女,该小伙知道自己的相貌并非超群, 他并没有像其他男生一样不时地打量那个女生,想法设法地去找话题陪她聊天,而是和普通的女生谈笑风生却从不看那美女一面。当谈到有兴趣话题,那个美女凑上 来问什么事时,这个小伙子却淡淡地说:“要听旁边一点,你这么高把我光线给挡了!”

  点评:先听听我的逻辑,再考虑他的可行性。小伙子 表面上对那个女生很冷淡,实际上却很喜欢那个女生。其一,小伙子知道自己如果向其他人一样去搭讪,去称赞就会成为众多追求那个美女的男生之一;其二,小伙 子知道大美女都对自己的外貌自信,他就偏偏不去看,充分利用美女对自己外貌自信的弱点;其三,大美女一般不会有人敢对她如此冷淡,而今这个小伙子却与众不 同,越发产生好奇;其四,大美女看到这个小伙子能够和普通女生聊得这么投入,则会以为该小伙并不是那种在乎女生外貌的人,因为美女并不希望对方只是因为自 己的外貌而喜欢自己。注意点有二:第一,这里适合那些对自己外貌非常自信的女生,她们会时时刻刻在乎哪些人会偷看自己;第二,你必须真正的内心强大,而且 真的表现得无所谓但友好的态度,不然你内心的不自信会被察觉,女生会以为你是不敢正视她。或许你担心事情搞砸的情况,是的,或许你给那个女生留下不好的印 象,但是我以为不好的印象总比没有印象要好得多。

  总结:表里不一是一种周全的方法,表面上的行为往往是为了顾全整体的面子、情绪,实 际上的行为却是为了个别差异,突出个人的能力。表里不一充分地考虑了团队与个人的对立统一。表里不一的难点是要有很强的控制自己喜好、偏爱、愤怒等情绪, 例4中的小伙子就必须有很强的控制自己欲望的能力,因为那些见到美女就开始表现的人潜意识里透入出内心的不自信,而这种不自信一旦被察觉,吸引力则锐减。 为了这个难点,我开始这样训练自己:每次当内心有喜好这样的情绪,有目的或者意图,都要问自己这种情绪或者意图能否直接表露出来,如果不能,表面该怎么 办,实际该怎么办。当然实际操作很难!

  练习题1:王总正在考虑薪酬激励机制,是一视同仁呢?还是个别差异?
  参考答案:王总表面上一视同仁,实际上个别差异。
  点评:在中国社会,我以为苦劳要公开公平,功劳要私下个别激励。苦劳公开公平是为了稳定全部成员的情绪,私下个别激励,是为了维护其他人的脸面。
  练习题2:你是个学生,刚刚放假回家,爸爸让你把客厅地好好拖拖。此时你想看书,你该怎么办?
  参考答案:你对爸爸说:“好的,我就来。”然后去房间里去看书。

点评:你表面上答应,实际上你并没有答应。你答应仅仅是为了不顶撞爸爸而已,而且你确实现在想看书嘛。一般错误的回答是这样,你说:“不行啊,我现在要看 书呢。”爸爸听到这话必然脸面没有,一定发火说:“那你整天在学校干什么的?回来做做家务不可以嘛?你多大啊?”我们回答参考答案,如果爸爸问你怎么不拖 地,你说:“等看完这书就去拖!”,如果爸爸继续问要多久,你说:“就两个小时吧。”爸爸此时会很高兴,心想儿子读书这么用功,还不忘做家务,于是喜悦地 说:“既然这样,你继续看,我来拖。”

  练习题3:你是公司职员,上司让你完成一个任务,但是以你当时的判断明确可以断定这个任务无法完成,你该怎么办?
  参考答案:你表面上说:“好好,我这就办”。然后回去过了十分钟,又跑到上司的办公室,说:“刚刚我突然想到几个困难,来请教一下。。。”
  点评:你表面上答应说好好,是为了上司的面子,如果你立刻拒绝,上司就会说:“每次一给你任务,你就说这个说那个,你都没试过怎么说不行”。如果你继续分析这个任务不能完成的道理,道理越对,上司越没面子,越火大。
  日期:2011-12-27 15:40:48
  第七种:布局造势法。
  布局造势法来源于孙子兵法,意思是说通过一系列策略使得对方能够自发地按照自己的愿望做。
   例题1:王同学在宿舍里,躺在床上看书,忽然发现天色逐渐变暗,他希望离日光灯开关最近的李同学把灯开一下。王同学这样说:“哇,时间真快,天色突然这 么黑啊!”李同学一听,立刻起身去开灯,王同学立刻说:“哎,每次都你开灯真不好意思,这次我来!”说完这句话李同学已经把灯亮了,一边说:“开个灯算什 么,你不方便,谁叫我离最近嘛。”

  点评:最糟方法是王同学直接对李同学说:“把灯开一下”,因为在他以为李同学离的最近开灯是应该 的。可是李同学一定会说(即使不说,一定会想):“凭什么每次都我开灯啊,你难道没腿没脚啊!”一般方法是王同学直接对李同学说:“麻烦把灯开一下”。李 同学则心里会想:“你每次倒好,嘴上麻烦一下就让我真的麻烦一下。”最后是不情愿地去开灯。下面来分析例题1:王同学的“天色突然这么黑啊!”这句话就是 布局,就是在提醒李同学你此时应该自发地去开灯,李同学自发去开灯时,王同学说:“每次都是你开灯真不好意思,这次我来”就是造势,因为这句话说到李同学 的心里,体会到他每次开灯的苦。人性很奇怪,当别人一旦替自己说话,自己就会替别人说话。此时李同学开灯对他来说是件很快乐的事,因为他感觉给宿舍带来了 方便。

  例题2:本校的经济学专业一直非常弱,赵校长发现全校只有经济学专业没有博士生,于是找到曾系主任,想让他聘请一位博士生。 赵校长说:“咱们学校几个系啊?我都记不得了”曾主任奇怪地说:“这个您最清楚了,9个啊!”赵校长:“各个系师资怎么样啊?”曾主任开始详细说哪个系有 多少博士,多少教授。赵校长假装疑问:“为什么就经济系没有博士呢?”曾主任说:“现在经济系的博士难找啊!”赵校长接着说:“难找的话看来是我去找 吗?”曾主任忙说:“不,不,还是我去找。”赵校长说:“哎,这个不行,你现在那么忙,系里面事务多,况且招聘也很不容易啊!”曾主任立刻说:“我的工作 还好了,还是可以抽出时间来的。”赵校长说:“那最迟什么时候呢?”曾主任说:“一个月吧”赵校长说:“这个不急,你慢慢来,给你一个半月,工作别太辛 苦,多休息。”

  点评:这些对话全部设想,未必真实。从曾主任的“不,不,还是我去找”之前是布局,就是在点曾主任自己主动去接这个任务,之后是造势,让曾主任干得很自愿!
   例题3:王总发现自己企业的产品成本比其他企业高,通过自己的分析发现原来是单位时间的产量太低,准备采取加班方案。王总让财务部门把产品成本计算出 来,把各个部门干部招来,说:“看看这个财务报告,说说你们的看法呢?”干部就开始分析起来,有人说产品成本高,有人说其他,通过这些干部的分析,王总发 现谁是用心的谁是瞎混的。可是假装不知,接着问:“啊?我们产品成本高?不会吧,你们的管理不错啊,怎么会成本高呢?”然后干部们就开始分析,最终分析出 来是单位产量太低。王总心里知道一般企业要单位时间生产30个,可是他仍然问:“啊?不会啊,我们员工很努力啊!怎么会单产低呢?低多少啊?”于是干部分 析:“一般要生产30个,我们只有24个。”接着,干部们就开始讨论,于是有人提出来:“以现在的情况,只能加班了。”王总说:“加班,这个怎么行呢,你 们都已经这么辛苦了,工人们也都卖力了。”于是干部们说:“没有拉,还是可以再努力的,有人还是愿意加班的。”王总说:“这样吧,加班的事你们再研究研 究,跟底下人沟通沟通,行就行,不行就算了。”干部们一听,感慨涕零,众志成城,加班的决心非常强烈,心里都在想,真是好老板啊,爱民如子!

   点评:王总心里想着要加班,可是他并没有直接以命令的口气,因为那样的话一定会造成干部的强烈抵抗。如果王总说:“为什么人家可以做30个,而我们只有 24个”干部一定说:“人家是人家,我们是我们”。心里却想,“人家老板好啊!”王总知道直接的方法不是最佳,采用了布局造势法。王总通过旁敲侧击,通过 投石问路,通过一个一个地提问,让干部自己分析出自己想要说出的事,这个过程就是布局。当干部开始主动提出加班的时候,王总开始运用关心,以退为进,这个 过程就是造势。最后的效果就会良好。

  总结:布局造势是相当的艺术。核心原理是,人性不太愿意接受别人的要求,但会接受自己的承诺。布 局就是让对方主动说出我想要说的话,其方法一般是主动提出问题或者现象,旁敲侧击,让对方自己去思考;造势就是让对方非常愿意完成他提出来的事,其方法一 般是激将或关心,以退为进。
  练习:你是产品部经理,你发现本公司的产品设计得并不漂亮,觉得市场上其他公司的产品设计水平已经高于你们公司,你该怎么办?
   参考答案:你并没有直接要求你部门的员工重新修改,因为那样会遭到抵抗。你运用布局造势法。你这样布局,你找到其他公司设计得更好的产品,然后带给你的 员工看,让他们点评点评。如果有人说很差,你就说:“怎么个差法,你是什么判断标准?”一下子就可以看出这家伙就是来捣蛋的。如果有人说很好,你就说: “不会啊,我看人家的产品也不怎么样啊,我们是什么地方不如人家呢?”于是你的成员开始分析议论,最后一致认为还是外观不够精致,尤其颜色不合适,需要重 新修改。你说:“重新修改?这怎么行呢?你们这么辛苦,好不容易刚刚弄出来,又要改,太麻烦了吧。”他们内心感到非常内疚,继续说:“不麻烦的,只是部分 的修改”你说:“你们再商量商量,可以改就改,不行就这样吧”。你越这样,他们越要求改,然后团结一致,下决心一定要超越别人。等你的员工正在加班修改设 计的时候,你掏出你的私人钱包,买了一些夜宵送给他们,你的员工更是感动万分,有人说:“今天不完成,咱就不回”你可以继续说:“别,别,吃完早点回吧, 已经太晚了,回家注意安全”。当晚,他们一定是非常努力,而且毫无怨言。