1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
#!/usr/bin/env python
# psax.py; illustration of curses library
# runs the shell command ps ax and saves the last lines of its output,
# as many as the window will fit; allows the user to move up and down
# within the window, killing those processes
# run line:
#python psax.py
# user commands:
''''u':
move highlight up a line
'd':
move highlight down a line
'k':
kill process in currently highlighted line
'r':
re-run 'ps ax' for update
'q':
quit'''
# possible extensions: allowing scrolling, so that the user could go
# through all the ps ax output; allow wraparound for long lines; ask
# user to confirm before killing a process
import curses, os, sys, traceback
# global variables
class gb:
scrn = None # will point to Curses window object
cmdoutlines = [] # output of 'ps ax' (including the lines we don't
# use, for possible future extension)
winrow = None # current row position in screen
startrow = None # index of first row in cmdoutlines to be displayed
def runpsax():
p = os.popen('ps ax','r')
gb.cmdoutlines = []
row = 0
for ln in p:
# don't allow line wraparound, so truncate long lines
ln = ln[:curses.COLS]
# remove EOLN if it is still there
if ln[-1] == '\n': ln = ln[:-1]
gb.cmdoutlines.append(ln)
p.close()
# display last part of command output (as much as fits in screen)
def showlastpart():
# clear screen
gb.scrn.clear()
# prepare to paint the (last part of the) 'ps ax' output on the screen
gb.winrow = 0
ncmdlines = len(gb.cmdoutlines)
# two cases, depending on whether there is more output than screen rows
if ncmdlines <= curses.LINES:
gb.startrow = 0
nwinlines = ncmdlines
gb.startrow = ncmdlines - curses.LINES - 1
nwinlines = curses.LINES
lastrow = gb.startrow + nwinlines - 1
# now paint the rows
for ln in gb.cmdoutlines[gb.startrow:lastrow]:
gb.scrn.addstr(gb.winrow,0,ln)
gb.winrow += 1
# last line highlighted
gb.scrn.addstr(gb.winrow,0,gb.cmdoutlines[lastrow],curses.A_BOLD)
gb.scrn.refresh()
# move highlight up/down one line
def updown(inc):
tmp = gb.winrow + inc
# ignore attempts to go off the edge of the screen
if tmp >= 0 and tmp < curses.LINES:
# unhighlight the current line by rewriting it in default attributes
gb.scrn.addstr(gb.winrow,0,gb.cmdoutlines[gb.startrow+gb.winrow])
# highlight the previous/next line
gb.winrow = tmp
ln = gb.cmdoutlines[gb.startrow+gb.winrow]
gb.scrn.addstr(gb.winrow,0,ln,curses.A_BOLD)
gb.scrn.refresh()
# kill the highlighted process
def kill():
ln = gb.cmdoutlines[gb.startrow+gb.winrow]
pid = int(ln.split()[0])
os.kill(pid,9)
# run/re-run 'ps ax'
def rerun():
runpsax()
showlastpart()
def main():
# window setup
gb.scrn = curses.initscr()
curses.noecho()
curses.cbreak()
# rpdb.set_trace() (I used RPDB for debugging)
# run 'ps ax' and process the output
gb.psax = runpsax()
# display in the window
showlastpart()
# user command loop
while True:
# get user command
c = gb.scrn.getch()
c = chr(c)
if c == 'u': updown(-1)
elif c == 'd': updown(1)
elif c == 'r': rerun()
elif c == 'k': kill()
else: break
restorescreen()
def restorescreen():
curses.nocbreak()
curses.echo()
curses.endwin()
if __name__ =='__main__':
main()
restorescreen()
# print error message re exception
traceback.print_exc()
|