การพิมพ์/usr/bin/env sed -f
งานเทอร์มินัล
แต่ถ้าใช้เป็น Shebang
#!/usr/bin/env sed -f
s/a/b/
สคริปต์จะล้มเหลวในการดำเนินการ:
/usr/bin/env: sed -f: No such file or directory
ฉันเชื่อว่ามันเกี่ยวข้องกับ -f แต่จะแก้ไขได้อย่างไร?
การพิมพ์/usr/bin/env sed -f
งานเทอร์มินัล
แต่ถ้าใช้เป็น Shebang
#!/usr/bin/env sed -f
s/a/b/
สคริปต์จะล้มเหลวในการดำเนินการ:
/usr/bin/env: sed -f: No such file or directory
ฉันเชื่อว่ามันเกี่ยวข้องกับ -f แต่จะแก้ไขได้อย่างไร?
คำตอบ:
คุณไม่สามารถพกพาวางอาร์กิวเมนต์มากกว่าหนึ่งตัวในหนึ่ง#!
บรรทัดได้ นั่นหมายถึงเส้นทางแบบเต็มและอาร์กิวเมนต์เดียว (เช่น#!/bin/sed -f
หรือ#!/usr/bin/sed -f
) หรือ#!/usr/bin/env
ไม่มีอาร์กิวเมนต์สำหรับผู้แปล
วิธีหลีกเลี่ยงปัญหาในการรับสคริปต์แบบพกพาคือการใช้#!/bin/sh
และ shell wrapper โดยส่งผ่านสคริปต์ sed เป็นอาร์กิวเมนต์บรรทัดคำสั่ง โปรดทราบว่านี่ไม่ใช่ทำนองคลองธรรมโดย POSIX (สคริปต์หลายคำสั่งจะต้องเขียนด้วย-e
อาร์กิวเมนต์แยกต่างหากสำหรับแต่ละคำสั่งเพื่อความสะดวกในการพกพา) แต่มันทำงานได้กับการใช้งานหลายอย่าง
#!/bin/sh
exec sed '
s/a/b/
' "$@"
สำหรับสคริปต์ที่ยาวอาจจะสะดวกกว่าถ้าใช้ heredoc ข้อดีของ heredoc คือคุณไม่จำเป็นต้องพูดคำพูดเดียวภายในถ้ามี ข้อเสียที่สำคัญคือสคริปต์ถูกป้อนเข้ากับมาตรฐานอินพุตพร้อมกับผลที่น่ารำคาญสองประการ sed บางรุ่นต้องการ-f /dev/stdin
แทน-f -
ซึ่งเป็นปัญหาสำหรับการพกพา ยิ่งไปกว่านั้นสคริปต์ไม่สามารถทำหน้าที่เป็นตัวกรองได้เนื่องจากอินพุตมาตรฐานคือสคริปต์และไม่สามารถเป็นข้อมูลได้
#!/bin/sh
exec sed -f - -- "$@" <<'EOF'
s/a/b/
EOF
ข้อเสียของการ heredoc cat
สามารถแก้ไขได้โดยการใช้งานของ เนื่องจากสิ่งนี้ทำให้สคริปต์ทั้งหมดบนบรรทัดคำสั่งอีกครั้งจึงไม่สอดคล้องกับ POSIX แต่ส่วนใหญ่พกพาได้ในทางปฏิบัติ
#!/bin/sh
exec sed "$(cat <<'EOF')" -- "$@"
s/a/b/
EOF
วิธีแก้ปัญหาอื่นคือการเขียนสคริปต์ที่สามารถแยกวิเคราะห์ทั้งโดย sh และโดย sed นี้เป็นแบบพกพาที่มีประสิทธิภาพพอสมควรเพียงเล็กน้อยที่น่าเกลียด
#! /bin/sh
b ()
{
x
}
i\
f true; then exec sed -f "$0" "$@"; fi
: ()
# sed script starts here
s/a/b/
คำอธิบาย:
b
; เนื้อหาไม่สำคัญตราบเท่าที่ฟังก์ชั่นนั้นมีรูปแบบที่ดี (โดยเฉพาะคุณไม่สามารถมีฟังก์ชั่นที่ว่างเปล่า) ถ้าเป็นจริงให้ดำเนินการsed
กับสคริปต์()
ป้ายกำกับ จากนั้นi
คำสั่งซึ่งไม่มีผลกระทบเพราะถูกข้ามไปเสมอ ในที่สุด()
ฉลากตามด้วยส่วนที่มีประโยชน์ของสคริปต์#!/usr/bin/env
ไม่โต้แย้ง" ประโยคไม่ดีมาก อาจจะ "หรือ#!/usr/bin/env sed
ไม่ก็โต้เถียงกันก็ได้"
มีการใช้งานที่ไม่เข้ากันของ shebang (#!) ขึ้นอยู่กับระบบปฏิบัติการ บางรายการกำลังสร้างรายการอาร์กิวเมนต์แบบเต็มบางรายการกำลังเก็บรักษาพา ธ คำสั่งและใส่อาร์กิวเมนต์ที่เหลือทั้งหมดเป็นรายการเดียวบางรายการจะละเว้นอาร์กิวเมนต์ทั้งหมดและส่งผ่านเฉพาะเส้นทางคำสั่งเท่านั้นและสุดท้ายบางรายการจะผ่านสตริงทั้งหมดเป็นรายการเดียว คำสั่ง คุณดูเหมือนจะเป็นในกรณีหลัง
env พยายามค้นหาไฟล์ที่มีชื่อ "sed -f" คุณสามารถลอง "#! / usr / bin / sed -f" เป็นบรรทัด shebang ของคุณ
ในฐานะของ GNU coreutils v8.30คุณสามารถทำได้:
#!/usr/bin/env -S sed -f
คุณลักษณะนี้ถูกเพิ่มเข้ามาในเร็ว ๆ นี้ (2018/04/20) กระทำการenv.c
ในแพคเกจ GNU coreutils ซึ่งเพิ่ม-S
หรือ--split-string
ตัวเลือก
จากenv
หน้าคน:
OPTIONS
-S/--split-string usage in scripts
The -S option allows specifing multiple parameters in a script.
Running a script named 1.pl containing the following first line:
#!/usr/bin/env -S perl -w -T
Will execute perl -w -T 1.pl .
Without the '-S' parameter the script will likely fail with:
/usr/bin/env: 'perl -w -T': No such file or directory
See the full documentation for more details.
ตัวอย่างอื่น ๆ ที่มีอยู่ในGNU coreutils คู่มือ
หากคุณใช้-v
ตัวเลือกสำหรับเอาต์พุต verbose คุณสามารถดูวิธี
env
แยกสตริงของอาร์กิวเมนต์:
ในmy_sed_script.sed
:
#!/usr/bin/env -vS sed -f
s/a/b/
การดำเนินการ:
$ ./my_sed_script.sed
split -S: ‘sed -f’
into: ‘sed’
& ‘-f’
executing: sed
arg[0]= ‘sed’
arg[1]= ‘-f’
arg[2]= ‘./my_sed_script.sed’
หมายเหตุ:สิ่งนี้ใช้กับ shebangs ที่ใช้/usr/bin/env
เท่านั้นเนื่องจาก
--split-string
เป็นคุณลักษณะของ GNU env
โดยเฉพาะ
คำตอบนี้ให้เส้นทางสู่โซลูชันที่หรูหรา: /programming//a/1655389/642372
read
heredoc เป็นตัวแปรเชลล์sed
ผ่านตัวแปรที่เป็นอาร์กิวเมนต์ตำแหน่งไปตัวอย่าง:
#!/usr/bin/env bash
read -rd '' SED_SCRIPT <<EOD
# Your sed script goes here.
s/a/b/
EOD
exec sed "$SED_SCRIPT" "$@"
ฉันชอบโซลูชันของวอล์คเกอร์ แต่สามารถปรับปรุงได้ (ควรใส่คำตอบแยกต่างหากเนื่องจากความคิดเห็นไม่ยอมรับข้อความที่ฟอร์แมตแล้ว) สิ่งนี้อาจใช้ไม่ได้กับ Linux หรือ Bash ทุกรุ่น แต่บน Ubuntu 17.10 คุณสามารถลดสิ่งต่อไปนี้ได้:
#!/usr/bin/env bash
read SED_SCRIPT <<EOD
s/a/b/
EOD
sed "$SED_SCRIPT" "$@"
คุณสามารถลบeval
และทำให้read
คำสั่งง่ายขึ้นแต่คุณต้องกำจัดความคิดเห็นที่อยู่ใน heredoc
นอกจากนี้สำหรับทุกสิ่งที่คุณอยากรู้เกี่ยวกับ sed แต่ก็กลัวที่จะถามมีการกวดวิชาที่มีประโยชน์มากที่http://www.grymoire.com/unix/sed.html สิ่งนี้ชี้ให้เห็นทางออกที่ดียิ่งขึ้นโดยเสียค่าใช้จ่ายในการสูญเสีย/usr/bin/env
และเข้ารหัสฮาร์ดไดรฟ์ไปยัง:
#!/bin/sed -f
s/a/b/
s/c/d/
นี่เป็นข้อดีเพิ่มเติมของการสนับสนุนการแทนที่มากกว่าหนึ่งs///
รายการ