Menjalankan perintah shell dan menangkap output


909

Saya ingin menulis fungsi yang akan menjalankan perintah shell dan mengembalikan hasilnya sebagai string , tidak peduli, apakah itu pesan kesalahan atau sukses. Saya hanya ingin mendapatkan hasil yang sama dengan yang saya dapatkan dengan baris perintah.

Apa yang akan menjadi contoh kode yang akan melakukan hal seperti itu?

Sebagai contoh:

def run_command(cmd):
    # ??????

print run_command('mysqladmin create test -uroot -pmysqladmin12')
# Should output something like:
# mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'test'; database exists'

Jawaban:


1139

Jawaban untuk pertanyaan ini tergantung pada versi Python yang Anda gunakan. Pendekatan paling sederhana adalah dengan menggunakan subprocess.check_outputfungsi:

>>> subprocess.check_output(['ls', '-l'])
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

check_outputmenjalankan satu program yang hanya menggunakan argumen sebagai input. 1 Ini mengembalikan hasil persis seperti yang dicetak stdout. Jika Anda perlu menulis input stdin, lanjutkan ke bagian runatau Popen. Jika Anda ingin menjalankan perintah shell yang kompleks, lihat catatan shell=Truedi akhir jawaban ini.

The check_outputfungsi bekerja pada hampir semua versi Python masih digunakan secara luas (2.7+). 2 Tetapi untuk versi yang lebih baru, ini bukan lagi pendekatan yang direkomendasikan.

Versi modern Python (3.5 atau lebih tinggi): run

Jika Anda menggunakan Python 3.5 atau lebih tinggi, dan tidak perlu kompatibilitas ke belakang , fungsi barurun disarankan. Ini menyediakan API tingkat tinggi yang sangat umum untuk subprocessmodul. Untuk menangkap hasil dari suatu program, berikan subprocess.PIPEtanda pada stdoutargumen kata kunci. Kemudian akses stdoutatribut dari CompletedProcessobjek yang dikembalikan :

>>> import subprocess
>>> result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE)
>>> result.stdout
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

Nilai kembali adalah bytesobjek, jadi jika Anda menginginkan string yang tepat, Anda harus melakukannya decode. Dengan asumsi proses yang dipanggil mengembalikan string yang dikodekan UTF-8:

>>> result.stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

Ini semua dapat dikompresi menjadi satu-liner:

>>> subprocess.run(['ls', '-l'], stdout=subprocess.PIPE).stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

Jika Anda ingin meneruskan input ke proses stdin, berikan bytesobjek ke inputargumen kata kunci:

>>> cmd = ['awk', 'length($0) > 5']
>>> input = 'foo\nfoofoo\n'.encode('utf-8')
>>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=input)
>>> result.stdout.decode('utf-8')
'foofoo\n'

Anda dapat menangkap kesalahan dengan meneruskan stderr=subprocess.PIPE(capture to result.stderr) atau stderr=subprocess.STDOUT(capture to result.stdoutbersama dengan output reguler). Ketika keamanan tidak menjadi masalah, Anda juga dapat menjalankan perintah shell yang lebih kompleks dengan melewati shell=Trueseperti yang dijelaskan dalam catatan di bawah ini.

Ini menambah sedikit kompleksitas, dibandingkan dengan cara lama dalam melakukan sesuatu. Tetapi saya pikir ini sepadan dengan hasilnya: sekarang Anda dapat melakukan hampir semua hal yang perlu Anda lakukan dengan runfungsi itu saja.

Versi Python yang lebih lama (2.7-3.4): check_output

Jika Anda menggunakan versi Python yang lebih lama, atau membutuhkan kompatibilitas mundur sederhana, Anda mungkin dapat menggunakan check_outputfungsi seperti dijelaskan secara singkat di atas. Ini telah tersedia sejak Python 2.7.

subprocess.check_output(*popenargs, **kwargs)  

Dibutuhkan argumen yang sama seperti Popen(lihat di bawah), dan mengembalikan string yang berisi keluaran program. Awal dari jawaban ini memiliki contoh penggunaan yang lebih rinci. Dalam Python 3.5 dan lebih tinggi, check_outputsetara dengan mengeksekusirun dengan check=Truedan stdout=PIPE, dan mengembalikan stdoutatribut saja.

