Apakah ada cara untuk mengetahui bagaimana pengguna menjalankan program dari bash?

Inilah masalahnya: Saya memiliki skrip ini foo.py, dan jika pengguna memintanya tanpa opsi --bar, saya ingin menampilkan pesan kesalahan berikut:

Please add the --bar option to your command, like so:
    python foo.py --bar

Sekarang, bagian tersulitnya adalah ada beberapa cara pengguna menjalankan perintah:

  • Mereka mungkin menggunakan python foo.py seperti pada contoh
  • Mereka mungkin telah menggunakan /usr/bin/foo.py
  • Mereka mungkin memiliki alias shell frob='python foo.py', dan sebenarnya menjalankan frob
  • Mungkin itu bahkan git alias flab=!/usr/bin/foo.py, dan mereka menggunakan git flab

Dalam setiap kasus, saya ingin pesan mencerminkan bagaimana pengguna menjalankan perintah, sehingga contoh yang saya berikan masuk akal.

sys.argv selalu berisi foo.py, dan /proc/$$/cmdline tidak mengenal alias. Tampaknya bagi saya satu-satunya sumber informasi ini adalah bash itu sendiri, tetapi saya tidak tahu bagaimana cara menanyakannya.

Ada ide?

PEMBARUAN Bagaimana jika kita membatasi kemungkinan skenario hanya pada skenario yang tercantum di atas?

PEMBARUAN 2: Banyak orang menulis penjelasan yang sangat bagus tentang mengapa hal ini tidak mungkin dilakukan dalam kasus umum, jadi saya ingin membatasi pertanyaan saya pada ini:

Dengan asumsi berikut:

  • Script dimulai secara interaktif, dari bash
  • The script was start in one of these 3 ways:
    1. foo <args> where foo is a symbolic link /usr/bin/foo -> foo.py
    2. git foo di mana alias.foo=!/usr/bin/foo di ~/.gitconfig
    3. git baz di mana alias.baz=!/usr/bin/foo di ~/.gitconfig

Apakah ada cara untuk membedakan antara 1 dan (2,3) dari dalam skrip? Apakah ada cara untuk membedakan antara 2 dan 3 dari dalam skrip?

Saya tahu ini masih jauh, jadi saya menerima jawaban Charles Duffy untuk saat ini.

PEMBARUAN 3: Sejauh ini, sudut pandang yang paling menjanjikan dikemukakan oleh Charles Duffy dalam komentar di bawah. Jika saya bisa membuat pengguna saya memilikinya

trap 'export LAST_BASH_COMMAND=$(history 1)' DEBUG

di .bashrc mereka, maka saya dapat menggunakan sesuatu seperti ini di kode saya:

like_so = None
cmd = os.environ['LAST_BASH_COMMAND']
if cmd is not None:
    cmd = cmd[8:]  # Remove the history counter
    if cmd.startswith("foo "):
        like_so = "foo --bar " + cmd[4:]
    elif cmd.startswith(r"git foo "):
        like_so = "git foo --bar " + cmd[8:]
    elif cmd.startswith(r"git baz "):
        like_so = "git baz --bar " + cmd[8:]
if like_so is not None:
    print("Please add the --bar option to your command, like so:")
    print("    " + like_so)
else:
    print("Please add the --bar option to your command.")

Dengan cara ini, saya menampilkan pesan umum jika saya tidak berhasil mendapatkan metode pemanggilannya. Tentu saja, jika saya akan mengandalkan perubahan lingkungan pengguna saya, saya mungkin juga memastikan bahwa berbagai alias mengekspor variabel lingkungan mereka sendiri yang dapat saya lihat, tapi setidaknya cara ini memungkinkan saya untuk menggunakan teknik yang sama untuk semua variabel lingkungan. skrip lain yang mungkin saya tambahkan nanti.


