【问题标题】:running a command line containing Pipes and displaying result to STDOUT运行包含管道的命令行并将结果显示到 STDOUT
【发布时间】:2011-11-13 06:16:23
【问题描述】:

如何从 Python 调用包含管道的 shell 命令并捕获输出?

假设命令类似于:

cat file.log | tail -1

Perl 相当于我正在尝试做的事情是这样的:

my $string = `cat file.log | tail -1`;

【问题讨论】:

    标签: python command-line pipe


    【解决方案1】:

    使用 subprocess.PIPE,如子流程文档部分 "Replacing shell pipeline" 中所述:

    import subprocess
    p1 = subprocess.Popen(["cat", "file.log"], stdout=subprocess.PIPE)
    p2 = subprocess.Popen(["tail", "-1"], stdin=p1.stdout, stdout=subprocess.PIPE)
    p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
    output,err = p2.communicate()
    

    或者,使用sh module,管道变成composition of functions

    import sh
    output = sh.tail(sh.cat('file.log'), '-1')
    

    【讨论】:

    • 哇。比 Perl 的方式简洁得多。
    【解决方案2】:
    import subprocess
    task = subprocess.Popen("cat file.log | tail -1", shell=True, stdout=subprocess.PIPE)
    data = task.stdout.read()
    assert task.wait() == 0
    

    请注意,这不会捕获标准错误。如果你还想捕获标准错误,你需要使用task.communicate();如果 stderr 的缓冲区已满,则调用 task.stdout.read() 然后 task.stderr.read() 可能会死锁。如果您希望将它们结合起来,您应该能够使用2>&1 作为 shell 命令的一部分。

    但考虑到你的具体情况,

    task = subprocess.Popen(['tail', '-1', 'file.log'], stdout=subprocess.PIPE)
    data = task.stdout.read()
    assert task.wait() == 0
    

    完全不需要管道。

    【讨论】:

      【解决方案3】:

      这个:

      import subprocess
      p = subprocess.Popen("cat file.log | tail -1", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
      #for shell=False use absolute paths
      p_stdout = p.stdout.read()
      p_stderr = p.stderr.read()
      print p_stdout
      

      或者这应该工作:

      import os
      result = os.system("cat file.log | tail -1")
      

      【讨论】:

      • 我可以得到第二个解决方案。尝试使用 Python 2.6.7 的第一个解决方案时,我在 Popen 行上出现错误,并且打印的错误是“OSError: [Errno 2] No such file or directory”。不清楚为什么会这样。
      • 尝试使用file.log的绝对路径。或者,试试shell=True
      • shell=False 应该是shell=True
      • 设置 shell=True 修复了它。谢谢!
      • 我需要写一些类似 subprocess.Popen("ps aux | grep php | wc -l", shell=True, stdout=subprocess.PIPE) 的东西来解决我的问题。谢谢
      【解决方案4】:

      另一种类似于 Popen 的方式是:

      command=r"""cat file.log | tail -1 """
      output=subprocess.check_output(command, shell=True)
      

      【讨论】:

        【解决方案5】:

        这是 @chown 的一个分支,有一些改进:

        • import subprocess 的别名,便于设置参数
        • 如果只需要输出,调用Popen时不需要设置stderrstdin
        • 为了更好的格式,建议对输出进行解码
        • shell=True 是必需的,以便为命令行调用解释器

        #!/usr/bin/python3
        
        import subprocess as sp
        
        p = sp.Popen("cat app.log | grep guido", shell=True, stdout=sp.PIPE)
        
        output = p.stdout.read()
        print(output.decode('utf-8'))
        

        $ cat app.log 
        2017-10-14 22:34:12, User Removed [albert.wesker]
        2017-10-26 18:14:02, User Removed [alexei.ivanovich] 
        2017-10-28 12:14:56, User Created [ivan.leon]
        2017-11-14 09:22:07, User Created [guido.rossum]
        
        $ python3 subproc.py 
        2017-11-14 09:22:07, User Created [guido.rossum]
        

        【讨论】:

          【解决方案6】:

          多管道运行shell命令的简单函数

          使用

          res, err = eval_shell_cmd('pacman -Qii | grep MODIFIED | grep -v UN | cut -f 2')
          

          功能

          import subprocess
          
          
          def eval_shell_cmd(command, debug=False):
              """
              Eval shell command with pipes and return result
              :param command: Shell command
              :param debug: Debug flag
              :return: Result string
              """
              processes = command.split(' | ')
          
              if debug:
                  print('Processes:', processes)
          
              for index, value in enumerate(processes):
                  args = value.split(' ')
          
                  if debug:
                      print(index, args)
          
                  if index == 0:
                      p = subprocess.Popen(args, stdout=subprocess.PIPE)
                  else:
                      p = subprocess.Popen(args, stdin=p.stdout, stdout=subprocess.PIPE)
          
                  if index == len(processes) - 1:
                      result, error = p.communicate()
                      return result.decode('utf-8'), error
          

          【讨论】:

            猜你喜欢
            • 2017-04-23
            • 2012-06-26
            • 2017-01-14
            • 2019-10-11
            • 2016-03-20
            • 2021-12-06
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多