Archive

Archive for the ‘脚本语言’ Category

Handy one-liners for awk

March 9th, 2010 ricky.zhu No comments

HANDY ONE-LINERS FOR AWK 22 July 2003
compiled by Eric Pement version 0.22
Latest version of this file is usually at:

http://www.student.northpark.edu/pemente/awk/awk1line.txt

USAGE:

Unix: awk ‘/pattern/ {print “$1″}’ # standard Unix shells
DOS/Win: awk ‘/pattern/ {print “$1″}’ # okay for DJGPP compiled
awk “/pattern/ {print \”$1\”}” # required for Mingw32

Most of my experience comes from version of GNU awk (gawk) compiled for
Win32. Note in particular that DJGPP compilations permit the awk script
to follow Unix quoting syntax ‘/like/ {“this”}’. However, the user must
know that single quotes under DOS/Windows do not protect the redirection
arrows (< , >) nor do they protect pipes (|). Both are special symbols
for the DOS/CMD command shell and their special meaning is ignored only
if they are placed within “double quotes.” Likewise, DOS/Win users must
remember that the percent sign (%) is used to mark DOS/Win environment
variables, so it must be doubled (%%) to yield a single percent sign
visible to awk.

If I am sure that a script will NOT need to be quoted in Unix, DOS, or
CMD, then I normally omit the quote marks. If an example is peculiar to
GNU awk, the command ‘gawk’ will be used. Please notify me if you find
errors or new commands to add to this list (total length under 65
characters). I usually try to put the shortest script first.

FILE SPACING:

 # double space a file
 awk ‘1;{print ""}’
 awk ‘BEGIN{ORS="\n\n"};1′

 # double space a file which already has blank lines in it. Output file
 # should contain no more than one blank line between lines of text.
 # NOTE: On Unix systems, DOS lines which have only CRLF (\r\n) are
 # often treated as non-blank, and thus ‘NF’ alone will return TRUE.
 awk ‘NF{print $0 "\n"}’

 # triple space a file
 awk ‘1;{print "\n"}’

NUMBERING AND CALCULATIONS:

 # precede each line by its line number FOR THAT FILE (left alignment).
 # Using a tab (\t) instead of space will preserve margins.
 awk ‘{print FNR "\t" $0}’ files*

 # precede each line by its line number FOR ALL FILES TOGETHER, with tab.
 awk ‘{print NR "\t" $0}’ files*

 # number each line of a file (number on left, right-aligned)
 # Double the percent signs if typing from the DOS command prompt.
 awk ‘{printf("%5d : %s\n", NR,$0)}’

 # number each line of file, but only print numbers if line is not blank
 # Remember caveats about Unix treatment of \r (mentioned above)
 awk ‘NF{$0=++a " :" $0};{print}’
 awk ‘{print (NF? ++a " :" :"") $0}’

 # count lines (emulates "wc -l")
 awk ‘END{print NR}’

 # print the sums of the fields of every line
 awk ‘{s=0; for (i=1; i< =NF; i++) s=s+$i; print s}’

 # add all fields in all lines and print the sum
 awk ‘{for (i=1; i<=NF; i++) s=s+$i}; END{print s}’

 # print every line after replacing each field with its absolute value
 awk ‘{for (i=1; i<=NF; i++) if ($i < 0) $i = -$i; print }’
 awk ‘{for (i=1; i<=NF; i++) $i = ($i < 0) ? -$i : $i; print }’

 # print the total number of fields ("words") in all lines
 awk ‘{ total = total + NF }; END {print total}’ file

 # print the total number of lines that contain "Beth"
 awk ‘/Beth/{n++}; END {print n+0}’ file

 # print the largest first field and the line that contains it
 # Intended for finding the longest string in field #1
 awk ‘$1 > max {max=$1; maxline=$0}; END{ print max, maxline}’

 # print the number of fields in each line, followed by the line
 awk ‘{ print NF ":" $0 } ‘

 # print the last field of each line
 awk ‘{ print $NF }’

 # print the last field of the last line
 awk ‘{ field = $NF }; END{ print field }’

 # print every line with more than 4 fields
 awk ‘NF > 4′

 # print every line where the value of the last field is > 4
 awk ‘$NF > 4′

