2008-12-09 20 views
6

Câu hỏi:câu hỏi/usr/bin/env về pecularities dòng công việc

  • kernel gì phải làm gì nếu bạn dính vào một vỏ kịch bản vào dòng công việc?
  • Hạt nhân biết trình thông dịch nào sẽ khởi chạy?

Giải thích:

Gần đây tôi đã muốn viết một wrapper xung quanh /usr/bin/env vì Môi trường CGI của tôi không cho phép tôi đặt biến PATH, ngoại trừ trên toàn cầu (trong đó tất nhiên là hút!).

Vì vậy, tôi nghĩ, "OK. Hãy đặt PREPENDPATH và đặt PATH trong trình bao bọc xung quanh env.". Các kịch bản kết quả (ở đây gọi là env.1) trông như thế này:

#!/bin/bash 
/usr/bin/env PATH=$PREPENDPATH:$PATH $* 

mà hình như nó nên làm việc. Tôi đã kiểm tra như thế nào cả hai đều phản ứng, sau khi cài đặt PREPENDPATH:

$ which /usr/bin/env python 
/usr/bin/env 
/usr/bin/python 

$ which /usr/bin/env.1 python 
/usr/bin/env 
/home/pi/prepend/bin/python 

Nhìn hoàn toàn hoàn hảo! Càng xa càng tốt. Nhưng hãy xem những gì xảy ra với "Hello World!".

# Shebang is #!/usr/bin/env python 
$ test-env.py 
Hello World! 

# Shebang is #!/usr/bin/env.1 python 
$ test-env.1.py 
Warning: unknown mime-type for "Hello World!" -- using "application/*" 
Error: no such file "Hello World!" 

Tôi đoán tôi thiếu một điều gì đó khá cơ bản về UNIX.

Tôi khá lạc mất, ngay cả sau khi xem mã nguồn của env gốc. Nó đặt môi trường và khởi chạy chương trình (hoặc có vẻ như với tôi ...).

Trả lời

6

Trước hết, bạn nên rất ít khi sử dụng $* và bạn hầu như luôn luôn sử dụng "[email protected]" để thay thế. Có một số câu hỏi ở đây về SO, giải thích về các lý do tại sao.

Thứ hai - lệnh env có hai cách sử dụng chính. Một là in môi trường hiện tại; khác là hoàn toàn kiểm soát môi trường của một lệnh khi nó được chạy. Việc sử dụng thứ ba, mà bạn đang thể hiện, là để sửa đổi môi trường, nhưng thẳng thắn không có nhu cầu cho điều đó - các vỏ có khả năng xử lý điều đó cho bạn.

Chế độ 1:

env 

Chế độ 2:

env -i HOME=$HOME PATH=$PREPENDPATH:$PATH ... command args 

Phiên bản này hủy bỏ tất cả các biến môi trường kế thừa và chạy command với chính môi trường được thiết lập bởi các tùy chọn ENVVAR = giá trị.

Chế độ thứ ba - sửa đổi môi trường - ít quan trọng hơn vì bạn có thể làm điều đó tốt với vỏ thông thường (văn minh). (. Điều đó có nghĩa "không C shell" - một lần nữa, có những câu hỏi khác về SO với câu trả lời giải thích đó) Ví dụ, bạn hoàn toàn tốt có thể làm:

#!/bin/bash 
export PATH=${PREPENDPATH:?}:$PATH 
exec python "[email protected]" 

này nhấn mạnh rằng $PREPENDPATH được đặt thành một phi chuỗi rỗng trong môi trường, sau đó thêm nó vào $PATH và xuất cài đặt PATH mới. Sau đó, sử dụng PATH mới, nó thực thi chương trình python với các đối số có liên quan. Các exec thay thế kịch bản shell với python. Lưu ý rằng điều này hoàn toàn khác với:

#!/bin/bash 
PATH=${PREPENDPATH:?}:$PATH exec python "[email protected]" 

Bề ngoài, điều này giống nhau. Tuy nhiên, điều này sẽ thực thi python được tìm thấy trên PATH đã tồn tại trước đó, mặc dù với giá trị mới của PATH trong môi trường của quá trình. Vì vậy, trong ví dụ, bạn sẽ kết thúc thực hiện Python từ /usr/bin và không phải là một từ /home/pi/prepend/bin.

Trong trường hợp của bạn, tôi có thể không sử dụng env và sẽ chỉ sử dụng một biến thể phù hợp của tập lệnh với xuất rõ ràng.

Lệnh env không bình thường vì lệnh này không nhận dạng dấu gạch ngang kép để tách các tùy chọn khỏi phần còn lại của lệnh. Điều này một phần vì nó không có nhiều lựa chọn, và một phần vì không rõ liệu các tùy chọn ENVVAR = value có đến trước hoặc sau dấu gạch ngang kép hay không.

Tôi thực sự có một loạt các tập lệnh để chạy (các phiên bản khác nhau) của một máy chủ cơ sở dữ liệu. Những kịch bản thực sự sử dụng env (và một loạt các chương trình cây nhà lá vườn) để kiểm soát môi trường của máy chủ:

#!/bin/ksh 
# 
# @(#)$Id: boot.black_19.sh,v 1.3 2008/06/25 15:44:44 jleffler Exp $ 
# 
# Boot server black_19 - IDS 11.50.FC1 

IXD=/usr/informix/11.50.FC1 
IXS=black_19 
cd $IXD || exit 1 

IXF=$IXD/do.not.start.$IXS 
if [ -f $IXF ] 
then 
    echo "$0: will not start server $IXS because file $IXF exists" 1>&2 
    exit 1 
fi 