person itsadok    schedule 12.07.2018    source sumber
comment
Mungkin history 1 sudah cukup?   -  person Aaron    schedule 12.07.2018
comment
Jika --bar wajib, bagaimana kalau selalu menambahkannya sendiri secara internal, dan memberikan --no-bar untuk pengguna yang lebih mahir yang tidak menginginkannya dan tahu cara menambahkan argumen?   -  person Mark Setchell    schedule 12.07.2018
comment
@MarkSetchell upaya saya untuk bersikap umum menjadi bumerang di sini. Kasus penggunaan saya saat ini adalah tentang skrip yang keluar, tetapi ingin memberi tahu pengguna bahwa mereka dapat melanjutkan --lanjutkan. Seperti ketika git rebase mengalami konflik. Jadi idemu tidak akan berhasil untukku.   -  person itsadok    schedule 12.07.2018
comment
@Aaron ya! Tapi bagaimana saya bisa mengakses riwayat proses bash induk?   -  person itsadok    schedule 12.07.2018
comment
Saya tidak yakin apa yang Anda maksud, riwayat mencatat apa yang dimasukkan pengguna di shell saat ini (dan shell lain yang riwayatnya disimpan di ~/.bash_history). Ini tidak akan mencatat eksekusi skrip Anda dalam beberapa situasi, misalnya skrip dipanggil dari skrip lain (hanya skrip yang dipanggil pengguna yang akan direkam) atau dari cron atau layanan lainnya. Itu juga dapat dinonaktifkan oleh pengguna.   -  person Aaron    schedule 12.07.2018
comment
@Aaron, riwayat biasanya hanya bertahan ketika bash keluar, kecuali Anda menggunakan beberapa trik dengan $PROMPT_COMMAND. Saya bertanya-tanya apakah ada cara untuk menanyakan riwayat proses bash yang sedang berjalan. Saya tidak peduli dengan kasus ketika sesuatu selain bash interaktif memunculkan proses, dengan pengecualian git, yang muncul dari bash interaktif.   -  person itsadok    schedule 12.07.2018
comment
Bukannya aku tahu, maaf   -  person Aaron    schedule 12.07.2018
comment
Lalu bagaimana jika pengguna meletakkannya di crontab? Anda tidak dapat menampilkan pesan pengguna di sana. Bukankah Anda seharusnya menambahkan pesan ke /var/log/messages?   -  person Dominique    schedule 18.07.2018
comment
Daripada menghabiskan banyak siklus tentang cara menemukan nama pemanggilan (yang IMHO, ada banyak kasus sudut yang akan gagal) -- Mengapa tidak mengerjakan pesan ke pengguna? Please add the --bar option to your command, like so: 'cmd --bar' Kebanyakan orang cukup pintar (sekali lagi, IMHO) untuk mengetahui bahwa cmd adalah isian untuk apa pun yang mereka ketik.   -  person dawg    schedule 18.07.2018
comment
Perhatikan bahwa adalah berpotensi membuat shell mengekspor salinan BASH_COMMAND ke lingkungan dari jebakan DEBUG, namun jika Anda mengandalkannya, program Anda hanya akan memiliki perilaku tersebut ketika dipanggil oleh a cangkangnya sudah disiapkan... jadi sepertinya tidak berguna.   -  person Charles Duffy    schedule 19.07.2018
comment
@dawg ini tentu saja solusi yang saya miliki saat ini. Saya bertanya-tanya apakah ada cara untuk membuatnya lebih nyaman.   -  person itsadok    schedule 19.07.2018
comment
Perihal: suntingan -- jika Anda ingin menelusuri pohon proses dan melihat baris perintah induk hingga Anda menemukan perintah git, ya, Anda dapat melakukannya. Alias ​​Shell tidak ditemukan di mana pun dalam riwayat, tetapi karena pemanggilan alias git melalui perintah eksternal yang sebenarnya, alias tersebut akan muncul.   -  person Charles Duffy    schedule 19.07.2018
comment
(Re: symlinks, pendekatan standar argv[0] berfungsi untuk mereka, dimediasi melalui /proc/self/cmdline atau sebaliknya).   -  person Charles Duffy    schedule 19.07.2018
comment
BTW, baris perintah lengkap saat diketik adalah pertanyaan yang terkait erat .   -  person Charles Duffy    schedule 19.07.2018
comment
Lihat jawaban saya yang diperbarui di bawah ini: bungkus skrip python dalam skrip bash, yang memberikan pesan kesalahan yang sesuai, berdasarkan kode keluar bukan nol, misalnya.   -  person philwalk    schedule 19.07.2018