TEXT CONVERSION AND SUBSTITUTION:

 # IN UNIX ENVIRONMENT: convert DOS newlines (CR/LF) to Unix format
 awk ‘{sub(/\r$/,"");print}’   # assumes EACH line ends with Ctrl-M

 # IN UNIX ENVIRONMENT: convert Unix newlines (LF) to DOS format
 awk ‘{sub(/$/,"\r");print}

 # IN DOS ENVIRONMENT: convert Unix newlines (LF) to DOS format
 awk 1

 # IN DOS ENVIRONMENT: convert DOS newlines (CR/LF) to Unix format
 # Cannot be done with DOS versions of awk, other than gawk:
 gawk -v BINMODE="w" ‘1‘ infile >outfile

 # Use "tr" instead.
 tr -d \r <infile>outfile            # GNU tr version 1.22 or higher

 # delete leading whitespace (spaces, tabs) from front of each line
 # aligns all text flush left
 awk ‘{sub(/^[ \t]+/, ""); print}

 # delete trailing whitespace (spaces, tabs) from end of each line
 awk ‘{sub(/[ \t]+$/, "");print}

 # delete BOTH leading and trailing whitespace from each line
 awk ‘{gsub(/^[ \t]+|[ \t]+$/,"");print}
 awk ‘
{$1=$1;print}‘           # also removes extra space between fields

 # insert 5 blank spaces at beginning of each line (make page offset)
 awk ‘{sub(/^/, "     ");print}

 # align all text flush right on a 79-column width
 awk ‘{printf "%79s\n", $0}‘ file*

 # center all text on a 79-character width
 awk ‘{l=length();s=int((79-l)/2); printf "%"(s+l)"s\n",$0}‘ file*

 # substitute (find and replace) "foo" with "bar" on each line
 awk ‘{sub(/foo/,"bar");print}‘           # replaces only 1st instance
 gawk ‘
{$0=gensub(/foo/,"bar",4);print}‘  # replaces only 4th instance
 awk ‘
{gsub(/foo/,"bar");print}‘          # replaces ALL instances in a line

 # substitute "foo" with "bar" ONLY for lines which contain "baz"
 awk ‘/baz/{gsub(/foo/, "bar")};{print}

 # substitute "foo" with "bar" EXCEPT for lines which contain "baz"
 awk ‘!/baz/{gsub(/foo/, "bar")};{print}

 # change "scarlet" or "ruby" or "puce" to "red"
 awk ‘{gsub(/scarlet|ruby|puce/, "red"); print}

 # reverse order of lines (emulates "tac")
 awk ‘{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j–] }‘ file*

 # if a line ends with a backslash, append the next line to it
 # (fails if there are multiple lines ending with backslash…)
 awk ‘/\\$/ {sub(/\\$/,""); getline t; print $0 t; next}; 1‘ file*

 # print and sort the login names of all users
 awk -F ":" ‘{ print $1 | "sort" }‘ /etc/passwd

 # print the first 2 fields, in opposite order, of every line
 awk ‘{print $2, $1}‘ file

 # switch the first 2 fields of every line
 awk ‘{temp = $1; $1 = $2; $2 = temp}‘ file

 # print every line, deleting the second field of that line
 awk ‘{ $2 = ""; print }

 # print in reverse order the fields of every line
 awk ‘{for (i=NF; i>0; i–) printf("%s ",i);printf ("\n")}‘ file

 # remove duplicate, consecutive lines (emulates "uniq")
 awk ‘a !~ $0; {a=$0}

 # remove duplicate, nonconsecutive lines
 awk ‘! a[$0]++‘                     # most concise script
 awk ‘
!($0 in a) {a[$0];print}‘      # most efficient script

 # concatenate every 5 lines of input, using a comma separator
 # between fields
 awk ‘ORS=%NR%5?",":"\n"‘ file

SELECTIVE PRINTING OF CERTAIN LINES:

 # print first 10 lines of file (emulates behavior of "head")
 awk ‘NR < 11

 # print first line of file (emulates "head -1")
 awk ‘NR>1{exit};1

  # print the last 2 lines of a file (emulates "tail -2")
 awk ‘{y=x "\n" $0; x=$0};END{print y}

 # print the last line of a file (emulates "tail -1")
 awk ‘END{print}

 # print only lines which match regular expression (emulates "grep")
 awk ‘/regex/

 # print only lines which do NOT match regex (emulates "grep -v")
 awk ‘!/regex/

 # print the line immediately before a regex, but not the line
 # containing the regex
 awk ‘/regex/{print x};{x=$0}
 awk ‘