Anda dapat melewati stderr=subprocess.STDOUTuntuk memastikan bahwa pesan kesalahan termasuk dalam output kembali - tetapi dalam beberapa versi Python lewat stderr=subprocess.PIPEuntuk check_outputdapat menyebabkan deadlock . Ketika keamanan tidak menjadi masalah, Anda juga dapat menjalankan perintah shell yang lebih kompleks dengan melewati shell=Trueseperti yang dijelaskan dalam catatan di bawah ini.

Jika Anda perlu pipa dari stderr mengirim atau mengirim input ke proses, check_outputtidak akan sesuai dengan tugas. Lihat Popencontoh di bawah ini dalam kasus itu.

Aplikasi kompleks & versi lama Python (2.6 dan di bawah): Popen

Jika Anda memerlukan kompatibilitas mundur yang dalam, atau jika Anda membutuhkan fungsionalitas yang lebih canggih daripada yang check_outputdisediakan, Anda harus bekerja secara langsung dengannyaPopen objek, yang merangkum API tingkat rendah untuk subproses.

The Popenkonstruktor menerima baik perintah tunggal tanpa argumen, atau daftar yang berisi perintah sebagai pos pertama, diikuti oleh sejumlah argumen, masing-masing sebagai bagian yang terpisah dalam daftar. shlex.splitdapat membantu mem-parsing string ke dalam daftar yang diformat dengan tepat. Popenbenda juga menerima a sejumlah argumen yang berbeda untuk manajemen IO proses dan konfigurasi tingkat rendah.

Untuk mengirim input dan menangkap output, communicatehampir selalu metode yang disukai. Seperti dalam:

output = subprocess.Popen(["mycmd", "myarg"], 
                          stdout=subprocess.PIPE).communicate()[0]

Atau

>>> import subprocess
>>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE, 
...                                    stderr=subprocess.PIPE)
>>> out, err = p.communicate()
>>> print out
.
..
foo

Jika Anda mengatur stdin=PIPE, communicatejuga memungkinkan Anda untuk mengirimkan data ke proses melalui stdin:

>>> cmd = ['awk', 'length($0) > 5']
>>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
...                           stderr=subprocess.PIPE,
...                           stdin=subprocess.PIPE)
>>> out, err = p.communicate('foo\nfoofoo\n')
>>> print out
foofoo

Catatan Aaron Hall jawaban , yang menunjukkan bahwa pada beberapa sistem, Anda mungkin perlu set stdout, stderrdan stdinsemua untuk PIPE(atau DEVNULL) untuk mendapatkancommunicate pekerjaan sama sekali.

Dalam beberapa kasus yang jarang terjadi, Anda mungkin perlu menangkap keluaran real-time yang kompleks. Jawaban Vartec menunjukkan jalan ke depan, tetapi metode selaincommunicate rentan terhadap kebuntuan jika tidak digunakan dengan hati-hati.

Seperti semua fungsi di atas, ketika keamanan tidak menjadi perhatian, Anda dapat menjalankan perintah shell yang lebih kompleks dengan melewati shell=True .

Catatan

1. Menjalankan perintah shell: the shell=True argumen

Biasanya, setiap panggilan ke run, check_output, atau Popenkonstruktor mengeksekusi program tunggal . Itu berarti tidak ada pipa bergaya bash mewah. Jika Anda ingin menjalankan perintah shell yang kompleks, Anda bisa meneruskannyashell=True , yang didukung oleh ketiga fungsi.

Namun, hal itu menimbulkan masalah keamanan . Jika Anda melakukan sesuatu lebih dari skrip ringan, Anda mungkin lebih baik memanggil setiap proses secara terpisah, dan meneruskan output dari masing-masing sebagai input ke yang berikutnya, melalui

run(cmd, [stdout=etc...], input=other_output)

Atau

Popen(cmd, [stdout=etc...]).communicate(other_output)

Godaan untuk menghubungkan langsung pipa kuat; menolaknya. Kalau tidak, Anda mungkin akan melihat kebuntuan atau harus melakukan hal-hal seperti ini .

2. Pertimbangan Unicode

check_outputmengembalikan sebuah string dengan Python 2, tetapi sebuah bytesobjek dengan Python 3. Perlu mengambil waktu sejenak untuk belajar tentang unicode jika Anda belum melakukannya.


