2010-10-21 5 views
7

Tôi có một chương trình mở tệp bằng cách sử dụng đường dẫn tương đối (ví dụ '..').Làm cách nào để mở tệp có đường dẫn tương đối trong Linux?

Bây giờ vấn đề là, khi tôi thực thi chương trình từ một thư mục khác, đường dẫn tương đối không liên quan đến chương trình nhưng liên quan đến thư mục làm việc. Vì vậy, nếu tôi bắt đầu chương trình với '/ path/to/program/myprog' thì nó không tìm được tập tin.

Có cách nào để thực thi chương trình độc lập với thư mục làm việc không? Id est, nếu thư mục làm việc là thư mục chứa chương trình? Hay tôi chỉ nghĩ một cách quá phức tạp và có một cách dễ dàng hơn để tham khảo một tệp, vị trí nào chỉ được biết bởi đường dẫn của nó liên quan đến đường dẫn của tệp chương trình?

+3

thể trùng lặp của [thư mục chạy chương trình trên Linux ?] (http://stackoverflow.com/questions/737996/directory-of-running-program-on-linux) – ereOn

+1

Điều này có thể đạt được tuy nhiên, bạn có chắc chắn bạn muốn điều này? Trên Linux, mỗi loại tệp có các thư mục chuyên dụng của nó: cấu hình thường đi tới '/ etc /' hoặc '~/.my_program', hình ảnh và tài liệu tới'/usr/share', v.v. Ngoài ra, hầu hết người dùng sẽ mong đợi chương trình của bạn chạy trong thư mục hiện tại. – ereOn

+0

Xem thêm http: //www.dreamincode.net/forums/topic/98402-directory-of-running-program/ –

Trả lời

4

Nếu chương trình không tự làm, nó là một chương trình không tốt. các chương trình xấu nên được bọc với một chút Bash scripting:

#!/bin/bash 

set -e 
cd $(readlink -f $(dirname $0)) 
exec ./myprog $* 

Kịch bản trên xác định thư mục nơi nó nằm, sau đó thay đổi thư mục làm việc hiện hành vào thư mục đó và chạy một chương trình myprog từ đó, đi qua tất cả các thông số một cách minh bạch . Vì vậy, bạn phải đặt kịch bản này vào cùng một thư mục nơi chương trình của bạn được đặt và chạy nó thay vì chương trình của bạn.

Giả sử bạn có quyền truy cập vào mã nguồn và có thể sửa chương trình, sau đó sử dụng proc fs để xác định vị trí của chương trình và sau đó sử dụng đường dẫn tuyệt đối.

Ví dụ: /proc/self/exe sẽ luôn là một liên kết tượng trưng trỏ vào tệp nhị phân của quy trình hiện tại. Sử dụng readlink để đọc giá trị của nó, sau đó cắt tên thực thi và bạn nhận được thư mục.

+1

Không phải là nó độc đáo để ghi đè lên sự lựa chọn của người dùng thư mục làm việc như thế này (hoặc lập trình bằng cách sử dụng 'chdir' như tôi đã đề cập)? – gspr

+0

@gspr: Tập lệnh Bash không ghi đè lên thư mục làm việc của người dùng. Nó ghi đè thư mục làm việc cho tệp thực thi mà nó chạy. Bởi vì nó cần nó. Thư mục của người dùng không bị thay đổi. –

+1

Tôi có nghĩa là lựa chọn thư mục làm việc của người dùng * cho chương trình này *. Không phải là loại thực hành xấu như vậy? Ý tôi là, như wilhelmtell, Let_Me_Be và tôi đều chỉ ra, có những cách tốt hơn để đạt được mục tiêu mong muốn. – gspr

1

đã có một câu hỏi một lúc trước làm thế nào để tìm the location of the executable in C bạn có thể sử dụng con đường này để mở cấu hình của bạn, tài nguyên, vv ..

0

Vâng, nếu chương trình của bạn cần phải mở một tập tin từ một vị trí đó phụ thuộc vào nơi chương trình được cài đặt, bạn có lẽ nên làm cho điều này một tùy chọn biên dịch thời gian. Có hệ thống xây dựng của bạn thiết lập một số CPP macro chỉ ra thư mục nơi các tập tin dữ liệu trong câu hỏi có thể được tìm thấy. Đây là những gì tùy chọn --datadir để cấu hình trong một chương trình "cấu hình, tạo, cài đặt" chuẩn thường làm.

Tất nhiên, nếu bạn thực sự muốn, bạn có thể thay đổi thư mục làm việc theo chương trình với các hàm POSIX chdir. Nhưng như tôi đã nói, nếu một chương trình cần biết vị trí của nó, điều này sẽ được cung cấp tại thời điểm biên dịch. Sau đó, bạn không cần phải ghi đè lên sự lựa chọn của người dùng làm việc dir.

1

Một cách là sử dụng argv [0] - có đường dẫn tương đối của chương trình của bạn (ví dụ: ./programs/test/a.out). Nếu bạn cắt tên chương trình và thêm đường dẫn tương đối vào tệp, bạn sẽ nhận được một con quái vật (ví dụ: ./programs/test/../../input_data) nhưng nó sẽ hoạt động.

+0

Điều này không được đảm bảo, đúng không? – ereOn

+0

Giả sử anh ta có thể sửa đổi chương trình. Câu hỏi đặt ra là làm thế nào để thực hiện chương trình trong thư mục của nó, không phải là cách sửa đổi chương trình và sửa lỗi này với đường dẫn tương đối. –

+0

Làm sao tôi quên được argv [0]? Nó có thể không chính xác vì nó được thiết lập bởi lệnh 'execve', nhưng nó thường đúng và nên đủ tốt để sử dụng cho điều này. – nategoose

1

Cách dễ nhất là đặt chương trình của bạn ở nơi đã biết trước (/ bin,/usr/bin, v.v.). Nếu không, bạn có thể sử dụng argv [0], loại bỏ tên chương trình (phần cuối), và sử dụng nó làm thư mục làm việc của bạn để tiền tố tất cả các đường dẫn tương đối (nếu bạn muốn đường dẫn tương đối liên quan đến vị trí của chương trình).

Ngoài ra, bạn có thể xác định đường dẫn chương trình của mình bằng phương pháp trên (sử dụng argv[0]), sau đó gọi số chdir() bằng thư mục này. Tất cả các đường dẫn tương đối từ đó trở đi sẽ liên quan đến vị trí của chương trình.Tuy nhiên, lưu ý rằng trong trường hợp này, bạn phải xác định xem argv[0] có giữ đường dẫn tuyệt đối hay không. Nếu không, bạn phải nhận được thư mục làm việc hiện tại (getcwd()) và sau đó nối thêm phần thư mục của argv[0]. Tuy nhiên, lưu ý rằng việc thay đổi thư mục công việc hiện tại. không phải là một ý tưởng tốt, thông thường, như thể người dùng cung cấp cho bạn đường dẫn tệp làm đối số, nó sẽ liên quan đến thư mục công việc hiện tại của bạn, không liên quan đến nơi chương trình được lưu trữ.

Một số ví dụ: Hãy tưởng tượng chương trình của bạn sống ở /usr/bin. Bạn có thể gọi chương trình của bạn như:

/usr/bin/myprog 

(đó sẽ là argv[0] tỉa tên thực thi và bạn có dir của bạn..) Hoặc là, nói, trong /usr:

./bin/myprog 

Bây giờ, argv[0] là một đường dẫn tương đối. Bạn phải thêm một thư mục làm việc hiện tại (/usr) vào thư mục argv[0]: /usr/./bin/myprog và sau đó lại cắt bớt tên thực thi. Thư mục sẽ lại là /usr/bin.

0

Không sử dụng đường dẫn tương đối. Sử dụng đường dẫn tuyệt đối. Bạn có thể có một hằng số được định nghĩa trong tệp tiêu đề config.h xác định nơi tệp thi hành của bạn được cài đặt. Sau đó, hãy thêm chuỗi liên tục vào bất kỳ đường dẫn tương đối nào bạn chỉ định trong mã của mình.

+0

Điều này sau đó sẽ dựa vào chương trình đang được cài đặt ở một vị trí cụ thể và sẽ phá vỡ chức năng nếu thư mục được di chuyển. – CJxD

2

openat mở tệp tương ứng với một bộ mô tả tệp thư mục cụ thể mà bạn truyền, nhưng tôi không nghĩ đó thực sự là những gì bạn muốn (chính xác).

Bạn sẽ cần phải tìm thư mục nơi tệp thực thi hiện tại và sau đó tạo cuộc gọi mở tương đối (sử dụng toán tử chuỗi để tạo đường dẫn, openat hoặc thay đổi thư mục hiện tại thành thư mục đó).

Để tìm tệp thực thi, bạn có thể readlink/proc/self/exe. readlink đọc đường dẫn mà liên kết tượng trưng trỏ tới và /proc/self là liên kết tượng trưng đến /proc/<PID> trong đó <PID> là ID tiến trình của quy trình hiện tại (được xử lý đặc biệt trong hạt nhân) và exe. cho quá trình đó. Sau đó, bạn sẽ cần phải tìm ra đường dẫn đến tệp thực thi đó và sử dụng nó.

Tất cả điều đó được nói, bạn thường nên tránh viết chương trình theo cách mà họ mong muốn tìm thấy những thứ liên quan đến tệp thực thi của họ.

0

Bạn có thể xác định đường dẫn thực hiện từ tham số argv[0], nhưng hãy cẩn thận khi làm như vậy.

Điều bạn mô tả là ngữ nghĩa nổi tiếng và được mong đợi. Người dùng sẽ mong đợi hành vi này.

0

Dưới đây là một số mã, bạn có thể sử dụng để tìm bạn cài đặt-con đường từ bên trong chương trình của bạn (thay thế "test0002" với tên của chương trình của bạn):

#include <iostream> 
#include <sstream> 
#include <string> 
#include <fstream> 
#include <unistd.h> 

///============================================================================= 
std::string FindInstallPath() 
{ 
    std::string sret=""; 
    int pid = (int)getpid(); 
    bool b=false; 
    std::string sf, s; 
    std::stringstream ss; 
    ss << "/proc/" << pid << "/maps"; 
    sf = ss.str(); 
    std::ifstream ifs(sf.c_str()); 
    size_t pos1, pos2; 
    while (!b && ifs.good()) 
    { 
     std::getline(ifs, s); 
     if ((pos1 = s.rfind("test0002")) != std::string::npos) 
     { 
      if ((pos2 = s.find_first_of('/')) != std::string::npos) 
      sret = s.substr(pos2, pos1 - pos2); 
      b = true; 
     } 
    } 
    if (!b) sret = ""; 
    ifs.close(); 
    return sret; 
}