/regex/{print (x=="" ? "match on line 1" : x)};{x=$0}

 # print the line immediately after a regex, but not the line
 # containing the regex
 awk ‘/regex/{getline;print}

 # grep for AAA and BBB and CCC (in any order)
 awk ‘/AAA/; /BBB/; /CCC/

 # grep for AAA and BBB and CCC (in that order)
 awk ‘/AAA.*BBB.*CCC/

 # print only lines of 65 characters or longer
 awk ‘length > 64

 # print only lines of less than 65 characters
 awk ‘length < 64

 # print section of file from regular expression to end of file
 awk ‘/regex/,0
 awk ‘
/regex/,EOF

 # print section of file based on line numbers (lines 8-12, inclusive)
 awk ‘NR==8,NR==12

 # print line number 52
 awk ‘NR==52
 awk ‘
NR==52 {print;exit}‘          # more efficient on large files

 # print section of file between two regular expressions (inclusive)
 awk ‘/Iowa/,/Montana/‘             # case sensitive

SELECTIVE DELETION OF CERTAIN LINES:

 # delete ALL blank lines from a file (same as "grep ‘.‘ ")
 awk NF
 awk ‘
/./

CREDITS AND THANKS:

Special thanks to Peter S. Tillier for helping me with the first release
of this FAQ file.

For additional syntax instructions, including the way to apply editing
commands from a disk file instead of the command line, consult:

“sed & awk, 2nd Edition,” by Dale Dougherty and Arnold Robbins
O’Reilly, 1997
“UNIX Text Processing,” by Dale Dougherty and Tim O’Reilly
Hayden Books, 1987
“Effective awk Programming, 3rd Edition.” by Arnold Robbins
O’Reilly, 2001

To fully exploit the power of awk, one must understand “regular
expressions.” For detailed discussion of regular expressions, see
“Mastering Regular Expressions, 2d edition” by Jeffrey Friedl
(O’Reilly, 2002).

The manual (“man”) pages on Unix systems may be helpful (try “man awk”,
“man nawk”, “man regexp”, or the section on regular expressions in “man
ed”), but man pages are notoriously difficult. They are not written to
teach awk use or regexps to first-time users, but as a reference text
for those already acquainted with these tools.

USE OF ‘\t’ IN awk SCRIPTS: For clarity in documentation, we have used
the expression ‘\t’ to indicate a tab character (0×09) in the scripts.
All versions of awk, even the UNIX System 7 version should recognize
the ‘\t’ abbreviation.

#—end of file—

Categories: 脚本语言 Tags:

GNU/Linux Advanced Administration

February 4th, 2010 ricky.zhu 2 comments

非常不错的一本电子书,从GNU/Linux的来历,发行版,内核,一直讲到GNU/Linux的管理(包括工具,网络管理,数据管理,服务器管理,安全管理,配置管理和优化以至于集群技术)非常的全面和细致,共享一下。

PDF下载

Categories: 脚本语言 Tags:

Shell中的[]

December 17th, 2009 ricky.zhu 10 comments

Shell中的[],没太搞明白,请高手指点。

ricky@ricky-desktop:~$ a=0
ricky@ricky-desktop:~$ if [ $a ]; then
> echo "Gooooood"
> else
> echo "Baaaaaaad"
> fi
Gooooood
ricky@ricky-desktop:~$ a=1
ricky@ricky-desktop:~$ if [ $a ]; then echo "Gooooood"; else echo "Baaaaaaad"; fi
Gooooood
ricky@ricky-desktop:~$ a="string"
ricky@ricky-desktop:~$ if [ $a ]; then echo "Gooooood"; else echo "Baaaaaaad"; fi
Gooooood
ricky@ricky-desktop:~$ a=
ricky@ricky-desktop:~$ if [ $a ]; then echo "Gooooood"; else echo "Baaaaaaad"; fi
Baaaaaaad
ricky@ricky-desktop:~$ a=100
ricky@ricky-desktop:~$ if [ $a ]; then echo "Gooooood"; else echo "Baaaaaaad"; fi
Gooooood
ricky@ricky-desktop:~$ unset a
ricky@ricky-desktop:~$ if [ $a ]; then echo "Gooooood"; else echo "Baaaaaaad"; fi
Baaaaaaad
ricky@ricky-desktop:~$

 


多些几位大虾的指点,上面如果用string存在不存在来解释,那么下面怎么解释:

ricky@ricky-desktop:~$ a=1
ricky@ricky-desktop:~$ if [ ! $a ] ; then echo "Goooooood"; else echo "Baaaaaad"; fi
Baaaaaad
ricky@ricky-desktop:~$ a=0
ricky@ricky-desktop:~$ if [ ! $a ] ; then echo "Goooooood"; else echo "Baaaaaad"; fi
Baaaaaad
ricky@ricky-desktop:~$ a=true
ricky@ricky-desktop:~$ if [ ! $a ] ; then echo "Goooooood"; else echo "Baaaaaad"; fi
Baaaaaad
ricky@ricky-desktop:~$ a=false
ricky@ricky-desktop:~$ if [ ! $a ] ; then echo "Goooooood"; else echo "Baaaaaad"; fi
Baaaaaad
ricky@ricky-desktop:~$ 

Categories: 脚本语言 Tags:

Learning Vim

November 17th, 2009 ricky.zhu No comments

最新发现的Vim里面的一些不太常用,但是非常强大的功能,包括列选定,分屏等等,学习并记录之。

这个思维导图来自于Joe Marinez的blog

vim

PDF版本下载

摘录下来从中学到的几个命令:

Marking Text:
v = start visual mode
V = start linewise visual mode
ctrl-v = start blockwise visual mode
o = move to other end of marked area
U = upper case of marked area
O = move to Other corner of block

> = shift right
< = shift left
y = yank
d = delete
~ = switch case

Multi-File:
:sp fn = open a file in new buffer and split window
ctrl-w s = split window
ctrl-w w = switch windows
ctrl w – window commands
ctrl-w q = quit a window
ctrl-w v = split windows vertically

Tab Commands:
:tabe fn = edit file in new tab
gt = next tab
gT = previous tabs
:tabr = First tab
:tabl = Last tab
:tabm [N] = move current tab after tab N

Categories: 脚本语言 Tags: ,

Shell中读取文件

September 10th, 2009 ricky.zhu No comments
.!.
.!.

在Shell中读取文件,一次一行进行处理。用for处理起来比较简单,但是如果一行中有空格分隔的话,那么处理起来如果把一行作为一个参数,用for就不行了,变通一下的做法有很多中,比如用awk。昨天在处理这个问题花了一点时间研究了一下shell读取文件的方法,如下:

#!/bin/ksh

cat file1 | while read line
do
  line2=`echo $line | sed ’s/\*/\\\*/g’ `
# echo "$line2"
  grep "$line2" file2 > /dev/null
  a=$?
  grep "$line2" file3 >/dev/null
  b=$?
# echo "a=$a,b=$b"
  if [[ $a == 0 ]] && [[ $b == 0 ]]
  then
    echo $line
  fi
done
ricky@ricky-desktop:~$
 

这个脚本是用来实现如下的需求:
1)比较三个文件中相同的部分,输出
2)每个文件中的内容中有些特殊字符,在grep的时候需要进行特殊处理,用sed进行了替换
3)因为文件内容中有空格,所以读取的时候不能用传统的for x in a b c的方法

文件内容大致如下:

ricky@ricky-desktop:~$ more file1
line1 32*256 megabyte(s)
line2 32*256 megabyte(s)
line3 32*256 megabyte(s)
 

эротическое женское нижние белье фото

Categories: 脚本语言 Tags:

Linux/Solair对时间的处理

August 26th, 2009 ricky.zhu 1 comment

今天跟老铁在Solaris上调试一个小的脚本,最初的需求是希望用Shell来实现修改系统时间,随机提前N秒或者推迟N秒。这个在Linux中是非常容易做到的,因为Linux的date函数提供了一个–date选项,可以支持秒,分钟,小时,甚至天或者月的修改,用法如下。

1) 取之前的时间:
date -d “a day ago” 取出前1天的系统时间
date -d “2 days ago” 取出前2天的系统时间

2) 取之后的时间:
date -d “a day ” 取出1天后的系统时间
date -d “2 days” 取出2天后的系统时间

因为Linux下的date是基于GNU的,但是Solaris基于POSIX的时间函数并没有提供类似的功能,甚至连秒一下的单位都无法直接获得,而是要自己写函数获得微秒和毫秒。

那么如何利用Shell在Solaris中实现之前的需求呢。有这么几种方法来实现:

利用其它语言,比如Perl、Tcl或者python。还有就是利用时区,但是这个仅仅局限于小时的修改,精确到分钟或者秒就不行了:

1)取之前的时间:yesterday=`TZ=$TZ+3; date +%Y%m%d`; echo $yesterday 取出前1天的系统时间

yesterday=`TZ=$TZ+27; date +%Y%m%d`; echo $yesterday 取出前2天的系统时间

2)取之后的时间:yesterday=`TZ=$TZ-22; date +%Y%m%d`; echo $yesterday 取出后1天的系统时间

yesterday=`TZ=$TZ-46; date +%Y%m%d`; echo $yesterday 取出后1天的系统时间

