วิ่ง `exec 'ด้วย Bash ในตัว


9

ฉันกำหนดฟังก์ชั่นเปลือก (ขอเรียกว่าclock) ซึ่งผมต้องการที่จะใช้เป็นเสื้อคลุมกับคำสั่งอื่นที่คล้ายกับฟังก์ชั่นเช่นtimeclock ls -R

exec "$@"ฉันเปลือกทำหน้าที่งานบางส่วนแล้วปลายด้วย

ฉันต้องการให้ฟังก์ชั่นนี้ทำงานได้แม้มี shell-ins อยู่เช่นclock time ls -Rควรส่งออกผลลัพธ์ของการtimeบิวด์อินไม่ใช่การ/usr/bin/timeประมวลผล แต่execมักจะจบลงด้วยการรันคำสั่งแทน

ฉันจะทำให้ฟังก์ชั่น Bash ของฉันทำงานเป็น wrapper ที่ยอมรับเชลล์บิวด์อินเป็นอาร์กิวเมนต์ได้อย่างไร

แก้ไข : ฉันเพิ่งเรียนรู้ว่าtimeไม่ใช่ Bash ในตัว แต่เป็นคำสงวนพิเศษที่เกี่ยวข้องกับท่อ ฉันยังคงสนใจวิธีแก้ปัญหาสำหรับบิวด์อินแม้ว่าจะไม่สามารถใช้งานได้timeแต่โซลูชันทั่วไปที่ดีกว่าก็ดีกว่า


exec bash -c \' "$@" \'คุณจำเป็นต้องเรียกใช้เปลือกอย่างชัดเจนโดยใช้ ยกเว้นว่าคำสั่งของคุณในพารามิเตอร์แรกเป็นที่รู้จักในฐานะเชลล์สคริปต์จากนั้นคำสั่งนั้นจะถูกตีความว่าเป็นไบนารีที่จะเรียกใช้โดยตรง หรืออีกวิธีง่ายๆเพียงแค่พลาดexecและรับสาย"@"จากเชลล์เดิมของคุณ
AFH

คำตอบ:


9

คุณกำหนดฟังก์ชันทุบตี ดังนั้นคุณอยู่ใน bash shell แล้วเมื่อเรียกใช้ฟังก์ชันนั้น ดังนั้นฟังก์ชั่นนั้นก็ดูเหมือน:

clock(){
  echo "do something"
  $@
}

ฟังก์ชันนั้นสามารถถูกเรียกใช้ด้วย bash builtins, คำสงวนพิเศษ, คำสั่ง, ฟังก์ชันที่กำหนดอื่น ๆ :

นามแฝง:

$ clock type ls
do something
ls is aliased to `ls --color=auto'

Bash ในตัว:

$ clock type type
do something
type is a shell builtin

ฟังก์ชั่นอื่น:

$ clock clock
do something
do something

ปฏิบัติการได้:

$ clock date
do something
Tue Apr 21 14:11:59 CEST 2015

ในกรณีนี้มีความแตกต่างระหว่างการทำงาน$@และexec $@ถ้าฉันรู้ว่าฉันกำลังใช้คำสั่งจริง?
anol

3
เมื่อคุณใช้execคำสั่งจะแทนที่เชลล์ ดังนั้นมี builtins execve()ไม่มีชื่อแทนคำสงวนพิเศษคำที่กำหนดไว้อีกต่อไปเพราะปฏิบัติการที่จะดำเนินการผ่านทางสายระบบ syscall นั้นต้องการไฟล์ที่ปฏิบัติการได้
ความโกลาหล

แต่จากมุมมองของผู้สังเกตการณ์ภายนอกมันยังคงเป็นไปได้ที่จะแยกแยะพวกเขาเช่นexec $0มีกระบวนการเดียวในขณะที่$@ยังมีสอง
anol

4
ใช่$@มีเชลล์ที่รันเป็นพาเรนต์และคำสั่งเป็นกระบวนการชายด์ แต่เมื่อคุณต้องการใช้ builtins, aliases และอื่น ๆ คุณจะต้องรักษาเชลล์ไว้ หรือเริ่มใหม่
ความโกลาหล

4

วิธีเดียวที่จะเรียกใช้งานเชลล์บิวด์อินหรือคีย์เวิร์ดเชลล์คือเปิดเชลล์ใหม่เนื่องจาก exec“ แทนที่เชลล์ด้วยคำสั่งที่กำหนด” คุณควรแทนที่บรรทัดสุดท้ายด้วย:

IFS=' '; exec bash -c "$*"

ใช้ได้กับทั้งบิลด์และคำสงวน หลักการเหมือนกัน


3

หาก wrapper จำเป็นต้องแทรกรหัสก่อนคำสั่งที่กำหนดนามแฝงจะทำงานเมื่อขยายในระยะแรก:

alias clock="do this; do that;"

นามแฝงเกือบจะแทรกตัวอักษรในสถานที่ของคำ aliased ดังนั้นต่อท้าย;เป็นสิ่งสำคัญ - มันทำให้ขยายตัวออกไปclock time foodo this; do that; time foo

คุณสามารถละเมิดนี้เพื่อสร้างนามแฝงมายากลซึ่งแม้บายพาส quoting


สำหรับการแทรกโค้ดหลังจากคำสั่งคุณสามารถใช้ hook "DEBUG"

shopt -s extdebug
trap "<code>" DEBUG

โดยเฉพาะ:

shopt -s extdebug
trap 'if [[ $BASH_COMMAND == "clock "* ]]; then
          eval "${BASH_COMMAND#clock }"; echo "Whee!"; false
      fi' DEBUG

hook ยังคงรันอยู่ก่อนคำสั่ง แต่เมื่อมันส่งคืนfalseมันจะบอกให้ทุบตีเพื่อยกเลิกการประมวลผล (เพราะ hook นั้นได้รันผ่าน eval แล้ว)

เป็นอีกหนึ่งตัวอย่างที่คุณสามารถใช้เพื่อนามแฝงcommand pleaseเพื่อsudo command:

trap 'case $BASH_COMMAND in *\ please) eval sudo ${BASH_COMMAND% *}; false; esac' DEBUG

1

ทางออกเดียวที่ฉันสามารถทำได้ในตอนนี้คือการวิเคราะห์เคสเพื่อแยกแยะว่าอาร์กิวเมนต์แรกคือคำสั่งบิวด์อินหรือคีย์เวิร์ดและล้มเหลวในกรณีสุดท้าย:

#!/bin/bash

case $(type -t "$1") in
  file)
    # do stuff
    exec "$@"
    ;;
  builtin)
    # do stuff
    builtin "$@"
    ;;
  keyword)
    >&2 echo "error: cannot handle keywords: $1"
    exit 1
    ;;
  *)
    >&2 echo "error: unknown type: $1"
    exit 1
    ;;
esac

มันไม่ได้จัดการtimeแต่อาจมีทางออกที่ดีกว่า (และกระชับยิ่งขึ้น)

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