2013-03-21 5 views
8

Tôi đang cố gắng chuyển đối số do người dùng nhập vào execvp().Truyền một mảng tới execvp() từ đầu vào của người dùng

Cho đến giờ tôi đã chia nhỏ chuỗi. Nếu người dùng nhập ls -a, temp được lưu dưới dạng "ls" và "-a", sau đó là ký tự NULL. Tôi không chắc chắn làm thế nào để trỏ đến điều này đúng trong execvp. Trong ví dụ tôi đã thấy nó bằng cách sử dụng execvp(temp[position], temp). Tôi biết cách tôi đang cố gắng làm điều đó vào lúc này là sai, nhưng tôi không chắc chắn làm thế nào để làm điều đó đúng cách! Tại thời điểm này tôi nhận được một lỗi phân đoạn.

int main(int argc, char *argv[]) 
{ 
    char line[124]; 
    int size = 124; 
    char *temp = NULL; 

    while(fgets(line, size, stdin) != NULL) { 
     if (strcmp(line, "exit\n") == 0) { 
      exit(EXIT_SUCCESS); 
     } 
     temp = strtok(line, " "); 
     while (temp != NULL) { 
      printf("%s\n", temp); 
      temp = strtok(NULL, " "); 
     } 
     execvp(temp, &temp);  
    } 
    return EXIT_SUCCESS; 
} 
+2

Vì bạn không sử dụng 'argc' hoặc' argv', bạn có thể sử dụng 'int main (void)'. Với các tùy chọn trình biên dịch tôi sử dụng thường xuyên, ngăn chặn một vài cảnh báo. –

+0

@JonathanLeffler Tôi sẽ sử dụng chúng sau này. Chỉ cần không vào thời điểm này. – caerulean

+1