这里需要几点:
1)不能在命令行中直接执行TZ=$TZ-100; date +%Y%m%d,这样会修改当前terminal中的TZ和系统时间。
2)TZ=$TZ+3 需要加减多少小时才是前/后一天,和系统的$TZ有关。以上运算是在$TZ=PRC下进行的。

在Perl中,可以取epoch时间,并加上一定的秒数然后再转回来:
#perl -e ‘print int(time)’

相对于Solaris,在Linux中,取epoch时间就容易的多,只需要一条Shell命令。

关于GNU,POSIX和epoch,可以参考维基百科


The Book of Eli hd
Categories: 脚本语言 Tags:

Using vi

July 8th, 2009 ricky.zhu 2 comments

General Startup

”’To use vi:”’ vi filename
”’To exit vi and save changes:”’ :wq
”’To exit vi and save changes:”’ :x
”’To exit vi without saving changes:”’ :q!
”’To enter vi command mode:”’ [esc]

Counts

A number preceding any vi command tells vi to repeat that command that many times.

Cursor Movement

h move left (backspace)
j move down
k move up
l move right (spacebar)
[return] move to the beginning of the next line
$ last column on the current line
0 move cursor to the first column on the current line
^ move cursor to first nonblank column on the current line
w move to the beginning of the next word or punctuation mark
W move past the next space
b move to the beginning of the previous word or punctuation mark
B move to the beginning of the previous word, ignores punctuation
e end of next word or punctuation mark
E end of next word, ignoring punctuation
H move cursor to the top of the screen
M move cursor to the middle of the screen
L move cursor to the bottom of the screen

Screen Movement

G move to the last line in the file
xG move to line x
z+ move current line to top of screen
z move current line to the middle of screen
z- move current line to the bottom of screen
^F move forward one screen
^B move backward one line
^D move forward one half screen
^U move backward one half screen
^R redraw screen (does not work with VT100 type terminals)
^L redraw screen (does not work with Televideo terminals)

Inserting

r replace character under cursor with next character typed
R keep replacing character until [esc] is hit
i insert before cursor
a append after cursor
A append at end of line
O open line above cursor and enter append mode

Deleting

x delete character under cursor
dd delete line under cursor
dw delete word under cursor
db delete word before cursor

Copying Code

yy (yank)’copies’ line which may then be put by the p(put) command. Precede with a count for multiple lines.

Put Command

brings back previous deletion or yank of lines, words, or characters

P bring back before cursor
p bring back after cursor

Find Commands

? finds a word going backwards
/ finds a word going forwards
f finds a character on the line under the cursor going forward
F finds a character on the line under the cursor going backwards
t find a character on the current line going forward and stop one character before it
T find a character on the current line going backward and stop one character before it
; repeat last f, F, t, T

Miscellaneous Commands

. repeat last command
u undoes last command issued
U undoes all commands on one line
xp deletes first character and inserts after second (swap)
J join current line with the next line
^G display current line number
% if at one parenthesis, will jump to its mate
mx mark current line with character x
‘x find line marked with character x

”’NOTE:”’ Marks are internal and not written to the file.

Line Editor Mode

Any commands form the line editor ”’ex”’ can be issued upon entering line mode.

To enter: type ‘:’
To exit: press [return] or [esc]

Substitution

:#,#s/old/new/g

# line number range
old pattern to replace
new pattern to insert
g optional key for global substitution (multiple occurences of old on the same line will not be replace without this)

Reading Files

copies (reads) filename after cursor in file currently editing

:r filename

Write File

:w saves the current file without quitting

Moving

:# move to line #
:$ move to last line of file

Shell Escape

executes ‘cmd’ as a shell command.

:!’cmd’
Categories: 脚本语言 Tags:

使用screen管理多个shell

July 8th, 2009 ricky.zhu 3 comments
.!.
.!.

screen可以说是网管必备,防止突然掉电或者远程操作连接突然中断造成的损失。
之前用过一阵子,转载一篇,仅供参考,原文地址

命令行是一种强大的工具,但是它有一个严重的缺点:如果 shell 停止了,您的工作也就停止了。要想让 shell 和您的工作保持活动状态(甚至是跨多个会话和中断的连接),可以使用 GNU Screen 作为控制台的窗口系统。

哈利波特有魔杖,雷神托尔有金色的巨锤,Buckethead 有斧子,但是这些武器与 QWERTY 键盘相比只是小儿科。只需在命令行上敲几下,就可以启动网站、招募军团或击败可怕的火龙。