Jawaban (5)


Tidak, tidak ada cara untuk melihat teks asli (sebelum alias/fungsi/dll).

Memulai program di UNIX dilakukan sebagai berikut pada tingkat syscall yang mendasarinya:

int execve(const char *path, char *const argv[], char *const envp[]);

Khususnya, ada tiga argumen:

  • Jalur menuju eksekusi
  • Array argv (item pertama -- argv[0] atau $0 -- diteruskan ke executable tersebut untuk mencerminkan nama permulaannya)
  • Daftar variabel lingkungan

Tidak ada string yang menyediakan perintah shell asli yang dimasukkan pengguna yang menjadi asal permintaan pemanggilan proses baru. Hal ini terutama benar karena tidak semua program dimulai dari shell; pertimbangkan kasus di mana program Anda dimulai dari skrip Python lain dengan shell=False.


Ini sepenuhnya konvensional di UNIX untuk berasumsi bahwa program Anda dimulai melalui nama apa pun yang diberikan di argv[0]; ini berfungsi untuk symlink.

Anda bahkan dapat melihat alat UNIX standar melakukan hal ini:

$ ls '*.txt'         # sample command to generate an error message; note "ls:" at the front
ls: *.txt: No such file or directory
$ (exec -a foobar ls '*.txt')   # again, but tell it that its name is "foobar"
foobar: *.txt: No such file or directory
$ alias somesuch=ls             # this **doesn't** happen with an alias
$ somesuch '*.txt'              # ...the program still sees its real name, not the alias!
ls: *.txt: No such file 

Jika Anda melakukan ingin membuat baris perintah UNIX, gunakan pipes.quote() (Python 2) atau shlex.quote() (Python 3) untuk melakukannya dengan aman.

try:
    from pipes import quote # Python 2
except ImportError:
    from shlex import quote # Python 3

cmd = ' '.join(quote(s) for s in open('/proc/self/cmdline', 'r').read().split('\0')[:-1])
print("We were called as: {}".format(cmd))

Sekali lagi, ini tidak akan "membatalkan perluasan" alias, kembali ke kode yang dipanggil untuk memanggil fungsi yang menjalankan perintah Anda, dll; tidak ada yang bisa membatalkan dering bel itu.


dapat itu digunakan untuk mencari instance git di pohon proses induk Anda, dan menemukan daftar argumennya:

def find_cmdline(pid):
    return open('/proc/%d/cmdline' % (pid,), 'r').read().split('\0')[:-1]

def find_ppid(pid):
    stat_data = open('/proc/%d/stat' % (pid,), 'r').read()
    stat_data_sanitized = re.sub('[(]([^)]+)[)]', '_', stat_data)
    return int(stat_data_sanitized.split(' ')[3])

def all_parent_cmdlines(pid):
    while pid > 0:
        yield find_cmdline(pid)
        pid = find_ppid(pid)

def find_git_parent(pid):
    for cmdline in all_parent_cmdlines(pid):
        if cmdline[0] == 'git':
            return ' '.join(quote(s) for s in cmdline)
    return None