Đủ công bằng - nhưng đối với một SSCCE ([Ví dụ ngắn, tự chứa, đúng] (http://sscce.org/)), bạn cố gắng loại bỏ mọi thứ không quan trọng đối với ví dụ được rút gọn. Đó là một vấn đề nhỏ - rất nhỏ. Hầu hết các chương trình cung cấp nhiều cơ hội hơn cho sniping hơn của bạn. –

Trả lời

6

Vấn đề của bạn là temp là một con trỏ và bạn cần chuyển một chuỗi con trỏ đến execvp().

Cái gì như:

enum { MAX_ARGS = 64 }; 
    char *args[MAX_ARGS]; 
    char **next = args; 

    temp = strtok(line, " "); 
    while (temp != NULL) 
    { 
     *next++ = temp; 
     printf("%s\n", temp); 
     temp = strtok(NULL, " "); 
    } 
    *next = NULL; 
    execvp(args[0], args); 

Lưu ý rằng danh sách đối số đã được đưa ra một con trỏ null như một terminator, giống như argv[argc] == NULL trong main(). Rõ ràng, tôi đã skimped trên kiểm tra lỗi (nếu bạn vượt qua hơn 63 đối số, bạn sẽ tràn qua args mảng). Nhưng điều này có chứa ý tưởng cốt lõi.


Với ví dụ này, tôi dường như không thể để có được những lệnh đơn giản của ls để làm việc, tôi đã cố gắng mkdirecho và họ dường như làm việc tốt. Chuyển số ls trả về -1 từ execvp().

Tôi không chắc chắn những gì các vấn đề có thể là - tất cả các công việc cho tôi:

  • ls
  • ls -l
  • ls -l madump.c (nơi madump.c sẽ xảy ra là một tập tin trong thư mục Tôi đang thử nghiệm tại)

Mã tôi đã sử dụng là:

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

int main(void) 
{ 
    char line[1024]; 

    while (fgets(line, sizeof(line), stdin) != NULL) 
    { 
     if (strcmp(line, "exit\n") == 0) 
      exit(EXIT_SUCCESS); 

     char *args[64]; 
     char **next = args; 
     char *temp = strtok(line, " \n"); 
     while (temp != NULL) 
     { 
      *next++ = temp; 
      printf("%s\n", temp); 
      temp = strtok(NULL, " \n"); 
     } 
     *next = NULL; 

     puts("Checking:"); 
     for (next = args; *next != 0; next++) 
      puts(*next); 

     execvp(args[0], args); 
    } 

    return EXIT_SUCCESS; 
} 

Lưu ý rằng tôi đã thêm \n vào danh sách mã thông báo strtok() sau khi tạo thư mục có dòng mới ở cuối tên. Tốt cho những người bạn khó chịu và làm bối rối kẻ thù bán giáo dục, nhưng gây phiền toái từ hầu hết các quan điểm khác. Lưu ý cách tôi in ra dữ liệu sẽ được chuyển đến execvp() ngay trước khi thực sự làm như vậy. Thông thường, tôi muốn sử dụng printf("<<%s>>\n", *next); thay vì chỉ puts() để có được một dấu hiệu rõ ràng về nơi các đối số bắt đầu và kết thúc.

Kết quả từ cách chạy lệnh (doit) là:

$ ./doit 
ls -l madump.c 
ls 
-l 
madump.c 
Checking: 
ls 
-l 
madump.c 
-rw-r--r-- 1 jleffler staff 2352 Jul 28 2011 madump.c 
$ 

bạn đã nhận được gì từ phiên bản của bạn?

+0

Tôi cũng không cần phải thêm bất cứ điều gì trong temp để args trước khi gọi trên execvp? – caerulean

+1

Điều đó (thêm những gì trong 'temp' vào' args') đang được thực hiện thông qua 'next'. Ban đầu, '* next' trỏ tại' args [0] '; giá trị trong 'temp' được sao chép vào' args [0] 'và' next' được tăng lên để trỏ tới 'args [1]'; rửa sạch và lặp lại. Ngẫu nhiên, thường có một 'fork()' trong đoạn mã, nếu không chương trình của bạn sẽ dừng lại khi nó thực hiện thành công một lệnh. –

+0

Tôi đã giữ mảng args của tôi với kích thước 16 cho bây giờ. Sau khi tôi làm việc này, tôi sẽ thực hiện fork() để tôi có thể xử lý nhiều quy trình. – caerulean

1

Tôi đoán segfault là vì bạn đang chuyển một con trỏ NULL đến execvp. Vòng lặp ngay phía trên cuộc gọi đảm bảo điều này.

Để làm những gì bạn đang cố gắng làm, bạn sẽ cần phải tạo một chuỗi các con trỏ chuỗi cho những gì bạn hiện đang gọi là temp. Điều này sẽ trở thành mảng argv cho chương trình bạn gọi. Đó là lý do tại sao lệnh thường được sử dụng như execvp (temp[0], temp) - argv[0] thường là tên của chương trình.

Vì vậy, hãy thử tạo một chuỗi các con trỏ chuỗi và mỗi điểm trỏ đến một từ được mã hóa từ line. Bạn có thể cần sử dụng malloc, mặc dù nếu bạn muốn thông minh, bạn có thể trỏ trực tiếp vào line. Nếu bạn làm điều đó, bạn cần đặt ký tự ngay sau mỗi 'từ' thành \0.

2

Khi mã của bạn trông hiện tại, sau khi kết thúc while(temp != NULL), temp sẽ là NULL!

execvp mong muốn đối số đầu tiên là đường dẫn của tệp sẽ là hình ảnh quy trình mới. Đối số thứ hai được mong đợi là một chuỗi các chuỗi bị chấm dứt NULL trong đó thành viên cuối cùng của mảng đó là con trỏ NULL và thành viên thứ nhất là tên tệp của tệp được chỉ định trong đối số đầu tiên.

Để thực hiện điều này trong mã của bạn, hãy xem xét while vòng lặp sau đây thay vì:

char **argList = NULL; 
unsigned int numArgs = 0; 
while (temp != NULL) { 
    numArgs++; 

    /* Reallocate space for your argument list */ 
    argList = realloc(argList, numArgs * sizeof(*argList)); 

    /* Copy the current argument */ 
    argList[numArgs - 1] = malloc(strlen(temp) + 1, 1); /* The +1 for length is for the terminating '\0' character */ 
    snprintf(argList[numArgs - 1], strlen(temp) + 1, "%s", temp); 

    printf("%s\n", temp); 
    temp = strtok(NULL, " "); 
} 

/* Store the last NULL pointer */ 
numArgs++; 
argList = realloc(argList, numArgs * sizeof(*argList)); 
argList[numArgs - 1] = NULL; 

/* Finally, pass this to execvp */ 
execvp(argList[0], argList); 
/* If you reach here, execvp() failed */ 

Code tôi cung cấp ở trên không làm một số kiểm tra lỗi (chẳng hạn như khi realloc hoặc malloc thất bại), nhưng về cơ bản giữ những điểm này trong tâm trí:

  1. Đối số 1: Tên đường dẫn của tệp sẽ được đưa vào bộ nhớ.
  2. Đối số 2: Danh sách đối số trong đó thành viên đầu tiên của danh sách đó = tên tệp, thành viên cuối cùng = con trỏ NULL.

Vui lòng xem the documentation để biết rõ hơn và một ví dụ rất đơn giản.

+0

Bạn không cần phải sao chép các chuỗi vì 'temp' trỏ đến các phần của mảng' dòng'. Nếu bạn sao chép các chuỗi, thì tôi khuyên bạn nên sử dụng 'strdup()'. 'Snprintf()' rất thú vị. Rõ ràng '\ 0' thực sự là không cần thiết; đã có được một null ở cuối anyway. 'Sprintf() 'cuối cùng không làm những gì bạn nghĩ. Nó hoặc là treo hoặc viết một chuỗi như '(null)' vào biến mà bạn chỉ phân bổ đủ không gian cho một ''\ 0''. –

+0

Ồ, tôi không biết gì về 'strdup()'. Có vẻ sạch sẽ hơn để sử dụng thay vào đó. Oh yeah 'snprintf()' sao chép '\ 0', xấu của tôi. Tôi không hiểu điểm cuối cùng của bạn. Trên thực tế, tôi chỉ nhận ra rằng 'strlen ((char *) 0)' = crash. '(char *) 0' =' (null) 'vì vậy tôi là loại không biết làm thế nào để phân bổ bộ nhớ cho điều đó. Bất kỳ đề xuất? –

+0

Sử dụng: 'argList [numArgs - 1] = NULL;' (hoặc '0' thay cho' NULL') thay vì vòng lặp cuối cùng 'sprintf()'. Giá trị cuối cùng trong mảng phải là một con trỏ null. Về mặt lý thuyết, bạn nên kiểm tra xem phân bổ bộ nhớ có thành công hay không. Ngoài ra, mặc dù nó làm cho mã khó hơn một chút, nhưng bạn nên phân bổ bộ nhớ cho 'argList' trong các khối (trong số 64 con trỏ tại một thời điểm), để tránh hành vi bậc hai khi bạn phát triển khối một mục tại một thời điểm . Tuy nhiên, đó là một sự tinh tế cho sau này. –