但是,QWERTY 键盘有一个严重的缺点:它很容易发生连接中断。电话线上的噪音、无线连接中断或者网络超时都可能导致远程 shell 中断。如果您已经在某个工作(比如调试一个应用程序)上花费了好几小时,而一下子就丢失了所有成果,一定会很沮丧的。

不过这种灾难是可以避免的。使用 GNU Screen 就可以了。Screen 可以在一个控制台(即与主机物理连接的哑终端)、xterm 窗口或 Secure Shell (SSH) 登录 shell 中创建和管理多个 shell 窗口。可以从一个 shell 窗口迅速切换到另一个窗口,可以离开正在运行的 shell 并在任何时候重新连接。实际上,Screen 提供许多虚拟化的控制台。

图 1 到图 5 展示 Screen 的特性和操作。看一下 图 1, 这里假设用户已经使用 SSH 登录到一台远程主机。最初,在本地主机(比如您的笔记本或桌面计算机)上有一个本地 shell 和一个远程 shell。通常,使用远程 shell 在远程主机上运行命令;输出经过加密之后通过 SSH 连接发送到本地 shell。(在各张图中,用蓝色表示当前可见的 shell 输出)。但是,如果本地 shell 或远程 shell 或它们之间的连接中断了,远程 shell 就会终止,您的工作成果就会丢失。
图 1. 典型的 SSH 连接

典型的 SSH 连接

图 2 显示在远程主机上启动 Screen 之后的情况。Screen 实用程序启动,进而启动一个新的 shell 窗口 A,可以在此窗口中运行命令。A 的输出是可见的(由蓝色表示);这个 shell 的输出经过 Screen,再经过远程登录 shell,最后通过 SSH 到达本地登录 shell。
图 2. Screen 管理 shell 窗口
Screen 管理 shell 窗口

Screen 本身并不 “露面”;它是一个代理,其作用是在它管理的正在运行的可用窗口中做出选择。在任何时候,Screen 只能显示一个窗口的输出。可以把 Screen 看作虚拟的键盘-视频-鼠标 (KVM) 开关。

但是,也可以断开与 Screen 的连接,见 图 3。Screen 代理仍然存在,它控制的所有窗口也还存在,但是与 Screen 的连接临时切断了,这会让用户返回到远程登录 shell。
图 3. 可以断开与 Screen 的连接,而它管理的窗口仍然存在
可以断开与 Screen 的连接,而它管理的窗口仍然存在

图 4 显示一种可能出现的情况。已经重新建立了到 Screen 的连接,还创建了另外两个窗口(BC)。窗口 A 和 B 继续运行 shell 和所有附属作业,但是只有 C 的输出是可见的。当然,可以在窗口之间切换以监视各个作业的状态。
图 4. Screen 可以管理多个窗口
Screen 可以管理多个窗口

最后,图 5 显示中断与 Screen 的连接并终止远程登录 shell 的情况。Screen 和它的窗口仍然存在。可以重新登录并连接 Screen(并通过 Screen 代理连接到它的窗口),继续您的工作。
图 5. 可以随意中断与 Screen 的连接和重新连接
可以随意中断与 Screen 的连接和重新连接

如果您常常要访问远程服务器以执行维护或开发软件,那么 GNU Screen 是必不可少的工具。

构建并安装 Screen

Screen 的最初版本早在 10 多年前就发布了,所以您的系统上很可能有这个实用程序(通常名为 /usr/bin/screen)。但是,如果系统上没有 Screen,也很容易通过发行版的包管理器安装它。例如,如果您使用 Debian Linux® 的变体(比如 Ubuntu),那么可以用 apt-get 简便地安装 Screen:

  $ sudo apt-get install screen

另外,如果您喜欢从源代码构建软件,可以访问 GNU Screen 项目页面(见 参考资料 中的链接)并下载最新的代码包。在撰写本文时,Screen 的最新版本是 4.0.3,于 2008 年 8 月发布。下载并提取代码,进入生成的源代码目录,然后依次运行 ./configuremakesudo make install

$ wget http://ftp.gnu.org/gnu/screen/screen-4.0.3.tar.gz
$ tar xzf tar xzf screen-4.0.3.tar.gz
$ cd screen-4.0.3
$ ./configure
this is screen version 4.0.3
...
$ make
CPP="gcc -E " srcdir=. sh ./osdef.sh
...
$ sudo make install
...
You may also want to install ./etc/etcscreenrc in
	/usr/etc/screenrc.
$ sudo cp ./etc/etcscreenrc /usr/etc/screenrc

