Python:执行命令行指令

文章目录

  • 简介
  • os.system
  • os.popen
  • subprocess.Popen()
  • 参考文献

    简介

    在python中,调用外部命令行(linux中的shell、或者windows中的cmd)来执行指令,常用的有三种方式:

    • os.system(‘pwd’);
    • os.popen()
    • subprocess.Popen()

      os.system

      是os模块中最基础的部分,其他的方法一般是在该方法的基础上衍生来的。

      每一条os.system指令在执行时,都会创建一个子进程在系统上来执行命令,子进程的执行结果是无法影响主进程的;

      上述原理会导致当需要执行多条命令行的时候会得不到想要的结果,比如说:

      import os
      os.system('cd /home/xxx/')
      os.mkdir('1.txt')
      

      执行后会发现txt文件没有创建在/home/xxx/文件夹,而是创建在了本地,因为多条命令是独立的。

      如果为了保证system执行多条命令可以成功,那么多条命令需要在一个os.system中执行,但需要用特殊的分隔符将它们隔开,如:

      import os
      os.system('cd /home/xxx/ && mkdir 1.txt')
      os.system('cd /home/xxx/ ; mkdir 1.txt')
      
      • 分号表示,这些命令会顺序执行下去;

      • &&表示,顺序执行,遇到执行错误的命令停下;

      • ||表示,顺序执行,遇到执行成功的命令停止,后面不再执行;

        os.system还有一个缺点,就是它的返回值只有0(成功),1,2。他对pwd等指令是会打印出执行结果,但是这个结果是内部打印出的,不是以返回值的形式打印出的,我们根本拿不到。比如说:

        import os
        a=os.system('pwd')
        print(a)
        

        输出:

        /home/ttss
        0
        

        如果无法忍受这个问题,那么可以考虑使用os.popen()

        os.popen

        os.popen()是打开一个管道来执行命令,并将命令的执行结果写入一个文件对象作为返回值。

        调用格式:

        os.popen(command, mode, bufsize)
        

        command是待执行命令,后面两个命令可选;

        mode,指示模式权限,r或者w,默认是r;

        bufsize,指明了文件需要的缓冲大小。0表示无缓冲,1表示行缓冲;其他正值表示使用参数大小的缓冲,以字节为单位;负值表示使用系统的默认值。可以使用默认

        import os
        a=os.popen('pwd', 'r').readlines()
        print a
        # 输出:
        # ['/et/ttss\n']
        

        我们可以对返回的文件对象做继续操作。

        这里需要注意两个地方:

        • 关闭文件对象;
        • 设置阻塞

          返回的文件对象是需要关闭的,因此规范的写法是在with语句里使用os.popen

          with os.popen(command, 'r') as p:
              r = p.read()
          

          就不需要再手动的写p.close()

          而且上面那种方式还有好处,就是利用read()的特性阻塞住主进程,只有子进程跑完了之后才能继续向下执行。

          但是这也带来了一个坏处,主进程会被完全卡住,直到子进程跑完了之后,主进程才能read成功,接着往下执行。所以如果子进程永远也结束不了的话,那么主进程就会一直这么卡着。。。

          比如说ping 127.0.0.1,默认是一直在执行的,然后我们:

          import os
          with os.popen('ping 127.0.0.1', 'r') as p:
                  for i in p.readlines():
                          print(i)
          print('执行完成')
          

          执行后前台就会一直卡着,也没有输出,因为子进程没有结束,所以主进程也拿不到任何返回值。。。。

          除了这个地方比较坑之外,popen()还是比较好用的,自己注意下就行,千万不要执行无法退出的命令、或者需要进入交互模式的命令。

          subprocess.Popen()

          功能最丰富的一种方式,一般是推荐使用这个。

          但是仍然做不到子进程边执行边输出哈

          感觉相比os.popen(),新增的功能有限,就是可以跟子进程交互了而已,如果不过分追求这一点的话,那么感觉也可以选择用os.popen()

          subprocess新增的功能有:

          • wait():手动阻塞主进程,等待子进程返回后,再执行下面的代码;
            from subprocess import Popen, PIPE
            p = Popen('sleep 100', stdout=PIPE, shell=True)
            p.wait()
            print('End')
            
            • pid():返回子进程的PID;
              from subprocess import Popen, PIPE
              p = Popen('sleep 100', stdout=PIPE, shell=True)
              print(p.pid)
              
              • poll():检查子进程是否已经执行成功,没结束则返回None,结束则返回状态码。这个用处很大
                #-*- coding:utf-8 -*-
                import time
                from subprocess import Popen, PIPE
                p = Popen('sleep 100', stdout=PIPE, shell=True)
                while True:
                    if p.poll() == None:
                        print('程序执行中...')
                        time.sleep(1)
                    else:
                        print('程序执行完毕, 状态码为:%s' % p.poll())
                        break
                
                • returncode():返回命令执行完之后的状态码。但是returnCode并不是实时刷新的,并不是每次调用就刷新这样子,而是显式执行一次p.poll()之后才会做刷新。
                   
                  • kill():杀死子进程
                    from subprocess import Popen, PIPE
                    p = Popen('sleep 100', stdout=PIPE, shell=True)
                    print(p.pid)
                    p.kill()
                    
                    • terminal():终止子进程,跟kill()差不多
                      from subprocess import Popen, PIPE
                      p = Popen('sleep 100', stdout=PIPE, shell=True)
                      print(p.pid)
                      p.terminate()
                      
                      • communicate():用于跟子进程进行交互,返回一个元组,包含stdout、stderr;
                        from subprocess import Popen, PIPE
                        p = Popen('cat', stdin=PIPE, stdout=PIPE, shell=True)
                        print(p.communicate('hello world'))
                        
                        • stdout.readlines():读取标准输出,一次性读取所有内容
                          from subprocess import Popen, PIPE
                          p = Popen('ls /home', stdout=PIPE, shell=True)
                          for line in p.stdout.readlines():
                              print(line)
                          

                          参考文献

                          1. python基础之os.system函数
                          2. python中的os.popen()
                          3. python os.popen()方法 写的很棒
                          4. os.system()和os.popen()概述
                          5. subprocess.Popen() 常用方法 很棒
                          6. python–subprocess.Popen()多进程