ONINIT=$IXD/bin/oninit.$IXS 
if [ ! -f $ONINIT ] 
then ONINIT=$IXD/bin/oninit 
fi 

tmpdir=$IXD/tmp 
DAEMONIZE=/work1/jleffler/bin/daemonize 
stdout=$tmpdir/$IXS.stdout 
stderr=$tmpdir/$IXS.stderr 

if [ ! -d $tmpdir ] 
then asroot -u informix -g informix -C -- mkdir -p $tmpdir 
fi 

# Specialized programs carried to extremes: 
# * asroot sets UID and GID values and then executes 
# * env, which sets the environment precisely and then executes 
# * daemonize, which makes the process into a daemon and then executes 
# * oninit, which is what we really wanted to run in the first place! 
# NB: daemonize defaults stdin to /dev/null and could set umask but 
#  oninit dinks with it all the time so there is no real point. 
# NB: daemonize should not be necessary, but oninit doesn't close its 
#  controlling terminal and therefore causes cron-jobs that restart 
#  it to hang, and interactive shells that started it to hang, and 
#  tracing programs. 
# ??? Anyone want to integrate truss into this sequence? 

asroot -u informix -g informix -C -a dbaao -a dbsso -- \ 
    env -i HOME=$IXD \ 
     INFORMIXDIR=$IXD \ 
     INFORMIXSERVER=$IXS \ 
     INFORMIXCONCSMCFG=$IXD/etc/concsm.$IXS \ 
     IFX_LISTEN_TIMEOUT=3 \ 
     ONCONFIG=onconfig.$IXS \ 
     PATH=/usr/bin:$IXD/bin \ 
     SHELL=/usr/bin/ksh \ 
     TZ=UTC0 \ 
    $DAEMONIZE -act -d $IXD -o $stdout -e $stderr -- \ 
    $ONINIT "[email protected]" 

case "$*" in 
(*v*) track-oninit-v $stdout;; 
esac 
+0

Đây là thông tin hữu ích, nhưng như OP nói,/kernel của mình sẽ không chạy không các tệp nhị phân (chẳng hạn như các bash stubs mà bạn cung cấp) làm phần tử đầu tiên của một dòng shebang. Mỏ sẽ không; cụ thể, hạt nhân âm thầm không chạy kịch bản với dòng shebang có vấn đề trong trình thông dịch mong muốn (không nhị phân), và sau đó trình bao của tôi sẽ cố gắng chạy tập lệnh thay thế. (Understading của tôi là đây là một hành vi vỏ cũ thời gian mà nhiều vỏ giữ lại.) – dubiousjim

+0

Tôi không hiểu những gì bạn đang nói? Bạn đang tuyên bố rằng có (vẫn) shell hoặc hạt nhân sẽ không xử lý '#!/Bin/bash' hoặc' #!/Bin/ksh' làm dòng đầu tiên của tập lệnh? Tôi không tuyên bố hoặc cho thấy rằng bạn có thể sử dụng '#!/Some/script' như một shebang. –

+0

Không xác nhận quyền sở hữu đầu tiên. Câu hỏi của OP là về câu hỏi thứ hai, mặc dù: anh ta/cô ấy hỏi "Hạt nhân sẽ làm gì nếu bạn gắn một kịch bản lệnh shell vào dòng shebang?" và sau đó báo cáo các vấn đề phát sinh từ việc sử dụng env.1 như một dòng shebang, trong đó env.1 là một tập lệnh. (Khi nó xảy ra, nó đang tìm kiếm các cuộc thảo luận về những hạn chế về các dòng shebang mang tôi đến đây. Và tôi đã tìm thấy bài đăng của bạn có thông tin hữu ích, vì vậy cảm ơn vì đã đóng góp nó. rằng những gì bạn thảo luận không giúp vượt qua giới hạn mà OP đã gặp phải.) – dubiousjim

4

Bạn nên đọc kỹ bài viết wikipedia về shebang.

Khi hệ thống của bạn thấy số ma thuật tương ứng với shebang, nó thực hiện execve trên đường dẫn đã cho sau shebang và cung cấp cho tập lệnh đó làm đối số.

Kịch bản của bạn không thành công vì các tập tin bạn cung cấp (/usr/bin/env.1) không phải là một thực thi, nhưng bắt đầu chính nó bằng một công việc ....

Tốt nhất, bạn có thể giải quyết nó bằng cách sử ...env trên kịch bản của bạn với dòng này như một công việc:

#!/usr/bin/env /usr/bin/env.1 python 

Nó sẽ không làm việc mặc dù trên Linux vì nó đối xử với "/usr/bin/env.1 python" như một con đường (nó không chia arguments)

Vì vậy, các chỉ Con đường tôi nhìn thấy là viết của bạn env.1 trong C

EDIT: có vẻ như không ai belives me ^^, vì vậy tôi đã viết một đơn giản và bẩn env.1.c:

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <unistd.h> 


const char* prependpath = "/your/prepend/path/here:"; 

int main(int argc, char** argv){ 
    int args_len = argc + 1; 
    char* args[args_len]; 
    const char* env = "/usr/bin/env"; 
    int i; 

    /* arguments: the same */ 
    args[0] = env; 
    for(i=1; i<argc; i++) 
    args[i] = argv[i]; 
    args[argc] = NULL; 

    /* environment */ 
    char* p = getenv("PATH"); 
    char* newpath = (char*) malloc(strlen(p) 
       + strlen(prependpath)); 
    sprintf(newpath, "%s%s", prependpath, p); 
    setenv("PATH", newpath, 1); 

    execv(env, args); 
    return 0; 
} 
Các vấn đề liên quan