Chạy `exec` với Bash tích hợp


9

Tôi đã định nghĩa một hàm shell (hãy gọi nó clock), mà tôi muốn sử dụng như một trình bao bọc cho một lệnh khác, tương tự như timehàm, ví dụ clock ls -R.

Hàm shell của tôi thực hiện một số tác vụ và sau đó kết thúc bằng exec "$@".

Tôi muốn chức năng này hoạt động ngay cả với các phần tử dựng sẵn, ví dụ như clock time ls -Rsẽ xuất kết quả của phần tử tích timehợp chứ không phải phần /usr/bin/timethi hành. Nhưng execluôn luôn kết thúc việc chạy lệnh thay thế.

Làm cách nào tôi có thể làm cho hàm Bash của mình hoạt động như một trình bao bọc cũng chấp nhận các hàm dựng sẵn dưới dạng đối số?

Chỉnh sửa : Tôi mới biết rằng đó timekhông phải là một Bash tích hợp, mà là một từ dành riêng đặc biệt liên quan đến đường ống dẫn. Tôi vẫn quan tâm đến một giải pháp tích hợp ngay cả khi nó không hoạt động time, nhưng một giải pháp tổng quát hơn sẽ còn tốt hơn nữa.


Bạn cần phải gọi một vỏ rõ ràng, sử dụng exec bash -c \' "$@" \'. Trừ khi lệnh của bạn trong tham số đầu tiên được nhận dạng là tập lệnh shell, thì nó sẽ được hiểu là tệp nhị phân được chạy trực tiếp. Ngoài ra, và đơn giản hơn, chỉ cần bỏ lỡ execvà gọi "@"từ vỏ ban đầu của bạn.
AFH

Câu trả lời:


9

Bạn đã xác định một hàm bash. Vì vậy, bạn đã ở trong bash shell khi gọi hàm đó. Vì vậy, chức năng đó có thể trông đơn giản như sau:

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

Hàm đó có thể được gọi với các nội trang bash, các từ dành riêng đặc biệt, các lệnh, các hàm được xác định khác:

Bí danh:

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

Một bash dựng sẵn:

$ clock type type
do something
type is a shell builtin

Một chức năng khác:

$ clock clock
do something
do something

Một thực thi:

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

Trong trường hợp này, có sự khác biệt nào giữa việc chạy $@exec $@, nếu tôi biết tôi đang chạy một lệnh thực tế?
anol

3
Khi bạn sử dụng exec, lệnh thay thế shell. Do đó, không có nội dung, bí danh, từ dành riêng đặc biệt, từ xác định nữa, vì thực thi được thực thi thông qua cuộc gọi hệ thống execve(). Tòa nhà đó mong đợi một tập tin thực thi.
hỗn loạn

Nhưng theo quan điểm của một người quan sát bên ngoài, vẫn có thể phân biệt chúng, ví dụ như exec $0có một quá trình duy nhất, trong khi $@vẫn có hai.
anol

4
Có, $@có shell đang chạy là cha mẹ và lệnh là tiến trình con. Nhưng khi bạn muốn sử dụng nội dung, bí danh, v.v., bạn phải bảo toàn vỏ. Hoặc bắt đầu một cái mới.
hỗn loạn

4

Cách duy nhất để khởi chạy một từ khóa shell hoặc shell shell là khởi chạy một shell mới bởi vì exec exec thay thế shell bằng lệnh đã cho. Bạn nên thay thế dòng cuối cùng của bạn bằng:

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

Điều này hoạt động với cả nội dung và từ dành riêng; Nguyên tắc là như nhau.


3

Nếu trình bao bọc cần chèn mã trước lệnh đã cho, một bí danh sẽ hoạt động khi chúng được mở rộng ở giai đoạn rất sớm:

alias clock="do this; do that;"

Các bí danh gần như được chèn theo đúng nghĩa của từ bí danh, do đó, dấu vết ;rất quan trọng - nó làm cho clock time foomở rộng sang do this; do that; time foo.

Bạn có thể lạm dụng điều này để tạo ra các bí danh ma thuật thậm chí bỏ qua trích dẫn.


Để chèn mã sau một lệnh, bạn có thể sử dụng hook "DEBUG".

shopt -s extdebug
trap "<code>" DEBUG

Đặc biệt:

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

Hook vẫn chạy trước lệnh, nhưng khi nó trả về, falsenó báo bash hủy bỏ việc thực thi (vì hook đã chạy nó thông qua eval).

Một ví dụ khác, bạn có thể sử dụng để bí danh command pleaseđể sudo command:

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

1

Giải pháp duy nhất tôi có thể đưa ra cho đến nay là thực hiện phân tích trường hợp để phân biệt xem đối số đầu tiên là lệnh, tích hợp hoặc từ khóa và không thành công trong trường hợp cuối:

#!/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

timeTuy nhiên, nó không xử lý , vì vậy có thể có một giải pháp tốt hơn (và ngắn gọn hơn).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.