El Programa tee Reescrito en Groovy

Not all systems support the classic tee program for splitting output pipes to multiple destinations. This command sends the output from someprog to /tmp/output and to the mail pipe beyond.

% someprog | tee /tmp/output | Mail -s 'check this' user@host.org

This program helps not only users who aren't on Unix systems and don't have a regular tee. It also helps those who are, because it offers features not found on other version of tee.

The four flag arguments are -i to ignore interrupts, -a to append to output files, -u for unbuffered output, and -n to omit copying the output on to standard out.

Veamos algunas ejecuciones:

lhp@nereida:~/Lgroovy/files$ echo 'hola LHP' |./tctee.groovy file1 file2
hola LHP
lhp@nereida:~/Lgroovy/files$ cat file1 file2
hola LHP
hola LHP
lhp@nereida:~/Lgroovy/files$ echo 'hola mundo' |./tctee.groovy -n '| cat -n' file2
     1  hola mundo
lhp@nereida:~/Lgroovy/files$ cat file2
hola mundo
lhp@nereida:~/Lgroovy/files$ echo 'hola mundo' |./tctee.groovy -n '| cat -n' '>>file2'
     1  hola mundo
lhp@nereida:~/Lgroovy/files$ cat file2
hola mundo
hola mundo
generaciondecodigos@nereida:~/src/groovy/files$ cat -n tctee.groovy                                  
   1  #!/usr/bin/env groovy                                                                        
   2  class MultiStream {                                                                          
   3      private targets                                                                          
   4      private ignoreErrors                                                                     
   5      MultiStream(List targets, ignore) {                                                      
   6          this.targets = targets                                                               
   7          ignoreErrors = ignore                                                                
   8      }                                                                                        
   9      def println(String content) {                                                            
  10          targets.each{                                                                        
  11              try {                                                                            
  12                  it?.write(content.bytes)                                                     
  13              } catch (Exception ex) {                                                         
  14                  if (!ignoreErrors) throw ex                                                  
  15                  targets -= it                                                                
  16                  it?.close()                                                                  
  17              }                                                                                
  18          }                                                                                    
  19      }                                                                                        
  20      def close() { targets.each{ it?.close() } }                                              
  21  }                                                                                            
  22                                                                                               
  23  class TeeTarget {                                                                            
  24      private filename                                                                         
  25      private stream                                                                           
  26      private p                                                                                
  27                                                                                               
  28      TeeTarget(String name, append, buffered, ignore) {                                       
  29          if (name.startsWith('>>')) {                                                         
  30              createFileStream(name[2..-1],true,buffered,ignore)                               
  31          } else if (name.startsWith('|')) {                                                   
  32              createProcessReader(name[1..-1])                                                 
  33          } else {                                                                             
  34              createFileStream(name,append,buffered,ignore)                                    
  35          }                                                                                    
  36      }                                                                                        
  37                                                                                               
  38      TeeTarget(OutputStream stream) { this.stream = stream }                                  
  39                                                                                               
  40      def write(bytes) { stream?.write(bytes) }                                                
  41      def close() { stream?.close() }                                                          
  42                                                                                               
  43      private createFileStream(name, append, buffered, ignore) {                               
  44          filename = name                                                                      
  45          def fos                                                                              
  46          try {                                                                                
  47              fos = new FileOutputStream(name, append)                                         
  48          } catch (Exception ex) {                                                             
  49              if (ignore) return                                                               
  50          }                                                                                    
  51          if (!buffered) stream = fos                                                          
  52          else stream = new BufferedOutputStream(fos)                                          
  53      }                                                                                        
  54      private createWriter(os) {new PrintWriter(new BufferedOutputStream(os))}                 
  55      private createReader(is) {new BufferedReader(new InputStreamReader(is))}                 
  56      private createPiperThread(br, pw) {                                                      
  57          Thread.start{                                                                        
  58              def next                                                                         
  59              while((next = br.readLine())!=null) {                                            
  60                  pw.println(next)                                                             
  61              }                                                                                
  62              pw.flush(); pw.close()                                                           
  63          }                                                                                    
  64      }                                                                                        
  65      private createProcessReader(name) {                                                      
  66          def readFromStream = new PipedInputStream()                                          
  67          def r1 = createReader(readFromStream)
  68          stream = new BufferedOutputStream(new PipedOutputStream(readFromStream))
  69          p = Runtime.runtime.exec(name)
  70          def w1 = createWriter(p.outputStream)
  71          createPiperThread(r1, w1)
  72          def w2 = createWriter(System.out)
  73          def r2 = createReader(p.inputStream)
  74          createPiperThread(r2, w2)
  75      }
  76  }
  77
  78  targets = []
  79  append = false; ignore = false; includeStdout = true; buffer = true
  80  (0..<args.size()).each{
  81      arg = args[it]
  82      if (arg.startsWith('-')) {
  83          switch (arg) {
  84              case '-a': append = true; break
  85              case '-i': ignore = true; break
  86              case '-n': includeStdout = false; break
  87              case '-u': buffer = false; break
  88              default:
  89                  println "usage: tee [-ainu] [filenames] ..."
  90                  System.exit(1)
  91          }
  92      } else targets += arg
  93  }
  94  targets = targets.collect{ new TeeTarget(it, append, buffer, ignore) }
  95  if (includeStdout) targets += new TeeTarget(System.out)
  96  def tee = new MultiStream(targets, ignore)
  97  def stdin = new BufferedReader(new InputStreamReader(System.in))
  98
  99  stdin.eachLine { line ->
 100      tee.println("$line\n")
 101  }
 102  tee.close()

Casiano Rodríguez León
2010-04-30