เหตุใดสคริปต์นี้จึงทำงานในเทอร์มินัล แต่ไม่ใช่จากไฟล์


19

ฉันได้บันทึกเชลล์สคริปต์ไว้ในไฟล์แล้วมันจะทำการทดแทนสตริงพื้นฐาน

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

ถ้าฉันวางลงในบรรทัดคำสั่งก็ใช้งานได้:

$ 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"

จะช่วยให้

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

นั่นเป็นผลลัพธ์จากเสียงสะท้อนด้านบน - มันทำงานได้ตามที่ตั้งใจไว้

แต่เมื่อฉันเรียกสคริปต์ด้วย

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

ฉันได้ผลลัพธ์นี้:

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

สคริปต์ของฉันใช้ bash รุ่นอื่นหรือบางอย่าง ฉันจำเป็นต้องเปลี่ยนสายหนังของฉันหรือไม่?


6
การขยายพารามิเตอร์นั้นเป็นความนิยม (ไม่ใช่ POSIX): เปลี่ยน shebang ของคุณ
jasonwryan

ขอบคุณ! ที่ได้ผล ฉันคิดว่าฉันหมอกบิตบนความแตกต่างระหว่างและsh bashฉันจะอ่านมัน หากคุณสามารถใส่ใจที่จะแสดงความคิดเห็นของคุณเป็นคำตอบแล้วฉันจะทำเครื่องหมายว่าถูกต้อง
Max Williams

มอบติ๊กให้ The Duke เพื่อรับคำตอบโดยละเอียด แต่ขอบคุณ
Max Williams

2
@ MaxWilliams: สรุปสั้น ๆ sh คือเชลล์ bourne ดั้งเดิม ควรเขียนสคริปต์ที่เข้ากันได้ทั้งหมดสำหรับ sh แต่เชลล์ที่ตามมาจำนวนมาก (เช่นทุบตีบอร์นเชลล์อีกครั้ง) เป็น "เกือบเข้ากันได้" และเพิ่มความพิเศษมากมาย เช่นการทดแทนที่คุณใช้ ทุกวันนี้คุณค่อนข้างปลอดภัยในการใช้ฟีเจอร์ posix เท่านั้น แต่โปรดทราบว่าการพกพานั้นขึ้นอยู่กับชุดที่แคบกว่า (เช่น SH เท่านั้น) ดังนั้นโดยทั่วไปให้ใช้: #!/usr/bin/env bashเป็น Shebang ของคุณและใช้การแทนที่ที่กำหนดไว้อย่างที่คุณต้องการ แต่ด้วยความสามารถในการพกพา และอ่าน: unix.stackexchange.com/a/48787/27616
Olivier Dulac

คำตอบ:


37

SH คืออะไร

sh(หรือ Shell Command Language) เป็นภาษาโปรแกรมที่อธิบายโดยมาตรฐาน POSIX แต่ก็มีการใช้งานจำนวนมาก ( ksh88, dash, ... ) bashสามารถพิจารณาการดำเนินการของsh(ดูด้านล่าง)

เนื่องจากshเป็นข้อมูลจำเพาะไม่ใช่การนำไปใช้/bin/shเป็น symlink (หรือฮาร์ดลิงก์) เพื่อนำไปใช้งานจริงในระบบ POSIX ส่วนใหญ่

ทุบตีคืออะไร

bashเริ่มต้นเป็น - shใช้งานร่วมกันได้ (แม้ว่าจะลงวันที่ก่อนหน้ามาตรฐาน POSIX โดยไม่กี่ปี) แต่เมื่อเวลาผ่านไปมันได้รับส่วนขยายจำนวนมาก ส่วนขยายเหล่านี้จำนวนมากอาจเปลี่ยนพฤติกรรมของสคริปต์เชลล์ POSIX ที่ถูกต้องดังนั้นโดยตัวมันเองbashจะไม่เป็น POSIX เชลล์ แต่เป็นภาษาถิ่นของภาษาเชลล์ POSIX

bashรองรับ--posixสวิตช์ซึ่งทำให้เป็นไปตาม POSIX ได้มากขึ้น นอกจากนี้ยังพยายามที่จะเลียนแบบ POSIX shถ้าเรียกว่าเป็น

sh = ทุบตี?

เป็นเวลานานที่/bin/shจะใช้ชี้ไป/bin/bashที่ระบบ GNU / Linux ส่วนใหญ่ เป็นผลให้มันเกือบจะปลอดภัยที่จะเพิกเฉยความแตกต่างระหว่างทั้งสอง แต่นั่นเริ่มเปลี่ยนไปเมื่อเร็ว ๆ นี้

ตัวอย่างที่นิยมของระบบที่/bin/shไม่ได้ชี้ไปที่/bin/bash(และในบางระบบ/bin/bashอาจไม่มีอยู่) คือ:

  1. โมเดิร์น Debian และ Ubuntu ระบบซึ่ง symlink shไปdashโดยค่าเริ่มต้น
  2. Busyboxinitramfsซึ่งดำเนินการปกติในช่วงเวลาการบูตระบบลินุกซ์เป็นส่วนหนึ่งของ มันใช้การใช้ashเปลือก
  3. BSDs และโดยทั่วไประบบที่ไม่ใช่ Linux OpenBSD ใช้pdkshผู้สืบทอดของ Korn เชลล์ FreeBSD's shเป็นผู้สืบทอดของเชลล์ UNIX Bourne ดั้งเดิม Solaris มีของตัวเองshซึ่งเป็นเวลานานไม่เป็นไปตาม POSIX; การดำเนินงานที่สามารถใช้ได้ฟรีจากโครงการ Heirloom