person Charles Duffy    schedule 18.07.2018
comment
@philwalk, jika saya memahami proposal Anda dengan benar (mengambil riwayat dari skrip), itu sangat sensitif terhadap konfigurasi runtime -- banyak konfigurasi shell tidak akan menulis riwayat ke file sampai keluar sama sekali. Jika Anda cukup mengontrol konfigurasi shell pengguna untuk memastikannya berfungsi, Anda dapat meminta mereka memasang jebakan DEBUG yang mengekspor salinan $BASH_COMMAND dan menyelesaikannya. - person Charles Duffy; 19.07.2018
comment
@philwalk, ...apalagi, tidak benar untuk mengatakan bahwa saya memberikan sebagai bukti beberapa pendekatan yang tidak berhasil -- Saya memberikan pendekatan yang berfungsi dalam kasus git alias (fungsi find_git_parent), dan pendekatan yang berfungsi dalam kasus symlink (sys.argv[0], yang -- seperti yang saya tunjukkan -- adalah pendekatan yang digunakan oleh alat UNIX standar). adalah kasus yang mengizinkan solusi yang tidak bergantung pada detail konfigurasi runtime shell. - person Charles Duffy; 19.07.2018
comment
Saya mengacu pada judul jawaban Anda. Tidak ada cara untuk melihat teks aslinya (sebelum alias/fungsi/dll).. Solusi saya melakukan hal itu. - person philwalk; 20.07.2018
comment
@philwalk, ya? Ini bukanlah solusi; OP ingin mengambil teks itu dari juru bahasa Python. Anda hanya dapat mengambilnya dari shell yang sama yang menjalankan perintah -- bahkan dari subkulit yang menafsirkan skrip, tanpa partisipasi aktif shell induk interaktif. - person Charles Duffy; 20.07.2018
comment
@philwalk, ...ketika skrip aliasTest Anda dapat dengan andal mengetahui apakah if dipanggil melalui alias, tanpa mengharuskan shell yang melakukan pemanggilan tersebut memiliki konfigurasi non-default sebelumnya, itu< /b> saat saya terkesan -- dan bukan sebelumnya. - person Charles Duffy; 20.07.2018
comment
pertanyaan awal secara khusus bertanya-tanya apakah mungkin untuk mendapatkan informasi dari lingkungan bash. Dan pertanyaan seperti yang diungkapkan saat ini menginginkan cara untuk menyesuaikan pesan kesalahan, dan jawaban saya memberikan informasi yang diperlukan untuk melakukan itu. - person philwalk; 20.07.2018
comment
@philwalk, dari lingkungan bash, ke Python (sehingga skrip Python dapat mengubah pesan kesalahannya). - person Charles Duffy; 20.07.2018

Lihat Catatan di bagian bawah mengenai skrip pembungkus yang awalnya diusulkan.

Pendekatan baru yang lebih fleksibel adalah skrip python menyediakan opsi baris perintah baru, memungkinkan pengguna menentukan string khusus yang ingin mereka lihat dalam pesan kesalahan.

Misalnya, jika pengguna lebih suka memanggil skrip python 'myPyScript.py' melalui alias, mereka dapat mengubah definisi alias dari ini:

  alias myAlias='myPyScript.py $@'

untuk ini:

  alias myAlias='myPyScript.py --caller=myAlias $@'

Jika mereka lebih suka memanggil skrip python dari skrip shell, ia dapat menggunakan opsi baris perintah tambahan seperti:

  #!/bin/bash
  exec myPyScript.py "$@" --caller=${0##*/}

Kemungkinan penerapan lain dari pendekatan ini:

  bash -c myPyScript.py --caller="bash -c myPyScript.py"

  myPyScript.py --caller=myPyScript.py

Untuk mencantumkan baris perintah yang diperluas, berikut skrip 'pyTest.py', berdasarkan masukan dari @CharlesDuffy, yang mencantumkan cmdline untuk skrip python yang sedang berjalan, serta proses induk yang memunculkannya. Jika argumen -caller baru digunakan, argumen tersebut akan muncul di baris perintah, meskipun alias akan diperluas, dll.

#!/usr/bin/env python

import os, re

with open ("/proc/self/stat", "r") as myfile:
  data = [x.strip() for x in str.split(myfile.readlines()[0],' ')]

pid = data[0]
ppid = data[3]

def commandLine(pid):
  with open ("/proc/"+pid+"/cmdline", "r") as myfile:
    return [x.strip() for x in str.split(myfile.readlines()[0],'\x00')][0:-1]

pid_cmdline = commandLine(pid)
ppid_cmdline = commandLine(ppid)

print "%r" % pid_cmdline
print "%r" % ppid_cmdline

Setelah menyimpannya ke file bernama 'pytest.py', dan kemudian memanggilnya dari skrip bash bernama 'pytest.sh' dengan berbagai argumen, inilah hasilnya:

$ ./pytest.sh a b "c d" e
['python', './pytest.py']
['/bin/bash', './pytest.sh', 'a', 'b', 'c d', 'e']

CATATAN: kritik terhadap skrip pembungkus asli aliasTest.sh valid. Meskipun keberadaan alias yang telah ditentukan sebelumnya adalah bagian dari spesifikasi pertanyaan, dan dapat dianggap ada di lingkungan pengguna, proposal tersebut mendefinisikan alias tersebut (menciptakan kesan menyesatkan bahwa itu adalah bagian dari rekomendasi, bukan bagian yang ditentukan). bagian dari lingkungan pengguna), dan itu tidak menunjukkan bagaimana pembungkus akan berkomunikasi dengan skrip python yang disebut. Dalam praktiknya, pengguna harus mencari sumber pembungkus atau menentukan alias di dalam pembungkus, dan skrip python harus mendelegasikan pencetakan pesan kesalahan ke beberapa skrip panggilan khusus (tempat informasi panggilan berada), dan klien akan memilikinya. untuk memanggil skrip pembungkus. Pemecahan masalah tersebut menghasilkan pendekatan yang lebih sederhana, yang dapat diperluas ke sejumlah kasus penggunaan tambahan.

Berikut versi skrip asli yang tidak terlalu membingungkan, untuk referensi:

#!/bin/bash
shopt -s expand_aliases
alias myAlias='myPyScript.py'

# called like this:
set -o history
myAlias $@
_EXITCODE=$?
CALL_HISTORY=( `history` )
_CALLING_MODE=${CALL_HISTORY[1]}

case "$_EXITCODE" in
0) # no error message required
  ;;
1)
  echo "customized error message #1 [$_CALLING_MODE]" 1>&2
  ;;
2)
  echo "customized error message #2 [$_CALLING_MODE]" 1>&2
  ;;
esac

Inilah hasilnya:

$ aliasTest.sh 1 2 3
['./myPyScript.py', '1', '2', '3']
customized error message #2 [myAlias]
person philwalk    schedule 18.07.2018
comment
Saya sedang mengerjakan versi yang menyertakan masukan Anda, terima kasih! - person philwalk; 19.07.2018
comment
Ingatlah bahwa riwayat tidak selalu disimpan sama sekali -- jika suatu perintah dijalankan dengan spasi sebelumnya, misalnya, maka perintah tersebut bahkan tidak disimpan dalam riwayat di dalam memori, apalagi dimasukkan ke disk, dengan HISTCONTROL disetel ke nospace atau ignoreboth. Dan bahkan ketika disimpan, pembilasan setelah setiap perintah adalah sesuatu yang harus diaktifkan secara eksplisit. Dan karena shell yang berbeda memiliki riwayat yang berbeda, pendekatan ini perlu mengetahui shell mana yang dijalankan pengguna agar memiliki peluang. - person Charles Duffy; 19.07.2018
comment
Saya sangat bersemangat dengan gagasan membungkus skrip python dengan skrip bash, tetapi menemukan bahwa skrip bash tidak melihat riwayat induknya kecuali saya meminta pengguna untuk selalu mencari sumbernya, yang menurut saya bukan opsi yang layak. . Sepertinya saran Charles Duffy tentang penggunaan DEBUG adalah sudut pandang yang paling menjanjikan. - person itsadok; 22.07.2018
comment
@itsadok ... dalam contoh aliasTest.sh di atas, ini menghasilkan baris perintah persis seperti yang dipanggil dari dalam skrip bash, tanpa memerlukan sumber skrip apa pun. - person philwalk; 23.07.2018
comment
Alternatif yang sangat sederhana, jika Anda dapat memberikan alias, atau mendokumentasikan cara pembuatannya, adalah dengan meminta alias atau skrip memberikan argumen yang mengidentifikasi tag yang diperlukan untuk pesan kesalahan. Atau, secara setara, jika alias dibuat untuk skrip shell bernama fooForAlias.sh, Anda dapat mengidentifikasinya di sys.argv[0], memberikan informasi yang diinginkan. - person philwalk; 24.07.2018
comment
@philwalk ini berfungsi di aliasTest.sh karena Anda menggunakan perintah history dari proses bash yang sama yang menjalankan perintah tersebut. Tidak ada cara untuk mengetahui bagaimana skrip aliasTest.sh itu sendiri dipanggil. - person itsadok; 24.07.2018
comment
@philwalk, ...ini berarti jika program Anda ingin memiliki penanganan kesalahan seperti ini ketika dipanggil dari shell pengguna, Anda harus menjalankan kode history di shell pengguna itu sendiri, bukan di a skrip yang dimulai shell. Ini bukan pendekatan yang bisa diikuti secara wajar dalam praktiknya, atau kebersihan yang baik -- jika setiap program hanya mencetak pesan kesalahan yang baik kepada pengguna yang memodifikasi konfigurasi shell mereka untuk menyelesaikan pemanggilannya, kita akan mendapat kekacauan. - person Charles Duffy; 25.07.2018
comment
@Charles Duffy ... Saya harus memperbarui deskripsinya karena tampaknya tidak jelas apa yang saya usulkan ... klien harus menelepon melalui skrip pembungkus, bukan meniru cara memanggil skrip python. Pembungkus terpisah diperlukan untuk setiap kasus penggunaan. - person philwalk; 26.07.2018
comment
Saya tidak mengerti bagaimana pemanggilan melalui wrapper menyelesaikan masalah -- wrapper tidak memiliki akses ke riwayat proses induknya, hanya riwayat nya sendiri, kecuali shell pengguna memiliki konfigurasi yang sangat spesifik ( segera membuang riwayat ke disk pada setiap perintah, yang bukan merupakan perilaku default). Tapi ya, silakan edit untuk mendemonstrasikannya. - person Charles Duffy; 26.07.2018
comment
Selain itu: $@ tidak berguna di dalam alias -- alias hanyalah substitusi awalan, jadi semua teks yang tersisa selalu mengikutinya. Jadi, alias foo='bar "$@"' dan kemudian foo one two three memanggil bar "$@" one two three; Anda tidak memperhatikan hal ini dalam skenario umum karena untuk shell interaktif $@ hampir selalu merupakan daftar elemen nol (kecuali jika Anda berusaha keras untuk memodifikasinya, seperti pada set -- "first argument" "second argument"). - person Charles Duffy; 27.07.2018
comment
(Dan $@ yang tidak diberi tanda kutip identik dengan $*, artinya menghilangkan perbedaan antara "first argument" "second argument" dan "first" "argument" "second" "argument"). - person Charles Duffy; 27.07.2018
comment
...namun kembali ke pokok permasalahan: Jika Anda akan memodifikasi shell pemanggil, Anda cukup memodifikasinya ke set $0, sehingga tidak memerlukan argumen baris perintah tambahan sama sekali. Misalnya: myAlias() { exec -a myAlias myPyScript "$@"; } akan memanggil myPyScript dengan myAlias di $0. - person Charles Duffy; 27.07.2018

Tidak ada cara untuk membedakan antara saat penerjemah skrip ditentukan secara eksplisit pada baris perintah dan saat disimpulkan oleh OS dari baris hashbang.

Bukti:

$ cat test.sh 
#!/usr/bin/env bash

ps -o command $$

$ bash ./test.sh 
COMMAND
bash ./test.sh

$ ./test.sh 
COMMAND
bash ./test.sh

Hal ini mencegah Anda mendeteksi perbedaan antara dua kasus pertama dalam daftar Anda.

Saya juga yakin bahwa tidak ada cara yang masuk akal untuk mengidentifikasi cara lain (yang dimediasi) dalam memanggil suatu perintah.

person Leon    schedule 18.07.2018
comment
Saya setuju bahwa tidak mungkin untuk mengambil kembali baris perintah shell asli dengan kuat, tetapi saya tidak setuju Anda membuktikannya di sini. Ada data yang jauh lebih baik di /proc/self daripada yang tersedia di ps. cmdline_for_pid() { local -a args; local arg; args=( ); while IFS= read -r -d '' arg; do args+=( "$arg" ); done </proc/"$1"/cmdline; printf '%q ' "${args[@]}"; echo; }, misalnya, mendefinisikan fungsi cmdline_for_pid yang memberikan daftar yang jauh lebih baik daripada ps. - person Charles Duffy; 19.07.2018
comment
@CharlesDuffy Untuk kasus pengujian saya cmdline_for_pid berfungsi tidak berbeda dengan ps -o command - person Leon; 19.07.2018
comment
gunakan kasus uji yang lebih menarik -- yang dilengkapi spasi, tanda kutip, dll. - person Charles Duffy; 19.07.2018
comment
@CharlesDuffy Saya tahu bedanya. Saya hanya ingin mencatat bahwa untuk ilustrasi pernyataan saya cmdline_for_pid tidak menambahkan sesuatu yang berguna sementara mengharuskan pembaca untuk menguraikan buktinya. - person Leon; 19.07.2018
comment
Kalau begitu, saya memahami kata bukti memiliki arti yang berbeda dari yang Anda maksudkan. Saat saya membacanya, suatu poin hanya terbukti tentang data mana yang tersedia atau tidak jika data tersebut tercermin dalam bentuk yang lengkap dan kanonik; terjemahan yang lossy tidak membuktikan sesuatu yang berguna tentang apa yang bisa diambil dengan proses yang tidak terlalu lossy, dan dengan demikian tidak membuktikan apa pun tentang data mana yang benar-benar tersedia atau tidak untuk diambil. - person Charles Duffy; 19.07.2018

Saya dapat melihat dua cara untuk melakukan ini:

  • Yang paling sederhana, seperti yang disarankan oleh 3sky, adalah mengurai baris perintah dari dalam skrip python. argparse dapat digunakan untuk melakukannya dengan cara yang andal. Ini hanya berfungsi jika Anda dapat mengubah skrip itu.
  • Cara yang lebih rumit, sedikit lebih umum dan rumit, adalah dengan mengubah python yang dapat dieksekusi di sistem Anda.

Karena opsi pertama sudah didokumentasikan dengan baik, berikut sedikit rincian lebih lanjut tentang opsi kedua:

Terlepas dari cara skrip Anda dipanggil, python dijalankan. Tujuannya di sini adalah mengganti python yang dapat dieksekusi dengan skrip yang memeriksa apakah foo.py ada di antara argumen, dan jika ya, periksa apakah --bar juga ada. Jika tidak, cetak pesannya dan kembalikan.

Dalam setiap kasus lainnya, jalankan python asli yang dapat dieksekusi.

Sekarang, mudah-mudahan, menjalankan python dilakukan melalui shebang berikut: #!/usr/bin/env python3, atau melalui python foo.py, bukan varian #!/usr/bin/python atau /usr/bin/python foo.py. Dengan begitu, Anda dapat mengubah variabel $PATH, dan menambahkan direktori tempat python palsu Anda berada.

Dalam kasus lain, Anda dapat mengganti /usr/bin/python executable, dengan risiko tidak bermain baik dengan pembaruan.

Cara yang lebih kompleks untuk melakukan hal ini mungkin adalah dengan namespace dan mount, tetapi cara di atas mungkin sudah cukup, terutama jika Anda memiliki hak admin.


Contoh apa yang bisa berfungsi sebagai skrip:

#!/usr/bin/env bash

function checkbar
{
    for i in "$@"
    do
            if [ "$i" = "--bar" ]
            then
                    echo "Well done, you added --bar!"
                    return 0
            fi
    done
    return 1
}