5
Baik dengan check_output()dan communicate()Anda harus menunggu sampai proses selesai, dengan poll()Anda mendapatkan output yang datang. Sangat tergantung apa yang Anda butuhkan.
vartec

2
Tidak yakin apakah ini hanya berlaku untuk versi Python yang lebih baru, tetapi variabelnya outadalah tipe <class 'bytes'>untuk saya. Untuk mendapatkan output sebagai string, saya harus mendekodekannya sebelum mencetak seperti ini:out.decode("utf-8")
PolyMesh

1
@par Itu tidak bekerja untuk Anda ketika Anda lulus shell=True? Ini bekerja untuk saya. Anda tidak perlu shlex.splitketika lulus shell=True. shlex.splitadalah untuk perintah non-shell. Saya pikir saya akan mengambil sedikit karena ini mengeruhkan air.
pengirim

2
Python 3.5+ memungkinkan argumen kata kunci universal_newlines=Trueyang memungkinkan Anda untuk masuk dan keluar string Unicode dalam pengkodean default sistem. Dalam 3.7 ini diganti namanya menjadi lebih masuk akal text=True.
tripleee

2
Untuk Python 3.6+ Anda bisa menggunakan encodingparameter subprocess.runalih-alih menggunakan result.stdout.decode('utf-8'), Anda bisa menggunakan subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, encoding='utf-8').
Pierre

191

Ini jauh lebih mudah, tetapi hanya berfungsi di Unix (termasuk Cygwin) dan Python2.7.

import commands
print commands.getstatusoutput('wc -l file')

Ini mengembalikan tuple dengan (return_value, output).

Untuk solusi yang berfungsi di Python2 dan Python3, gunakan subprocessmodul sebagai gantinya:

from subprocess import Popen, PIPE
output = Popen(["date"],stdout=PIPE)
response = output.communicate()
print response


22
Perhatikan bahwa ini khusus Unix. Misalnya akan gagal pada Windows.
Zitrax

4
+1 Saya harus mengerjakan versi kuno python 2.4 dan ini SANGAT membantu
javadba

1
Apa itu PIPE dude datang menunjukkan kode lengkap: subprocess.PIPE
Kyle Bridenstine

@KyleBridenstine Anda dapat mengedit jawaban.
Boris

106

Sesuatu seperti itu:

def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while(True):
        # returns None while subprocess is running
        retcode = p.poll() 
        line = p.stdout.readline()
        yield line
        if retcode is not None:
            break

Catatan, bahwa saya mengarahkan stderr ke stdout, mungkin tidak persis seperti yang Anda inginkan, tetapi saya juga ingin pesan kesalahan.

Fungsi ini menghasilkan baris demi baris saat mereka datang (biasanya Anda harus menunggu sampai selesai untuk mendapatkan output secara keseluruhan).

Untuk kasus Anda, penggunaannya adalah:

for line in runProcess('mysqladmin create test -uroot -pmysqladmin12'.split()):
    print line,

Pastikan untuk menerapkan semacam loop aktif untuk mendapatkan output untuk menghindari potensi kebuntuan waitdan callfungsi.
André Caron

@Silver Light: proses Anda mungkin sedang menunggu input dari pengguna. Coba berikan PIPEnilai stdindan tutup file itu segera setelah Popenkembali.
André Caron

4
-1: ini adalah infinite loop if retcodeis 0. Cek seharusnya if retcode is not None. Anda tidak harus menghasilkan string kosong (bahkan baris kosong setidaknya satu simbol '\ n'): if line: yield line. Panggil p.stdout.close()di akhir.
jfs

2
Saya mencoba kode dengan ls -l / dirname dan rusak setelah daftar dua file sementara ada lebih banyak file dalam direktori
Vasilis

3
@fuenfundachtzig: .readlines()tidak akan kembali sampai semua output dibaca dan karena itu rusak untuk output besar yang tidak sesuai dengan memori. Juga untuk menghindari data buffered yang hilang setelah subproses keluar harus ada analogif retcode is not None: yield from p.stdout.readlines(); break
jfs

67

Jawaban Vartec tidak membaca semua baris, jadi saya membuat versi yang melakukannya:

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

Penggunaannya sama dengan jawaban yang diterima:

command = 'mysqladmin create test -uroot -pmysqladmin12'.split()
for line in run_command(command):
    print(line)

6
Anda bisa menggunakan return iter(p.stdout.readline, b'')alih-alih loop sementara
jfs

2
Itu adalah penggunaan iter yang cukup keren, tidak tahu itu! Saya memperbarui kode.
Max Ekman

Saya cukup yakin stdout menyimpan semua output, itu adalah objek stream dengan buffer. Saya menggunakan teknik yang sangat mirip untuk menghabiskan semua output yang tersisa setelah Popen selesai, dan dalam kasus saya, menggunakan polling () dan readline selama eksekusi untuk menangkap keluaran langsung juga.
Max Ekman

Saya telah menghapus komentar menyesatkan saya. Saya dapat mengkonfirmasi, p.stdout.readline()dapat mengembalikan output buffered yang tidak kosong sebelumnya bahkan jika proses anak sudah keluar ( p.poll()tidak None).
jfs

Kode ini tidak berfungsi. Lihat di sini stackoverflow.com/questions/24340877/…
thang

61

Ini adalah solusi rumit namun super sederhana yang bekerja dalam banyak situasi:

import os
os.system('sample_cmd > tmp')
print open('tmp', 'r').read()

File sementara (di sini adalah tmp) dibuat dengan output dari perintah dan Anda dapat membaca darinya output yang Anda inginkan.

Catatan tambahan dari komentar: Anda dapat menghapus file tmp dalam hal pekerjaan satu kali. Jika Anda perlu melakukan ini beberapa kali, tidak perlu menghapus tmp.

os.remove('tmp')

5
Hacky tapi super sederhana + berfungsi di mana saja .. saya bisa menggabungkannya dengan mktempmembuatnya berfungsi dalam situasi berulir yang saya kira
Prakash Rajagaopal

2
Mungkin metode tercepat, tetapi lebih baik tambahkan os.remove('tmp')untuk membuatnya "fileless".
XuMuK

@XuMuK Anda benar dalam hal pekerjaan satu kali. Jika itu adalah pekerjaan yang berulang-ulang mungkin menghapus tidak perlu
Mehdi Saman Booy

1
buruk untuk konkurensi, buruk untuk fungsi reentrant, buruk karena tidak meninggalkan sistem seperti sebelum dimulai (tidak ada pembersihan)
2mia

1
@ 2mia Jelas itu mudah karena suatu alasan! Jika Anda ingin menggunakan file sebagai semacam memori bersama untuk membaca dan menulis secara bersamaan, ini bukan pilihan yang baik. Tapi, untuk s.th. seperti memiliki keluaran dari perintah (mis. ls atau find atau ...) itu bisa menjadi pilihan yang baik dan cepat. Btw jika Anda membutuhkan solusi cepat untuk masalah sederhana itu yang terbaik menurut saya. Jika Anda membutuhkan saluran pipa, subproses bekerja untuk Anda lebih efisien.
Mehdi Saman Booy

44

Saya memiliki masalah yang sama tetapi menemukan cara yang sangat sederhana untuk melakukan ini:

import subprocess
output = subprocess.getoutput("ls -l")
print(output)

Semoga ini bisa membantu

Catatan: Solusi ini spesifik untuk Python3 karena subprocess.getoutput()tidak berfungsi di Python2


Bagaimana cara mengatasi masalah OP? Tolong jelaskan.
RamenChef

4
Ia mengembalikan output dari perintah sebagai string, sesederhana itu
azhar22k

1
Tentu saja, cetak adalah pernyataan pada Python 2. Anda harus bisa mengetahui ini adalah jawaban Python 3.

2
@Dev print (s) valid python 2. subprocess.getoutput tidak.
user48956

2
Untuk sebagian besar kasus penggunaan, inilah yang mungkin diinginkan orang: mudah diingat, tidak perlu menguraikan hasil, dll. Terima kasih.
bwv549

18

Anda dapat menggunakan perintah berikut untuk menjalankan perintah shell. Saya telah menggunakannya di ubuntu.

import os
os.popen('your command here').read()

Catatan: Ini sudah tidak digunakan lagi sejak python 2.6. Sekarang Anda harus menggunakan subprocess.Popen. Di bawah ini adalah contohnya