Screen 现在就安装好了,可以使用了。输入 man screen

art of war the dvd download

查看 Screen 的手册页。


回页首 jackal the dvd

开始使用 Screen

启动 Screen。在出现许可协议消息时,按 Return;现在应该会看到一个新的登录 shell 提示。(在下面的清单中,在每个 shell 提示前面人为地添加一个昵称,以此区分各个 shell 实例并与图 1 到图 5 联系起来) 。

Local $ ssh remote.example.com
Last login: Sun Dec 21 17:23:16 2008 from local.example.com
Remote $ hostname
remote.example.com
Remote $ screen
A $ top

现在处于 图 2 所示的状态。一个 Screen 会话正在运行(尽管看不到),窗口 A 处于活动状态,它正在运行系统监视器 top,每隔几秒刷新一次系统性能信息。

为了临时断开与 Screen 会话及其所有窗口的连接,按 Ctrl-a ,然后按 d(小写的字母 D,代表 “detach”)。Ctrl-a 是 Screen 命令的前缀,d 是用于断开连接的命令。现在处于 图 3 所示的状态。Screen 会话和窗口 A 仍然存在。可以通过运行 screen -list 确认这一点:

$ screen -list
There is a screen on:
21020.pts-2.remote  (Detached)
1 Socket in /tmp/screens/S-strike.

screen -list 命令显示所有可用的 Screen 会话。(顺便说一句,可以同时运行多个 Screen 会话,每个会话有一组同时运行的窗口。图 6 显示这种情况。每个 Screen 实例跟踪它自己的当前窗口。稍后会提供一些关于 Screen 的高级用法的提示)。编号为 21020 的会话已经断开连接并相应地加上了标志。因为只有一个 Screen 会话,所以可以直接用 screen -r 重新连接它。由于运行了 Screen,top 会一直运行。
图 6. Screen 提供多个会话,每个会话有多个窗口
Screen 提供多个会话,每个会话有多个窗口

为了进入 图 4 所示的状态,重新连接当前会话,然后按 Ctrl-a,然后按 c(小写的字母 C,代表 “create”)创建一个新窗口。接下来,按 Ctrl-a,然后再次按 c。现在,Screen 会话管理三个窗口。

