Tại sao chương trình của tôi có tên là bộ cài đặt không được thực thi?


10

Tôi đã tạo một chương trình C đơn giản như vậy:

int main(int argc, char *argv[]) {

    if (argc != 5) {
       fputs("Not enough arguments!\n", stderr);
       exit(EXIT_FAILURE);
    }

Và tôi đã sửa đổi PATH của mình trong etc / bash.bashrc như vậy:

PATH=.:$PATH

Tôi đã lưu chương trình này dưới dạng set.c và đang biên dịch nó với

gcc -o set set.c

trong thư mục

~/Programming/so

Tuy nhiên, khi tôi gọi

set 2 3

chẳng có gì xảy ra. Không có văn bản xuất hiện.

Gọi điện thoại

./set 2 3

cho kết quả như mong đợi

Tôi chưa bao giờ gặp vấn đề với PATH trước đây và

which set

trả lại ./set. Vì vậy, có vẻ như PATH là chính xác. Chuyện gì đang xảy ra vậy?


10
Nó tương đối nguy hiểm khi thêm '.' để PATH của bạn. Tốt hơn là chỉ sử dụng ./ khi thực thi một cái gì đó từ thư mục cục bộ hoặc di chuyển tệp thực thi vào một thư mục nổi tiếng như ~ / bin /
TREE

7
Đó cũng là một ý tưởng tồi để gọi chương trình thử nghiệm của bạn testvì lý do tương tự; testlà một vỏ tích hợp quá.
Jonathan Leffler

@JonathanLeffler Và đối với các bài kiểm tra nhanh, việc gọi một chương trình testdường như có ý nghĩa. Tất nhiên vào thời điểm bạn đặt nó vào bạn, PATHbạn thực sự nên nghĩ ra một cái tên khác. Và cho đến khi bạn đặt chương trình vào, PATHbạn sẽ phải gọi nó bằng ./testmọi cách. Vì vậy, sẽ ổn khi sử dụng tên testcho một chương trình miễn là nó là một bài kiểm tra nhanh mà bạn định xóa trước khi kết thúc ngày.
kasperd

1
@kasperd: Theo như tôi biết, tên thông thường cho một chương trình kiểm tra nhanh là foo.
hmakholm còn lại của Monica

Nếu bạn đặt tên cho nó lsthì bất cứ khi nào bạn đi để xem nếu nó tồn tại, nó sẽ chạy (nhưng chỉ khi bạn sửa đổi đường dẫn của bạn như bạn đã làm trong câu hỏi).
ctrl-alt-delor

Câu trả lời:


24

Thay vì sử dụng which, thứ không hoạt động khi bạn cần nó nhất , hãy sử dụng typeđể xác định cái gì sẽ chạy khi bạn gõ lệnh:

$ which set
./set
$ type set
set is a shell builtin

Shell luôn tìm kiếm các nội trang trước khi tìm kiếm $PATH, vì vậy cài đặt $PATHkhông giúp ích gì ở đây.

Tốt nhất là đổi tên thực thi của bạn thành một thứ khác, nhưng nếu bài tập của bạn yêu cầu chương trình được đặt tên set, bạn có thể sử dụng hàm shell:

$ function set { ./set; }
$ type set
set is a function
set ()
{
    ./set
}

(Điều đó hoạt động trong bash, nhưng các vỏ khác như kshcó thể không cho phép. Xem câu trả lời của mikeerv để biết giải pháp di động hơn.)

Bây giờ gõ setsẽ chạy chức năng có tên "set", thực thi ./set. GNU bashtìm kiếm các chức năng trước khi tìm kiếm các nội trang và nó tìm kiếm các nội dung trước khi tìm kiếm $PATH. Phần có tên "THỰC HIỆN HÀNH LÝ" trong trang bash man cung cấp thêm thông tin về điều này.

Xem thêm các tài liệu về builtincommand: help builtinhelp command.


3
Bạn đề nghị typehơn which, nhưng không đưa ra bất kỳ lý do tại sao. ( Tôi biết tại sao , nhưng ai đó cần đề xuất sẽ không.)
cjm

1
@cjm Đây là toàn bộ chuyên luận về lý do tại sao không : unix.stackexchange.com/questions/85249/ợi
Anthony Geoghegan

Rất nhiều thông tin. Bạn sẽ không bao giờ đoán được có quá nhiều tranh cãi về một lệnh tưởng chừng đơn giản như vậy lại thực hiện một nhiệm vụ đơn giản
Ganea Dan Andrei

4
@GaneaDanAndrei Chủ yếu, sử dụng typethay vì which, không đặt tên chương trình của bạn là "bộ" và nhận ra rằng đó function set { ./set; }là một hack xấu xí mà bạn có lẽ nên tránh.
yellowantphil

11

setlà một nội dung trong bash (và có lẽ hầu hết các shell khác). Điều này có nghĩa là bash thậm chí sẽ không tìm kiếm đường dẫn khi tìm hàm.

Là một nhận xét phụ, tôi sẽ khuyên mạnh mẽ chống lại việc thêm .vào đường dẫn vì lý do bảo mật. Ví dụ, hãy tưởng tượng cdra /tmpsau khi bất kỳ người dùng nào khác thêm tệp thực thi /tmp/cd.


2
Vâng, đó là một ý tưởng ngu ngốc mà giáo viên của tôi buộc chúng tôi phải, không trông không chuyên nghiệp trong khi trình bày một chương trình ". Anh ấy chấm điểm cho bạn nếu nó không được thực hiện.
Ganea Dan Andrei

1
Brownie điểm cho các giáo viên tuyệt vời :(
klimpergeist

4
cdlà một vỏ được tích hợp sẵn, vì vậy ví dụ này sẽ không hoạt động với cùng lý do như với set.
Emil Jeřábek

15
Sử dụng ./foođể gọi một chương trình chuyên nghiệp; nó cho thấy rằng bạn hiểu tại sao .không nên ở $ PATH. Giáo viên của bạn sai, và bạn có thể nói với anh ấy tôi đã nói như vậy.
zwol

10

setkhông chỉ là một nội dung, nó là một nội dung đặc biệt POSIX . Có một số lệnh dựng sẵn được chỉ định theo tiêu chuẩn được tìm thấy trong tìm kiếm lệnh trước bất kỳ thứ gì khác - $PATHkhông được tìm kiếm, tên hàm không được tìm kiếm, v.v ... Hầu hết các nội dung không đặc biệt đều được tiêu chuẩn POSIX yêu cầu được tìm thấy trong của bạn $PATH trước khi shell sẽ chạy bất kỳ quy trình dựng sẵn nào của chính nó. Điều này đúng với echovà hầu hết những người khác (mặc dù liệu tiêu chuẩn được vinh danh trong lĩnh vực này đã là một vấn đề tranh chấp tại danh sách gửi thư nhóm mở trong quá khứ) , nhưng không của set, trap, break,return, continue, ., :, times, eval, exit, export, readonly, unset, Hoặc exec.

Tất cả đều là tên dành riêng của shell và chúng có các thuộc tính đặc biệt ngoài thứ tự ưu tiên cho tìm kiếm lệnh. Ví dụ: bạn không thể xác định hàm shell với bất kỳ tên nào trong shell tuân thủ tiêu chuẩn. Đây là một điều tốt - nó cho phép mọi người viết các kịch bản di động một cách an toàn . Đây là những lệnh cơ bản mà từ đó một người viết kịch bản có kinh nghiệm có thể thiết lập một chỗ đứng an toàn và đáng tin cậy trong môi trường của mình. Xâm nhập không gian tên này là không nên.

Tuy nhiên, nếu bạn muốn xâm chiếm nó, bạn có thể làm như vậy với alias. Thứ tự mở rộng vỏ cho phép giải quyết công việc này. Bởi vì lệnh aliasđược mở rộng trong khi lệnh đang được đọc, bất cứ điều gì bạn thay thế settên bằng định nghĩa của bạn sẽ mở rộng chính xác, có lẽ nó không nên mở rộng sang một trong những tên đó.

Vì vậy, bạn có thể làm:

alias set=./set

... Nó sẽ hoạt động tốt.


3

Vấn đề là đó setlà một shell dựng sẵn và giải pháp tốt nhất là sử dụng một tên khác cho chương trình thực thi của bạn.

Tình cờ, tuần trước, tôi đã hỏi một câu hỏi về cách chạy các lệnh hệ thống thay vì các nội trang shell có cùng tên và giải pháp tôi chấp nhận là chạy lệnh thông qua env:

env set 2 3

Trong trường hợp cụ thể này, nơi bạn đã biết rằng lệnh bạn muốn sử dụng được đặt trong thư mục hiện tại của bạn, sẽ tốt hơn nếu chạy trực tiếp tệp thực thi bằng cách nhập đường dẫn của nó (sử dụng .để thể hiện thư mục làm việc hiện tại):

./set 2 3

Cả hai giải pháp trên đều là thuyết bất khả tri, nghĩa là chúng sẽ hoạt động bất kể bạn đang sử dụng loại vỏ nào.

Các đề xuất như sử dụng commandnội dung dựng sẵn sẽ không hoạt động trong Bash: điều này chỉ ngăn các hàm shell được chạy. Trong khi, nó không được ghi lại, tôi cũng nhận thấy rằng việc sử dụng commandcũng ngăn chặn các từ khóa shell . Tuy nhiên, nó sẽ không làm như vậy đối với các nội dung shell như set. Theo tôi hiểu, commandcó thể hoạt động với các shell khác như zsh.

Ngoài ra, thủ đoạn như \sethoặc "set"hoặc 'set'không làm việc cho builtins Bash - mặc dù họ là hữu ích cho việc chạy các file thực thi thay vì bí danh hoặc vỏ từ khóa .

Lưu ý: Câu trả lời này ban đầu bắt đầu như một nhận xét về câu trả lời (được chấp nhận) của Eric nhưng đã trở nên quá lớn để phù hợp với nhận xét. Các câu trả lời khác khuyến nghị sử dụng typevà không thêm .vào PATH là những câu trả lời hay.

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.