import subprocess

p = subprocess.Popen("Your command", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")

2
Tidak digunakan lagi sejak versi 2.6 - docs.python.org/2/library/os.html#os.popen
Filippo Vitale

1
@FilippoVitale Terima kasih. Saya tidak tahu bahwa itu sudah usang.
Muhammad Hassan

1
Menurut raspberrypi.stackexchange.com/questions/71547/... os.popen() tidak digunakan lagi dalam Python 2.6, tetapi ini tidak ditinggalkan dalam Python 3.x, karena dalam 3.x itu diimplementasikan menggunakan subprocess.Popen().
JL

12

Mileage Anda Mungkin Bervariasi, saya mencoba memutar @ senderle pada solusi Vartec di Windows pada Python 2.6.5, tetapi saya mendapatkan kesalahan, dan tidak ada solusi lain yang berfungsi. Kesalahan saya adalah:WindowsError: [Error 6] The handle is invalid .

Saya menemukan bahwa saya harus menetapkan PIPE ke setiap pegangan untuk mendapatkannya mengembalikan output yang saya harapkan - berikut ini bekerja untuk saya.

import subprocess

def run_command(cmd):
    """given shell command, returns communication tuple of stdout and stderr"""
    return subprocess.Popen(cmd, 
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE).communicate()

dan panggil seperti ini, ( [0]dapatkan elemen pertama dari tuple, stdout):

run_command('tracert 11.1.0.1')[0]

Setelah belajar lebih banyak, saya yakin saya perlu argumen pipa ini karena saya sedang mengerjakan sistem kustom yang menggunakan pegangan berbeda, jadi saya harus langsung mengendalikan semua std's.

Untuk menghentikan popup konsol (dengan Windows), lakukan ini:

def run_command(cmd):
    """given shell command, returns communication tuple of stdout and stderr"""
    # instantiate a startupinfo obj:
    startupinfo = subprocess.STARTUPINFO()
    # set the use show window flag, might make conditional on being in Windows:
    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    # pass as the startupinfo keyword argument:
    return subprocess.Popen(cmd,
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE, 
                            startupinfo=startupinfo).communicate()

run_command('tracert 11.1.0.1')

1
Menarik - ini pasti Windows. Saya akan menambahkan catatan yang menunjuk ke ini jika orang-orang mendapatkan kesalahan yang sama.
pengirim

gunakan DEVNULLalih - alihsubprocess.PIPE jika Anda tidak menulis / membaca dari pipa jika tidak, Anda dapat menggantung proses anak.
jfs

10

Saya memiliki rasa yang sedikit berbeda dari masalah yang sama dengan persyaratan berikut:

  1. Abadikan dan kembalikan pesan STDOUT saat terakumulasi dalam buffer STDOUT (yaitu dalam waktu nyata).
    • @vartec menyelesaikan masalah ini dengan menggunakan generator dan
      kata kunci 'hasil' di atas
  2. Cetak semua baris STDOUT ( bahkan jika proses keluar sebelum buffer STDOUT dapat sepenuhnya dibaca )
  3. Jangan sia-siakan siklus CPU untuk polling proses pada frekuensi tinggi
  4. Periksa kode pengembalian subproses
  5. Cetak STDERR (terpisah dari STDOUT) jika kita mendapatkan kode pengembalian kesalahan yang tidak nol.

Saya telah menggabungkan dan mengubah jawaban sebelumnya untuk menghasilkan yang berikut:

import subprocess
from time import sleep

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE,
                         shell=True)
    # Read stdout from subprocess until the buffer is empty !
    for line in iter(p.stdout.readline, b''):
        if line: # Don't print blank lines
            yield line
    # This ensures the process has completed, AND sets the 'returncode' attr
    while p.poll() is None:                                                                                                                                        
        sleep(.1) #Don't waste CPU-cycles
    # Empty STDERR buffer
    err = p.stderr.read()
    if p.returncode != 0:
       # The run_command() function is responsible for logging STDERR 
       print("Error: " + str(err))

Kode ini akan dieksekusi sama dengan jawaban sebelumnya:

for line in run_command(cmd):
    print(line)

1
Apakah Anda keberatan menjelaskan bagaimana penambahan tidur (0,1) tidak akan membuang siklus CPU?
Moataz Elmasry

