Terlalu banyak baris shebang (deklarasi skrip) - cara apa pun untuk mengurangi jumlahnya?


12

Saya punya proyek yang terdiri dari sekitar 20 .shfile kecil . Saya beri nama "kecil" ini karena umumnya, tidak ada file yang memiliki lebih dari 20 baris kode. Saya mengambil pendekatan modular karena dengan demikian saya setia pada filosofi Unix dan lebih mudah bagi saya untuk mempertahankan proyek.

Di awal setiap .shfile, saya taruh #!/bin/bash.

Sederhananya, saya mengerti deklarasi skrip memiliki dua tujuan:

  1. Mereka membantu pengguna mengingat shell apa yang diperlukan untuk menjalankan file (katakanlah, setelah beberapa tahun tanpa menggunakan file).
  2. Mereka memastikan bahwa skrip berjalan hanya dengan shell tertentu (Bash dalam kasus itu) untuk mencegah perilaku tak terduga jika shell lain digunakan.

Ketika sebuah proyek mulai tumbuh dari katakanlah 5 file menjadi 20 file atau dari 20 file menjadi 50 file (bukan kasus ini tetapi hanya untuk menunjukkan) kami memiliki 20 baris atau 50 baris deklarasi skrip. Saya akui, meskipun mungkin lucu bagi sebagian orang, rasanya agak berlebihan bagi saya untuk menggunakan 20 atau 50 sebagai gantinya mengatakan hanya 1 per proyek (mungkin dalam file utama proyek).

Apakah ada cara untuk menghindari dugaan redundansi 20 atau 50 atau lebih banyak baris deklarasi skrip dengan menggunakan beberapa deklarasi skrip "global", dalam beberapa file utama?


2
Anda seharusnya tidak bisa tetapi tidak bisa; hapus dan jalankan semua skrip Anda dengan/bin/bash -x "$script"
jesse_b


... Biasanya pada saat sesuatu mencapai titik ini, saya sudah menyimpulkan sudah waktunya untuk berhenti menggunakan skrip shell dan mendapatkan bahasa skrip aktual untuk melakukan pekerjaan.
Shadur

Jawaban:


19

Meskipun proyek Anda sekarang mungkin hanya terdiri dari 50 skrip Bash, ia cepat atau lambat akan mulai mengumpulkan skrip yang ditulis dalam bahasa lain seperti Perl atau Python (untuk manfaat yang dimiliki bahasa skrip ini yang tidak dimiliki Bash).

Tanpa garis yang tepat #!di setiap skrip, akan sangat sulit untuk menggunakan berbagai skrip tanpa juga mengetahui penerjemah apa yang digunakan. Tidak masalah jika setiap skrip tunggal dieksekusi dari skrip lain , ini hanya memindahkan kesulitan dari pengguna akhir ke pengembang. Tak satu pun dari dua kelompok orang ini harus tahu bahasa apa yang digunakan untuk menulis naskah.

Script shell dieksekusi tanpa garis #!dan tanpa juru bahasa eksplisit dieksekusi dengan cara yang berbeda tergantung pada shell apa yang memintanya (lihat misalnya pertanyaan. Shell interpreter mana yang menjalankan script tanpa shebang? Dan terutama jawaban Stéphane ), yang bukan yang Anda inginkan dalam lingkungan produksi (Anda ingin perilaku yang konsisten, dan mungkin bahkan portabilitas).

Skrip yang dieksekusi dengan penerjemah eksplisit akan dijalankan oleh penerjemah itu terlepas dari apa yang #!dikatakan -line. Ini akan menyebabkan masalah lebih lanjut di telepon jika Anda memutuskan untuk menerapkan kembali, katakanlah, skrip Bash dengan Python atau bahasa lainnya.

Anda harus menghabiskan penekanan tombol ekstra itu dan selalu menambahkan garis- #!untuk setiap naskah.


Di beberapa lingkungan, ada teks hukum multi-paragraf boilerplate di setiap skrip di setiap proyek. Sangat senang bahwa itu hanya garis #!yang terasa "berlebihan" dalam proyek Anda.


Jawabannya harus diperluas dengan ini: Shell menentukan penerjemah oleh #!baris segera setelah file memiliki izin exectuable dan tidak dimuat oleh penerjemah, tetapi oleh shell. Yaitu, /path/to/myprog.pljalankan program perl jika #!garisnya ada (menunjuk ke penerjemah perl) dan file tersebut memiliki izin exec.
rexkogitans

11

Anda salah mengerti maksud dari #!. Sebenarnya a arahan ke sistem operasi untuk menjalankan program ini di bawah penerjemah yang ditentukan.

Jadi skrip bisa dibuat #!/usr/bin/perldan program yang dihasilkan dapat dijalankan sebagai./myprogram dan penerjemah perl dapat digunakan. Serupa #!/usr/bin/pythonakan menyebabkan program untuk berjalan di bawah python.

Jadi intinya #!/bin/bash memberitahu OS untuk menjalankan program ini di bawah bash.

Ini sebuah contoh:

$ echo $0
/bin/ksh

$ cat x
#!/bin/bash

ps -aux | grep $$

$ ./x
sweh      2148  0.0  0.0   9516  1112 pts/5    S+   07:58   0:00 /bin/bash ./x
sweh      2150  0.0  0.0   9048   668 pts/5    S+   07:58   0:00 grep 2148

Jadi, meskipun shell saya adalah ksh, program "x" berjalan di bawah bash karena #!baris tersebut, dan kita dapat melihatnya secara eksplisit dalam daftar proses.


ps -auxdapat memberikan peringatan,
Weijun Zhou

@ WeijunZhou Katakan lagi ...
Kusalananda

Beberapa versi psmemberikan peringatan Warning: bad syntax, perhaps a bogus '-'?.
Weijun Zhou

2
pssuooprts baik sintaks argumen BSD dan UXIX . -auxsalah UNIX Stephen mungkin berarti auxBSD mana yang benar
Jasen

1
Orang, 2 hal; (1) fokus pada konsep contoh (yang psmenunjukkan pemanggilan bash) bukan sintaks eksplisit; (2) ps -auxberfungsi dengan baik pada CentOS 7 dan Debian 9 tanpa peringatan; cut'n'paste itu persis output dari mesin saya; seluruh diskusi apakah -diperlukan atau tidak berlaku untuk versi lama dari linux procps, bukan versi saat ini.
Stephen Harris

9

Di .sh

Apa yang buruk adalah .shdi akhir nama file.

Bayangkan Anda menulis ulang salah satu skrip dengan python.

Nah sekarang Anda harus mengubah baris pertama agar #!/usr/bin/python3ini tidak terlalu buruk, karena Anda harus mengubah setiap baris kode lainnya dalam file juga. Namun Anda juga harus mengubah nama file dari prog.shmenjadi prog.py. Dan kemudian Anda harus menemukan di mana-mana di skrip lain, dan semua skrip pengguna Anda (skrip yang Anda bahkan tidak tahu ada), dan mengubahnya untuk menggunakan skrip baru.sed -e 's/.sh/.py/g'dapat membantu orang-orang yang Anda kenal. Tapi sekarang alat kontrol revisi Anda menunjukkan perubahan dalam banyak file yang tidak terkait.

Atau lakukan dengan cara Unix dan beri nama program progtidak prog.sh.

Di #!

Jika Anda memiliki yang benar #!, dan atur izin / mode untuk memasukkan eksekusi. Maka Anda tidak perlu tahu penerjemahnya. Komputer akan melakukannya untuk Anda.

chmod +x prog #on gnu adds execute permission to prog.
./prog #runs the program.

Untuk sistem non gnu baca manual untuk chmod(dan belajar oktal), mungkin membacanya pula.


1
Hmmm, ekstensi tidak selalu buruk, setidaknya mereka membantu berkomunikasi dengan pengguna lain apa jenis file.
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy Tapi Anda tidak perlu tahu bahasa, Anda hanya perlu menjalankannya. Bahkan Microsoft berusaha untuk menjauh dari ekstensi (sayangnya yang melakukan ini saya menyembunyikannya). Namun saya setuju bahwa itu bisa menjadi ide yang bagus: .imageuntuk foto. .audiountuk sound dll. Dengan demikian memberi tahu Anda apa itu tetapi tidak tentang implementasi / encoding.
ctrl-alt-delor

1
@ ctrl-alt-delor Jika file dipanggil launch-missilesatau delete-project-and-make-manager-pissedsaya tidak ingin "jalankan saja" ketika saya hanya ingin tahu tentang bahasa :)
Sergiy Kolodyazhnyy

2
jika Anda penasaran dengan bahasa tersebut gunakan fileperintah. itu mengenali sebagian besar tipe file.
Jasen

2
Saya setuju bahwa ekstensi buruk untuk skrip yang dapat dieksekusi, untuk alasan yang diberikan. Saya menggunakan ekstensi .sh untuk skrip yang perlu dipasok oleh skrip shell lain (yaitu skrip yang tidak dapat dieksekusi) - dalam skenario itu, akhiran adalah penting / valid karena memberi tahu kita cara menggunakan file.
Laurence Renshaw

8

[Garis hashbang] memastikan bahwa skrip berjalan hanya dengan shell tertentu (Bash dalam kasus itu) untuk mencegah perilaku yang tidak terduga jika seandainya shell lain digunakan.

Mungkin perlu untuk menunjukkan bahwa ini sangat salah. Baris hashbang tidak menghalangi Anda untuk mencoba memberi makan file ke shell / interpreter yang salah:

$ cat array.sh
#!/bin/bash
a=(a b c)
echo ${a[1]} $BASH_VERSION
$ dash array.sh
array.sh: 2: array.sh: Syntax error: "(" unexpected

(atau serupa dengan awk -f array.shdll.)


Tetapi bagaimanapun juga, pendekatan lain terhadap modularitas adalah dengan mendefinisikan fungsi-fungsi shell dalam file, satu fungsi per file, jika Anda suka, dan kemudian sourcefile-file dari program utama. Dengan begitu, Anda tidak perlu hashbangs: mereka akan diperlakukan sebagai komentar lain, dan semuanya akan berjalan menggunakan shell yang sama. Kelemahannya adalah Anda harus sourcemenggunakan file dengan fungsi (misalnya for x in functions/*.src ; do source "$x" ; done), dan mereka semua akan berjalan menggunakan shell yang sama, sehingga Anda tidak bisa langsung mengganti salah satu dari mereka dengan implementasi-Perl.


+1 misalnya dengan array bash. Pengguna yang tidak terbiasa dengan beberapa shell atau beberapa definisi POSIX, mungkin menganggap beberapa fitur dari satu shell ada di shell lain. Juga untuk fungsi, ini mungkin relevan: unix.stackexchange.com/q/313256/85039
Sergiy Kolodyazhnyy

4

Apakah ada cara untuk menghindari dugaan redundansi 20 atau 50 atau lebih banyak baris deklarasi skrip dengan menggunakan beberapa deklarasi skrip "global", dalam beberapa file utama?

Ada - itu disebut portabilitas atau kepatuhan POSIX. Berusaha keras untuk selalu menulis skrip dengan cara portabel, netral-shell, sumber mereka hanya dari skrip yang menggunakan #!/bin/shatau ketika Anda menggunakan secara /bin/shinteraktif (atau setidaknya shell yang sesuai dengan POSIX seperti ksh).

Namun, skrip portabel tidak menyelamatkan Anda dari:

  • perlu menggunakan fitur dari salah satu shell ( jawaban ilkkachu adalah contoh)
  • perlu menggunakan bahasa skrip yang lebih maju dari pada shell (Python dan Perl)
  • Kesalahan PEBKAC: skrip Anda jatuh ke tangan pengguna J. Apa yang menjalankan skrip Anda tidak seperti yang Anda inginkan, misalnya mereka menjalankan /bin/shskrip dengan csh.

Jika saya dapat menyatakan pendapat saya, "menghindari redundansi" dengan menghilangkan #!adalah tujuan yang salah. Sasarannya haruslah kinerja yang konsisten dan benar-benar skrip yang berfungsi, dan mempertimbangkan kasus sudut, mengikuti aturan umum tertentu seperti tidak-parsing-ls atau selalu-mengutip-variabel-kecuali-perlu-kata-pemisahan .

Mereka membantu pengguna mengingat shell apa yang diperlukan untuk menjalankan file (katakanlah, setelah beberapa tahun tanpa menggunakan file).

Mereka benar-benar bukan untuk pengguna - mereka untuk sistem operasi untuk menjalankan penerjemah yang tepat. "Tepat" dalam hal ini, berarti OS menemukan apa yang ada di shebang; jika kode di bawahnya untuk shell yang salah - itu kesalahan penulis. Mengenai memori pengguna, saya rasa "pengguna" program seperti Microsoft Word atau Google Chrome tidak perlu mengetahui program bahasa apa yang ditulis; penulis mungkin perlu.

Kemudian lagi, skrip yang ditulis dengan mudah dapat menghilangkan kebutuhan untuk bahkan mengingat cangkang mana yang Anda gunakan awalnya, karena skrip portabel harus bekerja di semua cangkang yang sesuai dengan POSIX.

Mereka memastikan bahwa skrip berjalan hanya dengan shell tertentu (Bash dalam kasus itu) untuk mencegah perilaku tak terduga jika shell lain digunakan.

Mereka tidak melakukannya. Apa yang sebenarnya mencegah perilaku tak terduga adalah menulis skrip portabel.


1

Alasan mengapa Anda harus meletakkan #!/bin/bashdi bagian atas setiap skrip adalah karena Anda menjalankannya sebagai proses yang terpisah.

Ketika Anda menjalankan skrip sebagai proses baru, OS melihat bahwa itu adalah file teks (sebagai lawan dari file biner yang dapat dieksekusi), dan perlu tahu penerjemah apa yang harus dieksekusi. Jika Anda tidak memberi tahu, OS hanya dapat menebak, menggunakan pengaturan default (yang dapat diubah oleh administrator). Jadi #!baris (ditambah menambahkan izin yang dapat dieksekusi, tentu saja) mengubah file teks sederhana menjadi executable yang dapat dijalankan secara langsung, dengan keyakinan bahwa OS tahu apa yang harus dilakukan dengannya.

Jika Anda ingin menghapus #!garis, maka opsi Anda adalah:

  1. Jalankan skrip secara langsung (seperti yang Anda lakukan sekarang), dan berharap bahwa penerjemah default cocok dengan skrip - Anda mungkin aman di sebagian besar sistem Linux, tetapi tidak portabel untuk sistem lain (misalnya Solaris), dan dapat diubah menjadi apa saja (saya bahkan bisa mengaturnya agar berjalan vimdi file!).

  2. Jalankan skrip menggunakan bash myscript.sh. Ini berfungsi dengan baik, tetapi Anda harus menentukan path ke skrip, dan itu berantakan.

  3. Sumber menggunakan skrip . myscript.sh, yang menjalankan skrip dalam proses juru bahasa saat ini (yaitu bukan sebagai sub-proses). Tapi ini hampir pasti membutuhkan modifikasi pada skrip Anda, dan membuatnya kurang modular / dapat digunakan kembali.

Dalam kasus 2 dan 3, file tidak perlu (dan seharusnya tidak) menjalankan izin, dan memberikan .sh(atau .bash) akhiran adalah ide yang bagus.

Tetapi tidak satu pun dari opsi itu yang terlihat bagus untuk proyek Anda, seperti yang Anda katakan Anda ingin menjaga skrip Anda tetap modular (=> mandiri dan mandiri), jadi Anda harus menjaga #!/bin/bashgaris Anda di bagian atas skrip.

Dalam pertanyaan Anda, Anda juga mengatakan Anda mengikuti filosofi Unix. Jika itu benar, maka Anda harus mempelajari untuk apa #!garis itu sebenarnya, dan tetap menggunakannya di setiap skrip yang dapat dieksekusi!


Terima kasih atas jawaban yang bagus. Aku mencintai segala sesuatu dalam jawaban dan memikirkan upvoting sampai ini: then you should learn what the #! line is really for, and keep using it in every executable script!. Seseorang dapat mengikuti U. Filosofi hingga titik pengetahuan tertentu. Setelah semua jawaban ini saya mengerti mengapa itu bisa dimasukkan dalam istilah itu. Saya sarankan untuk mengedit jawaban, menggantinya dengan "Lihat shebang sebagai bagian internal dari Filsafat Unix yang Anda hormati dan adopsi".
user9303970

1
Maaf jika saya langsung, atau jika komentar itu tampak kasar. Komentar Anda menyarankan agar Anda lebih suka bekerja dalam IDE - dengan 'proyek' yang memungkinkan Anda menentukan aturan global untuk skrip dalam proyek itu. Itu cara yang valid untuk dikembangkan, tetapi tidak cocok dengan memperlakukan setiap skrip sebagai program mandiri dan mandiri (filosofi Unix 'yang Anda sebutkan).
Laurence Renshaw
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.