คุณจะทราบได้อย่างไรว่า/bin/shระบบของคุณชี้ไปที่ใด?

ภาวะแทรกซ้อนคือ/bin/shอาจเป็นลิงก์สัญลักษณ์หรือลิงก์ยาก หากเป็นลิงก์สัญลักษณ์วิธีพกพาในการแก้ไขคือ:

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

หากเป็นการเชื่อมโยงที่ยากลอง

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

ในความเป็นจริงการ-Lตั้งค่าสถานะครอบคลุมทั้ง symlinks และ hardlinks แต่ข้อเสียของวิธีนี้คือมันไม่สามารถพกพาได้ - POSIX ไม่ต้องการ findการสนับสนุน-samefileตัวเลือกแม้ว่าGNU findและFreeBSD จะพบการสนับสนุน

เส้น Shebang

ในที่สุดก็ขึ้นอยู่กับคุณที่จะตัดสินใจว่าจะใช้อันไหนโดยเขียนบรรทัด« shebang »

เช่น

#!/bin/sh

จะใช้sh(และอะไรก็ตามที่เกิดขึ้นกับจุด)

#!/bin/bash

จะใช้/bin/bashถ้ามันมีอยู่ (และล้มเหลวพร้อมกับข้อความแสดงข้อผิดพลาดหากไม่มี) แน่นอนคุณสามารถระบุการนำไปใช้อื่นเช่น

#!/bin/dash

ใช้อันไหนดี

สำหรับสคริปต์ของฉันเองฉันชอบshด้วยเหตุผลดังต่อไปนี้:

  • มันเป็นมาตรฐาน
  • มันง่ายกว่ามากและง่ายต่อการเรียนรู้
  • มันเป็นแบบพกพาในระบบ POSIX - แม้ว่าพวกเขาจะไม่เกิดขึ้นbashพวกเขาจะต้องมีsh

มีข้อดีในการใช้bashเช่นกัน คุณสมบัติต่างๆทำให้การเขียนโปรแกรมสะดวกและคล้ายกับการเขียนโปรแกรมในภาษาการเขียนโปรแกรมสมัยใหม่อื่น ๆ สิ่งเหล่านี้รวมถึงสิ่งต่าง ๆ เช่นตัวแปรและอาร์เรย์ในตัวเครื่อง ธรรมดาshเป็นภาษาโปรแกรมเรียบง่ายมาก


ขอบคุณสำหรับคำตอบรายละเอียด: ผมใช้ลินุกซ์มิ้นท์ซึ่งเป็น Debian-based และ/bin/shเป็นที่แน่นอน symlink /bin/dashไป
Max Williams

หากคุณต้องการปฏิบัติตาม POSIX sh ดังนั้นควรเว้น shebang ให้สมบูรณ์ที่สุด: หากบรรทัดแรกของไฟล์คำสั่งเชลล์เริ่มต้นด้วยอักขระ "#!" ผลลัพธ์จะไม่ถูกระบุ pubs.opengroup.org/onlinepubs/009695399/ Utilities / …
Daniel Jour

4
@DanielJour หากระบบ Unix ที่เหมือนกันหลุดการสนับสนุนโดยทั่วไปสำหรับ shebangs มันจะทำลายหลายสิ่งหลายอย่างมันจะไม่สามารถใช้งานได้จริง ดังนั้นจึงเป็นมาตรฐานตามความเป็นจริงแม้ว่าจะไม่ได้ระบุใน POSIX ปัญหาความเข้ากันได้จริงเท่านั้นคือเส้นทางไปยังล่ามอาจแตกต่างกัน
Barmar

23

การเพิ่มคำตอบที่ยอดเยี่ยมจาก @ Hunter.Sompson ฉันต้องการจะชี้ให้เห็นว่าส่วนที่ไม่ใช่แบบพกพาของสคริปต์คือ

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

นี่${variable/search/replace}คือส่วนขยาย GNU แต่คุณสามารถหลีกเลี่ยงได้อย่างง่ายดายด้วย POSIX บริสุทธิ์:

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

การติดตามฮันเตอร์นี่เป็นการแก้ไขที่ดีกว่าการเปลี่ยน shebang เป็น #! /bin/bash


ขอบคุณ - ฉันยอมรับว่ามันเป็นการแก้ไข "purer" แต่ฉันต้องการ shebang ที่โหลด bash เนื่องจาก bash เป็นสิ่งที่ฉันใช้เป็นประจำในเทอร์มินัล (โดยค่าเริ่มต้น) และง่ายกว่าที่จะให้สคริปต์ทำสิ่งเดียวกันตามปกติ บรรทัดคำสั่ง.
Max Williams

1
@ MaxWilliams แน่นอนไม่มีปัญหา นี่คือส่วนใหญ่ indended สำหรับคำถาม & คำตอบฐานข้อมูล
Philippos
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.