2
Jika kami terus memanggil p.poll()tanpa tidur di antara panggilan, kami akan membuang siklus CPU dengan memanggil fungsi ini jutaan kali. Sebagai gantinya, kita "menekan" loop kita dengan memberi tahu OS bahwa kita tidak perlu diganggu untuk 1/10 detik berikutnya, sehingga dapat melakukan tugas-tugas lain. (Mungkin p.poll () tidur juga, membuat pernyataan tidur kita berlebihan).
The Aelfinn

5

Membagi perintah awal untuk yang subprocessmungkin rumit dan rumit.

Gunakan shlex.split()untuk membantu diri Anda sendiri.

Perintah sampel

git log -n 5 --since "5 years ago" --until "2 year ago"

Kode

from subprocess import check_output
from shlex import split

res = check_output(split('git log -n 5 --since "5 years ago" --until "2 year ago"'))
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'

Tanpa shlex.split()kode akan terlihat sebagai berikut

res = check_output([
    'git', 
    'log', 
    '-n', 
    '5', 
    '--since', 
    '5 years ago', 
    '--until', 
    '2 year ago'
])
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'

1
shlex.split()adalah suatu kemudahan, terutama jika Anda tidak tahu bagaimana tepatnya mengutip dalam shell berfungsi; tetapi secara manual mengubah string ini ke daftar ['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago']tidak sulit sama sekali jika Anda mengerti mengutip.
tripleee

4

Jika Anda perlu menjalankan perintah shell pada banyak file, ini berhasil bagi saya.

import os
import subprocess

# Define a function for running commands and capturing stdout line by line
# (Modified from Vartec's solution because it wasn't printing all lines)
def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

# Get all filenames in working directory
for filename in os.listdir('./'):
    # This command will be run on each file
    cmd = 'nm ' + filename

    # Run the command and capture the output line by line.
    for line in runProcess(cmd.split()):
        # Eliminate leading and trailing whitespace
        line.strip()
        # Split the output 
        output = line.split()

        # Filter the output and print relevant lines
        if len(output) > 2:
            if ((output[2] == 'set_program_name')):
                print filename
                print line

Sunting: Baru melihat solusi Max Persson dengan saran JF Sebastian. Pergi ke depan dan memasukkan itu.


Popenmenerima string, tetapi kemudian Anda perlu shell=True, atau daftar argumen, dalam hal ini Anda harus meneruskan ['nm', filename]bukannya string. Yang terakhir lebih disukai karena shell menambah kompleksitas tanpa memberikan nilai apa pun di sini. Melewati string tanpa shell=Truetampaknya bekerja pada Windows, tetapi itu bisa berubah di versi Python berikutnya.
tripleee

2

Menurut @senderle, jika Anda menggunakan python3.6 seperti saya:

def sh(cmd, input=""):
    rst = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=input.encode("utf-8"))
    assert rst.returncode == 0, rst.stderr.decode("utf-8")
    return rst.stdout.decode("utf-8")
sh("ls -a")

Akan bertindak persis seperti Anda menjalankan perintah di bash


Anda menciptakan kembali argumen kata kunci check=True, universal_newlines=True. Dengan kata lain subprocess.run()sudah melakukan semua kode Anda.
tripleee

1

Jika Anda menggunakan subprocessmodul python, Anda dapat menangani STDOUT, STDERR dan mengembalikan kode perintah secara terpisah. Anda dapat melihat contoh untuk implementasi pemanggil perintah lengkap. Tentu saja Anda dapat memperpanjangnya try..exceptjika Anda mau.

Fungsi di bawah ini mengembalikan kode STDOUT, STDERR, dan Return sehingga Anda dapat menanganinya di skrip lain.

import subprocess

def command_caller(command=None)
    sp = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False)
    out, err = sp.communicate()
    if sp.returncode:
        print(
            "Return code: %(ret_code)s Error message: %(err_msg)s"
            % {"ret_code": sp.returncode, "err_msg": err}
            )
    return sp.returncode, out, err

Implementasi lain yang buruk dari subprocess.run(). Jangan menemukan kembali roda.
tripleee

0

mis., jalankan ('ls -ahl') yang membedakan tiga / empat kemungkinan pengembalian dan platform OS:

  1. tidak ada output, tetapi jalankan dengan sukses
  2. output baris kosong, jalankan dengan sukses
  3. lari gagal
  4. Keluarkan sesuatu, jalankan dengan sukses

fungsi di bawah ini

def execute(cmd, output=True, DEBUG_MODE=False):
"""Executes a bash command.
(cmd, output=True)
output: whether print shell output to screen, only affects screen display, does not affect returned values
return: ...regardless of output=True/False...
        returns shell output as a list with each elment is a line of string (whitespace stripped both sides) from output
        could be 
        [], ie, len()=0 --> no output;    
        [''] --> output empty line;     
        None --> error occured, see below

        if error ocurs, returns None (ie, is None), print out the error message to screen
"""
if not DEBUG_MODE:
    print "Command: " + cmd

    # https://stackoverflow.com/a/40139101/2292993
    def _execute_cmd(cmd):
        if os.name == 'nt' or platform.system() == 'Windows':
            # set stdin, out, err all to PIPE to get results (other than None) after run the Popen() instance
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        else:
            # Use bash; the default is sh
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, executable="/bin/bash")

        # the Popen() instance starts running once instantiated (??)
        # additionally, communicate(), or poll() and wait process to terminate
        # communicate() accepts optional input as stdin to the pipe (requires setting stdin=subprocess.PIPE above), return out, err as tuple
        # if communicate(), the results are buffered in memory

        # Read stdout from subprocess until the buffer is empty !
        # if error occurs, the stdout is '', which means the below loop is essentially skipped
        # A prefix of 'b' or 'B' is ignored in Python 2; 
        # it indicates that the literal should become a bytes literal in Python 3 
        # (e.g. when code is automatically converted with 2to3).
        # return iter(p.stdout.readline, b'')
        for line in iter(p.stdout.readline, b''):
            # # Windows has \r\n, Unix has \n, Old mac has \r
            # if line not in ['','\n','\r','\r\n']: # Don't print blank lines
                yield line
        while p.poll() is None:                                                                                                                                        
            sleep(.1) #Don't waste CPU-cycles
        # Empty STDERR buffer
        err = p.stderr.read()
        if p.returncode != 0:
            # responsible for logging STDERR 
            print("Error: " + str(err))
            yield None

    out = []
    for line in _execute_cmd(cmd):
        # error did not occur earlier
        if line is not None:
            # trailing comma to avoid a newline (by print itself) being printed
            if output: print line,
            out.append(line.strip())
        else:
            # error occured earlier
            out = None
    return out
else:
    print "Simulation! The command is " + cmd
    print ""

0

Keluaran dapat diarahkan ke file teks dan kemudian membacanya kembali.

import subprocess
import os
import tempfile

def execute_to_file(command):
    """
    This function execute the command
    and pass its output to a tempfile then read it back
    It is usefull for process that deploy child process
    """
    temp_file = tempfile.NamedTemporaryFile(delete=False)
    temp_file.close()
    path = temp_file.name
    command = command + " > " + path
    proc = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    if proc.stderr:
        # if command failed return
        os.unlink(path)
        return
    with open(path, 'r') as f:
        data = f.read()
    os.unlink(path)
    return data

if __name__ == "__main__":
    path = "Somepath"
    command = 'ecls.exe /files ' + path
    print(execute(command))

Tentu bisa, tetapi mengapa Anda ingin; dan mengapa Anda menggunakan shell bukannya lewat stdout=temp_file?
tripleee

Sebenarnya, secara umum Anda benar tetapi dalam contoh saya ecls.exetampaknya menyebarkan alat baris perintah lain, sehingga cara sederhana kadang-kadang tidak berfungsi.
MR

0

baru saja menulis skrip bash kecil untuk melakukan ini menggunakan ikal

https://gist.github.com/harish2704/bfb8abece94893c53ce344548ead8ba5

#!/usr/bin/env bash

# Usage: gdrive_dl.sh <url>

urlBase='https://drive.google.com'
fCookie=tmpcookies

curl="curl -L -b $fCookie -c $fCookie"
confirm(){
    $curl "$1" | grep jfk-button-action | sed -e 's/.*jfk-button-action" href="\(\S*\)".*/\1/' -e 's/\&amp;/\&/g'
}

$curl -O -J "${urlBase}$(confirm $1)"
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.