Ctrl-a,然后在当前窗口中按双引号键("),就可以看到会话中可用窗口的列表:

Num  Name           Flags

 0   bash           $
 1   bash           $
 2   bash           $

在默认情况下,窗口的名称取自它启动的第一个命令(通常是一个 shell)。因此,在上面的列表中有三个 bash 窗口。可以用箭头键在窗口列表中上下移动;只需按回车即可选择窗口。

终止当前窗口的方法是,在窗口的 shell 提示上输入 exit,或者按键盘快捷键 Ctrl-a,然后按 k(小写的字母 K,代表 “kill”)。如果使用后一种方法,那么在窗口底部会出现一个警告,要求您确认要杀死此窗口。按 y(小写的字母 Y,代表 “yes”)确认,或按 n(小写的字母 N,代表 “no”)拒绝。如果杀死一个 Screen 会话中运行的所有窗口,屏幕命令就会输出一个消息并终止:

Remote $ screen
... Create and manipulate windows ...
... Exit from all windows...
[screen is terminating]
Remote $ screen -list
No Sockets found in /tmp/uscreens/S-supergiantrobot.

如果杀死了所有打开的窗口,那么 screen -list 命令输出 No sockets...,表示没有 Screen 会话可用。


回页首

更智能化的屏幕

到目前为止,已经看到了如何在一个 Screen 会话中创建多个窗口。只需这么做,就足以确保命令行工作不会丢失。

但是坦率地说,由于同时运行多个看起来相同的窗口,管理时可能引起混乱。最好能够轻松地区分窗口,而不需要逐一打开每个窗口。如果在断开连接和重新连接之间间隔的时间比较长的话,这种特性尤其有意义。

实际上,Screen 提供了许多选项和工具来帮助定制您的工作环境。可以给每个窗口指定名称,还可以在每个窗口的底部显示一个状态栏以帮助区分窗口。

指定窗口名称的方法是,激活窗口,按 Ctrl-a A(大写的字母 A,代表 “Annotate”),根据需要按 Backspace 删除现有的名称,然后在提示上输入一个有意义的名称:

Num  Name            Flags

 0   Window A       $
 1   Window B       $
 2   Window C       $

图 7 中,窗口的标题被设置为 Window A。窗口的昵称不必是惟一的。
图 7. 可以给每个窗口指定描述性的名称
可以给每个窗口指定描述性的名称

可以使用状态栏在视觉上进一步区分各个窗口。启动您喜欢的文本编辑器,在主目录中创建包含以下代码的 .screenrc 文件:

hardstatus on
hardstatus alwayslastline
hardstatus string "%{.bW}%-w%{.rW}%n %t%{-}%+w %=%{..G} %H %{..Y} %m/%d %C%a "

创建这样的 .screenrc 配置文件之后,每个新窗口就会显示状态栏,其中包含窗口的名称。图 8 显示一个包含状态栏的窗口。
图 8. 使用状态栏帮助识别每个窗口
使用状态栏帮助识别每个窗口


回页首

有帮助的 Screen 提示

Screen 的特性非常多,无法在这里一一介绍。下面给出一些提示并介绍几个比较有用的选项,帮助您更顺畅地使用 Screen:

  • 在任何窗口中输入 screen,不带任何参数,就会打开一个新窗口。Ctrl-a,然后按 c 与输入 screen 的作用相同,差异只是后一种方式通过提供命令行选项立即配置新会话。
  • 可以使用 screen -t name 命令在创建窗口时指定窗口名称。例如,要想创建一个新窗口并把它命名为 debugger,只需进入 Screen 管理的一个窗口,然后输入 screen -t debugger。如果打开窗口列表,其中一个窗口的标签应该是 debugger
  • 如果已经断开了与 Screen 会话的连接,可以用 screen -p ID 命令重新连接特定的窗口,其中的 ID 是一个数字或名称。我们来试一下:
    Local $ ssh remote.example.com
    Remote $ screen -t ghost
    Ghost $ screen -t new
    New $
    ... Press Control-a d to detach...
    Remote $ screen -r -p ghost
    Ghost $
  • 可以用 screen -L 命令把每个窗口的输出记录在日志中。每个窗口有自己的日志文件,文件名通常是 ~/screenlog.n,其中的 n 是窗口列表中显示的窗口编号。这个特性对于记录复杂的步骤(比如重新配置系统)非常有帮助。
  • 在 Screen 文档中记录了所有快捷键。最有用的组合键包括:按 Ctrl-a,然后按 0(数字零)到 9 立即切换到特定的窗口;按 Ctrl-a,然后按 C(大写的字母 C,代表 “Clear”)清除一个窗口的内容;按 Ctrl-a,然后按 H 启用或禁用日志记录;按 Ctrl-a,然后按 Ctrl-a 在当前窗口和前一个窗口之间来回切换;按 Ctrl-a,然后按 Ctrl-\(反斜杠)杀死所有窗口并终止当前的 Screen 会话。

回页首

Screen 的高级用法

正如前面提到的,可以创建多个同时运行的 Screen 会话,每个会话可以管理一系列窗口。每个会话有一个惟一标识符;使用 screen -list 列出可用的所有会话。与窗口一样,可以给会话指定名称以便引用它。使用 screen -S label 给新会话分配标签。

共享是 Screen 会话的最佳用法之一。如果允许,可以连接现有的会话并在此会话的任何窗口中与其他用户协作。甚至可以独立地切换到会话中的另一个窗口。如果您与其他用户在同一窗口中,输入或显示的任何内容都会反映给所有伙伴。我们来试一下:

  1. 选择一台工作计算机并登录。
  2. 输入 screen -S sharing -t one 创建一个名为 sharing 的 Screen 会话和一个名为 one 的新窗口。
  3. screen -t two 创建另一个窗口。
  4. 如果愿意,检查一下目前的状态:按 Ctrl-a,然后按 d,然后输入 screen -list screen -r sharing,然后按 Ctrl-a,然后按
  5. one 窗口中输入 echo,在此窗口中产生一些输出。
  6. 在同一台计算机上,打开第二个登录窗口。
  7. 在此窗口中,输入 screen -x -r sharing -p one-x 选项指定多用户模式;-p one 直接连接到 one 窗口。应该会马上看到与另一个登录会话相同的输出,见 图 9图 9. 可以共享会话
    可以共享会话

在每个登录窗口中,运行 UNIX® 命令产生输出,按 Screen 快捷键在共享的会话中的窗口之间切换并查看结果。


回页首

结束语

要想进一步掌握 Screen,可以研究一下多屏幕模式,学习如何用老式的锁防止对各个窗口的访问。

Screen 是一个很实用的工具,它对于在命令行上执行的任何工作都很有价值。实际上,一旦掌握了它,您就会觉得离不开它了。

Categories: 脚本语言 Tags: