Mengapa skrip ini berfungsi di terminal tetapi tidak dari file?

19

Saya memiliki skrip shell ini disimpan dalam file: ia melakukan beberapa penggantian string dasar.

#!/bin/sh
html_file=$1
echo "html_file = $html_file"
substr=.pdf
pdf_file="${html_file/.html/.pdf}"
echo "pdf_file = $pdf_file"

Jika saya tempel ke baris perintah, itu berfungsi dengan baik:

$ html_file="/home/max/for_pauld/test_no_base64.html"
  echo "html_file = $html_file"
  substr=.pdf
  pdf_file="${html_file/.html/.pdf}"
  echo "pdf_file = $pdf_file"

memberi

html_file = /home/max/for_pauld/test_no_base64.html
pdf_file = /home/max/for_pauld/test_no_base64.pdf

Itulah output dari gema di atas - itu berfungsi sebagaimana dimaksud.

Tapi, ketika saya memanggil skrip, dengan

$ saucer "/home/max/for_pauld/test_no_base64.html"

Saya mendapatkan hasil ini:

html_file = /home/max/for_pauld/test_no_base64.html
/home/max/bin/saucer: 5: /home/max/bin/saucer: Bad substitution

Apakah skrip saya menggunakan versi bash yang berbeda atau apa? Apakah saya perlu mengubah jalur shebang saya?

Max Williams
sumber
6
Perluasan parameter itu adalah bashism (well, non-POSIX): ubah shebang Anda.
jasonwryan
Terima kasih! Itu berhasil. Saya pikir saya agak kabur tentang perbedaan antara shdan bash. Saya akan membacanya. Jika Anda bisa repot-repot membuat komentar Anda menjadi jawaban maka saya akan menandainya dengan benar.
Max Williams
Beri tanda centang pada The Duke untuk jawaban terincinya, tapi terima kasih.
Max Williams
2
@ MaxWilliams: Singkatnya, sh adalah shell bourne asli. Semua skrip yang kompatibel harus ditulis untuk sh. Tetapi banyak cangkang selanjutnya (mis: bash, cangkang Bourne Again) "hampir kompatibel" dan menambah banyak kebaikan tambahan. Seperti substitusi yang Anda gunakan. Saat ini, Anda cukup aman hanya menggunakan fitur posix, tetapi perlu diketahui bahwa portabilitas tergantung pada set yang lebih sempit (yaitu, sh saja). Jadi secara umum, gunakan: #!/usr/bin/env bashsebagai shebang Anda, dan gunakan substitusi yang sudah pasti seperti yang Anda inginkan, tetapi dengan peringatan portabilitas. Dan baca: unix.stackexchange.com/a/48787/27616
Olivier Dulac

Jawaban:

37

Apa itu sh

sh(atau Bahasa Perintah Shell) adalah bahasa pemrograman yang dijelaskan oleh standar POSIX . Ini memiliki banyak implementasi ( ksh88, dash, ...). bashjuga dapat dianggap sebagai implementasi sh(lihat di bawah).

Karena shini adalah spesifikasi, bukan implementasi, /bin/shadalah symlink (atau hard link) untuk implementasi aktual pada sebagian besar sistem POSIX.

Apa itu bash

bashdimulai sebagai shimplementasi -kompatibel (meskipun ia mendahului standar POSIX beberapa tahun), tetapi seiring berjalannya waktu ia telah memperoleh banyak ekstensi. Banyak dari ekstensi ini dapat mengubah perilaku skrip POSIX shell yang valid, jadi dengan sendirinya bashbukan shell POSIX yang valid. Sebaliknya, ini adalah dialek bahasa shell POSIX.

bashmendukung --posixswitch, yang membuatnya lebih kompatibel dengan POSIX. Itu juga mencoba untuk meniru POSIX jika dipanggil sebagai sh.

sh = bash?

Untuk waktu yang lama, /bin/shdigunakan untuk menunjuk /bin/bashpada sebagian besar sistem GNU / Linux. Akibatnya, hampir menjadi aman untuk mengabaikan perbedaan antara keduanya. Tapi itu mulai berubah baru-baru ini.

Beberapa contoh populer sistem di mana /bin/shtidak menunjuk /bin/bash(dan beberapa di antaranya /bin/bashbahkan mungkin tidak ada) adalah:

  1. Modern Debian dan Ubuntu sistem, yang symlink shuntuk dashsecara default;
  2. Busybox , yang biasanya dijalankan selama waktu boot sistem Linux sebagai bagian dari initramfs. Ia menggunakan ashimplementasi shell.
  3. BSD, dan secara umum sistem non-Linux. OpenBSD menggunakan pdksh, keturunan dari shell Korn. FreeBSD's shadalah turunan dari shell UNIX Bourne yang asli. Solaris memiliki sendiri shyang untuk waktu yang lama tidak sesuai dengan POSIX; implementasi gratis tersedia dari proyek Heirloom .

Bagaimana Anda bisa mengetahui /bin/shpoin apa yang ada di sistem Anda?

Kerumitannya /bin/shbisa berupa tautan simbolik atau tautan keras. Jika itu tautan simbolis, cara portabel untuk mengatasinya adalah:

% file -h /bin/sh
/bin/sh: symbolic link to bash

Jika ini tautan keras, coba

% find -L /bin -samefile /bin/sh
/bin/sh
/bin/bash

Faktanya, -Lflag tersebut mencakup symlink dan hardlink, tetapi kelemahan dari metode ini adalah tidak portable - POSIX tidak perlu find mendukung -samefileopsi, meskipun GNU menemukan dan FreeBSD menemukan mendukungnya.

Garis Shebang

Pada akhirnya, terserah Anda untuk memutuskan mana yang akan digunakan, dengan menulis baris «shebang».

Misalnya

#!/bin/sh

akan menggunakan sh(dan apa pun yang terjadi menunjuk ke),

#!/bin/bash

akan digunakan /bin/bashjika tersedia (dan gagal dengan pesan kesalahan jika tidak). Tentu saja, Anda juga dapat menentukan implementasi lain, mis

#!/bin/dash

Yang mana yang akan digunakan

Untuk skrip saya sendiri, saya lebih suka shkarena alasan berikut:

  • itu standar
  • itu jauh lebih sederhana dan lebih mudah dipelajari
  • itu portabel di seluruh sistem POSIX - bahkan jika mereka tidak memilikinya bash, mereka diharuskan memilikinyash

Ada keuntungan menggunakan bashjuga. Fitur-fiturnya membuat pemrograman lebih mudah dan mirip dengan pemrograman dalam bahasa pemrograman modern lainnya. Ini termasuk hal-hal seperti scoping variabel lokal dan array. Plain shadalah bahasa pemrograman yang sangat minimalis.

Pemburu. Thompson
sumber
Terima kasih atas jawaban terinci: Saya menggunakan Linux Mint yang berbasis Debian dan /bin/shmemang merupakan symlink ke /bin/dash.
Max Williams
Jika Anda ingin mematuhi POSIX sh, maka sebaiknya tinggalkan shebang sepenuhnya: Jika baris pertama dari file perintah shell dimulai dengan karakter "#!", Hasilnya adalah pubs.opengroup.org/onlinepubs/009695399/ yang
Daniel Jour
4
@DanielJour Jika ada sistem mirip Unix yang menjatuhkan dukungan biasa untuk shebang, itu akan merusak banyak hal sehingga secara praktis tidak dapat digunakan. Jadi ini adalah standar de facto, bahkan jika itu tidak ditentukan dalam POSIX. Satu-satunya masalah kompatibilitas yang sebenarnya adalah bahwa jalur menuju juru bahasa mungkin berbeda.
Barmar
23

Menambahkan ke jawaban yang sangat baik dari @ Hunter.S.Thompson Saya ingin menunjukkan bahwa bagian non-portabel dari skrip adalah

pdf_file="${html_file/.html/.pdf}"

Ini ${variable/search/replace}adalah ekstensi GNU. Tetapi Anda dapat dengan mudah menghindarinya dengan POSIX murni:

pdf_file="${html_file%.html}".pdf

Mengikuti Hunter, ini adalah perbaikan yang lebih baik daripada mengubah shebang menjadi #! /bin/bash

Filipos
sumber
Terima kasih - Saya setuju bahwa ini adalah perbaikan "murni" tapi saya lebih suka bash shebang dimuat, karena bash adalah apa yang saya gunakan secara rutin di terminal (secara default) dan lebih mudah untuk memiliki skrip hanya melakukan hal yang sama seperti biasa garis komando.
Max Williams
1
@ MaxWilliams Tentu saja, tidak ada masalah. Ini terutama ditujukan untuk basis data T&J.
Philippos