PageRenderTime 23ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/scripts/ps

https://github.com/myw/cope
Perl | 178 lines | 114 code | 43 blank | 21 comment | 12 complexity | 78310777cfe0166f12c53f22be4c8dde MD5 | raw file
  1. #!/usr/bin/perl
  2. use App::Cope qw[run real_path colourise mark line];
  3. use App::Cope::Extra qw[user nonzero];
  4. use Term::ANSIColor;
  5. # The diversity of ps's available output formats is useful, but makes
  6. # it annoyingly-difficult to parse. Instead of treating each line with
  7. # a pre-defined set of columns, parse the header line and format it
  8. # based on that.
  9. # As the header line must be explicitly turned off, rather than on,
  10. # it's a lot safer for this command rather than any of the other ones.
  11. my $me = (getpwuid( $< ))[0] || "nobody";
  12. # Process state codes ('STAT' column)
  13. my %codes = (
  14. 'D' => 'blue bold', # sleeping on IO
  15. 'R' => 'green bold', # running
  16. 'S' => 'cyan bold', # sleeping on event
  17. 'T' => 'red bold', # stopped
  18. 'W' => 'magenta bold', # paging
  19. 'X' => 'white on_red', # dead
  20. 'Z' => 'white', # zombie
  21. '<' => 'red', # not nice
  22. 'N' => 'green', # nice
  23. 'L' => 'magenta', # locked pages into memory
  24. 's' => 'cyan', # session leader
  25. 'l' => 'yellow', # multi-threaded
  26. '+' => 'blue', # foreground process
  27. );
  28. # Process flags
  29. my %flags = (
  30. 1 => 'yellow', # forked but didn't exec
  31. 4 => 'red', # used super-user privileges
  32. );
  33. # pending/blocked/ignored/caught signal mask
  34. sub sigmask {
  35. line qr{([0-9a-f])} => { 0 => 'black bold' };
  36. }
  37. # colour anything memory-related in purple
  38. sub memory {
  39. mark qr{\S+} => \&{ nonzero 'magenta' };
  40. }
  41. # and anything cpu-related in blue
  42. sub cpu {
  43. mark qr{\S+} => \&{ nonzero 'blue' };
  44. }
  45. sub nice {
  46. mark qr{\S+} => sub {
  47. given (shift) {
  48. when ( $_ > 10 ) { return 'green bold' }
  49. when ( $_ > 0 ) { return 'green' }
  50. when ( $_ < 0 ) { return 'red' }
  51. when ( $_ < -10 ) { return 'red bold' }
  52. }
  53. };
  54. }
  55. # The fields themselves
  56. my %fields = (
  57. UID => sub {
  58. mark qr{\w+} => sub { my $uid = shift; ( $uid eq $me || $uid eq $< ) ? 'yellow bold' : 'yellow' }
  59. },
  60. USER => sub {
  61. mark qr{\w+} => \&{ user 'yellow' };
  62. },
  63. PID => sub { mark qr{\d+} => sub { (shift) == $$ ? 'cyan' : 'cyan bold' } },
  64. PGID => sub { mark qr{\d+} => 'cyan' },
  65. PPID => sub { mark qr{\d+} => 'cyan' },
  66. SID => sub { mark qr{\d+} => 'cyan' },
  67. '%CPU' => \&cpu,
  68. C => \&cpu,
  69. '%MEM' => \&memory,
  70. VSZ => \&memory,
  71. RSS => \&memory,
  72. SZ => \&memory,
  73. NI => \&nice,
  74. TTY => sub {
  75. line qr{(vc)(/\d+)\b} => 'blue', 'blue bold';
  76. line qr{(pts)(/\d+)\b} => 'green', 'green bold';
  77. line qr{(ttys?|p|s)(\d+)\b} => 'magenta', 'magenta bold';
  78. line qr{(system boot)\b} => 'red';
  79. line qr{(run-level \d)\b} => 'red';
  80. },
  81. STIME => sub { mark qr{\S+} => 'blue' },
  82. START => sub { mark qr{\S+} => 'blue' },
  83. TIME => sub {
  84. line qr{(\d+:?)} => sub { ( shift !~ /^0[0:]/ ) ? 'blue bold' : 'blue' };
  85. },
  86. STAT => sub { line qr{(\S)} => \%codes },
  87. PENDING => \&sigmask,
  88. BLOCKED => \&sigmask,
  89. IGNORED => \&sigmask,
  90. CAUGHT => \&sigmask,
  91. S => sub { mark qr{\S} => \%codes },
  92. F => sub { mark qr{\S} => \%flags },
  93. );
  94. # Used throughout multiple iterations
  95. my @headers;
  96. sub process {
  97. # Skip blabber about bad syntax
  98. return if /^(?:Warning|ERROR):/;
  99. # Parse the headings
  100. if (/^\s*%?[A-Z]+/) {
  101. @headers = ();
  102. push @headers, $1 while m/(\S+)/g;
  103. line qr{(\w+%?)} => 'underline';
  104. }
  105. # Use the headings
  106. else {
  107. my $orig = $_;
  108. my $string = "";
  109. my $i = 0;
  110. while ( $orig =~ m/(\s*)(\S+)/gc ) {
  111. my ( $spaces, $word ) = ( $1, $2 );
  112. my $header = $headers[$i++] || 'CMD';
  113. # Instead of colourising the line as a whole, split it into bits
  114. # and colourise each of them in turn.
  115. if ( $word eq '-' ) {
  116. $string .= $spaces . colored( $word => 'black bold' );
  117. next;
  118. }
  119. elsif ( $header eq 'COMMAND' or $header eq 'CMD' or $header eq 'STARTED' ) {
  120. my $cmd = substr $orig, $-[2];
  121. $cmd =~ s{([^\\\s\|_]\S*)}{colored( $1 => 'bold' )}e; # command names
  122. $cmd =~ s{(\||\\_)}{colored( $1 => 'green' )}ge; # forest branches
  123. $string .= $spaces . $cmd;
  124. # skip the rest of the headers - cmds can go on forever
  125. last;
  126. }
  127. else {
  128. $string .= $spaces . colourise( $fields{$header} || sub { }, $word );
  129. }
  130. }
  131. # After all matches have been found, reset $_ to the modified string
  132. $_ = $string;
  133. }
  134. }
  135. run( \&process, real_path, @ARGV );