command=$(basename ${1:-none})
if [ $command = "foo.py" ]
then
    if ! checkbar "$@"
    then
        echo "Please add --bar to the command line, like so:"
        printf "%q " $0
        printf "%q " "$@"
        printf -- "--bar\n"
        exit 1
    fi
fi
/path/to/real/python "$@"

Namun, setelah membaca kembali pertanyaan Anda, jelas saya salah memahaminya. Menurut pendapat saya, tidak apa-apa untuk mencetak "foo.py harus dipanggil seperti foo.py --bar", "harap tambahkan bilah ke argumen Anda" atau "silakan coba (daripada )", apa pun yang terjadi pengguna memasukkan:

  • Jika itu adalah alias (git), ini adalah kesalahan satu kali, dan pengguna akan mencoba aliasnya setelah membuatnya, sehingga mereka tahu di mana harus meletakkan bagian --bar
  • with either with /usr/bin/foo.py or python foo.py:
    • If the user is not really command line-savvy, they can just paste the working command that is displayed, even if they don't know the difference
    • Jika ya, mereka seharusnya dapat memahami pesan tersebut tanpa kesulitan, dan menyesuaikan baris perintah mereka.
person MayeulC    schedule 18.07.2018
comment
"$@", bukan $@. $@ yang tidak diberi tanda kutip berperilaku sama dengan $* yang tidak diberi tanda kutip -- yang berarti bahwa semua argumen Anda dipecah menjadi string dan diperluas sebagai gumpalan. - person Charles Duffy; 19.07.2018
comment
Dan exit -1 tidak masuk akal -- status keluar UNIX adalah bilangan bulat unsigned (positif) satu byte. Dan [ tidak menjanjikan bahwa == akan berfungsi sama sekali -- satu-satunya POSIX- operator perbandingan string standar adalah =. Dan x tidak ada gunanya/tidak ada gunanya ketika Anda mengutip perluasan Anda dan tidak menggunakan mode test yang ditandai usang (-a dan -o menimbulkan ambiguitas sintaksis, tetapi hal itu tidak ada di sini). - person Charles Duffy; 19.07.2018
comment
Ya, itu dengan cepat dijadikan sebagai contoh, dan tidak diuji secara menyeluruh; Saya ingin aman dengan x. Anda benar, saya akan mengubah keduanya $@ menjadi "$@". Bit tanda menghasilkan nilai kembalian 255, tetapi saya dapat mengubahnya menjadi 1, karena itu sebenarnya tidak terlalu menjadi masalah. Namun jawaban tersebut sebenarnya di luar topik karena tidak menjawab kekhawatiran OP. - person MayeulC; 19.07.2018

Saya tahu ini tugas bash, tapi menurut saya cara termudah adalah mengubah 'foo.py'. Tentu saja tergantung tingkat kerumitan skrip, tapi mungkin cocok. Berikut ini contoh kode:

#!/usr/bin/python

import sys

if len(sys.argv) > 1 and sys.argv[1] == '--bar':
    print 'make magic'
else:
    print 'Please add the --bar option to your command, like so:'
    print '    python foo.py --bar'

Dalam hal ini, tidak masalah bagaimana pengguna menjalankan kode ini.

$ ./a.py
Please add the --bar option to your command, like so:
    python foo.py --bar

$ ./a.py -dua
Please add the --bar option to your command, like so:
    python foo.py --bar

$ ./a.py --bar
make magic

$ python a.py --t
Please add the --bar option to your command, like so:
    python foo.py --bar

$ /home/3sky/test/a.py
Please add the --bar option to your command, like so:
    python foo.py --bar

$ alias a='python a.py'
$ a
Please add the --bar option to your command, like so:
    python foo.py --bar

$ a --bar
make magic
person 3sky    schedule 18.07.2018
comment
Jawaban Anda tidak menjawab pertanyaan OP dengan cara apa pun. Solusi Anda selalu menghasilkan pesan yang sama ketika opsi --bar tidak ditentukan, sedangkan OP memerlukan pesan berbeda tergantung pada perintah sebenarnya yang digunakan. - person Leon; 18.07.2018