Estoy tratando de ejecutar argumentos de línea de comando (específicamente echo) a través de la familia de funciones ejecutivas. Puedo ejecutar la función execv si escribo mi propio ejecutable y lo ejecuto, pero si intento ejecutar touch o echo me devuelve -1

#include <stdio.h>
#include <unistd.h> // exec functions
#include <sys/types.h> // pid_t
#include <sys/wait.h>

#define HIGH 1
#define LOW 0

int digitalWrite(int pin, short type) {

    pid_t pid = fork();
    if (pid == 0) {
        printf("pid == %i\n", pid);
        if (type == HIGH) {
            char* args[] = {"echo", "1", ">", "/sys/class/gpio/gpio67/value", NULL};
            int val = execv(args[0], args);
            printf("ran function execl, %i\n", val);
        } else {
            printf("Unable to do anything but set pin to HIGH\n");
        }
    } else if (pid < 0) { // pid < 0
        printf("fork failed\n");
    }

    wait(NULL);
}

int main() {

    printf("Starting digitalWrite\n");

    digitalWrite(0, HIGH);

    printf("Completed digitalWrite()\n");

    return 0;
}

Solo por contexto, aquí está mi construcción:

$ gcc wiringbeagle.c 
$ ./a.out 
Starting digitalWrite
pid == 0
ran function execl, -1
Completed digitalWrite()
Completed digitalWrite()
$ ls
a.out  wiringbeagle.c

El comando echo 1 > /sys/class/gpio/gpio67/value funciona bien en el terminal por sí mismo, y si creo un archivo local (es decir, toco tmpfile.txt) e intento ejecutar echo hi > tmpfile.txt, se ejecuta como se esperaba en mi línea de comando pero no funciona No se ejecute en el programa.

No debo entender algo con execv, y cualquier ayuda sería muy apreciada.

0
haxonek 11 may. 2019 a las 03:22

3 respuestas

La mejor respuesta

El primer argumento para execv es el archivo a ejecutar. A diferencia de su shell, execv no busca a través de los directorios indicados por la variable de entorno PATH, por lo que debe darle la ruta completa al ejecutable. A menos que haya un archivo ejecutable llamado echo en su directorio de trabajo actual, execv("echo",...) fallará con un error "archivo no encontrado". (Use perror para obtener mejores mensajes de error).

Si desea buscar el ejecutable como lo hace el shell, use execvp. Pero tenga en cuenta que su shell probablemente ejecuta echo como un comando incorporado, por lo que no será el mismo echo que usa su shell. En este caso, está bien.

Una vez que arregles eso, encontrarás un problema diferente. Como solo está invocando una utilidad de línea de comandos con argumentos, en lugar de usar un shell, el argumento ">" es solo un argumento. Es el shell que maneja las redirecciones (así como las tuberías, las citas y un montón de otras cosas útiles). Entonces, todo lo que logrará es enviar los tres argumentos a stdout.

Puede usar la función system para ejecutar un comando usando el shell, o puede configurar la redirección usted mismo freopen ing stdout en su hijo antes de hacer execvp.

Puede obtener mucha información sobre las interfaces del sistema utilizando el comando man. Por ejemplo, para saber qué hace freopen, use man freopen. También puede leer páginas de manual en Internet, por ejemplo. http://man7.org/linux/man-pages/man3/ freopen.3.html, pero la documentación en su propio sistema está allí, y también se aplica a la versión real del software instalado en su sistema (suponiendo que haya instalado la documentación).

3
rici 11 may. 2019 a las 01:07

execv() no busca la variable de entorno PATH para encontrar un archivo ejecutable. Por la página del manual de Linux execv() (en negrita texto agregado):

...

Semántica especial para execlp(), execvp() y execvpe()

Las funciones execlp(), execvp() y execvpe() duplican las acciones del shell al buscar un archivo ejecutable si el especificado el nombre del archivo no contiene un carácter de barra diagonal (/) . ...

...

Entonces, esos tres buscarán la variable de entorno PATH si el nombre de archivo pasado no contiene un carácter /.

Estás utilizando execv(), que no es uno de esos tres. Por lo tanto, execv() no buscará la variable de entorno PATH.

Como su directorio de trabajo actual no contiene un archivo ejecutable llamado echo, execv() falla.

Debe usar execvp() por la página del manual.

1
Andrew Henle 11 may. 2019 a las 00:47

No estoy completamente seguro de por qué incluso está usando la familia exec para ejecutar programas externos en este caso. La biblioteca estándar de C proporciona material de E / S de archivos perfectamente adecuado.

Por ejemplo, puede simplemente fopen, fprintf y fclose el archivo sin iniciar otro proceso externo para hacer ese trabajo por usted:

int bytesWrit = 0;
FILE *gpioHndl = fopen("/sys/class/gpio/gpio67/value");
if (gpioHndl != NULL) {
    bytesWrit = fprintf(gpioHndl, "1\n");
    fclose(gpioHndl);
}
if (bytesWrit != 2) {
    HandleError();
}

Esta es probablemente la forma preferida de hacer lo que desea, que es simplemente escribir un valor fijo en un archivo.


En términos de por qué su llamada execv no funciona (aunque es totalmente irrelevante si sigue mi consejo anterior), hay varias cosas que debe tener en cuenta.

Primero, aunque algunos comandos son en realidad archivos en el disco que puede exec, otros pueden ser comandos internos bash (a) . En mi sistema, por ejemplo:

pax:~$ type ftp
ftp is /usr/bin/ftp

pax:~$ type echo
echo is a shell builtin

Una forma de resolver esto es ejecutar el ejecutable real bash (que, al ser un comando en el disco, puede hacerse a través de exec), diciéndole que ejecute su interna echo comando. Desde la línea de comando:

pax:~$ bash -c 'echo xyzzy'
xyzzy

En segundo lugar, si desea utilizar la redirección, esto normalmente es algo que hace el shell , no las llamadas exec o ejecutables individuales.

Intentar hacer la redirección a través de la familia exec generalmente solo dará como resultado que >somefile se pase como un parámetro literal al ejecutable (en la matriz argv), no se utiliza para adjuntar salida estándar a un archivo. En otras palabras, no funcionará a menos que el ejecutable maneje específicamente la redirección, lo cual es raro.

Eso significa que tendrá que ejecutar el shell con redirección y hacer que ejecute el ejecutable después de realizar esas redirecciones, incluso si el comando no es interno.

En tercer lugar, si desea que la ruta busque su ejecutable, execvp es la llamada que desea, no execv (esta última solo usa el archivo que proporciona explícitamente, ya sea relativo desde el directorio de trabajo actual o un absoluto ruta como /bin/ls). Entonces, en su caso, usted debería:

  • use execvp para buscar la ruta; o
  • especifique completamente la ruta con execv.

(a) El comando echo, mientras que es bash - interno también se puede proporcionar como un ejecutable separado (creo que Posix requiere esto), por lo que esto puede no ser un problema aquí. puede ser un problema si espera que actúen exactamente igual en términos de más argumentos esotéricos :-)

2
paxdiablo 12 may. 2019 a las 02:16