¿Por qué el comando `which` no funcionaría para` cd`? ¡Tampoco puedo encontrar el ejecutable para `cd`!

30

Lo intenté which cdy no dio una ruta, sino que devolvió el código de salida 1 (comprobado con echo $?). El coreutil en cdsí está funcionando, por lo que el ejecutable debería estar allí, ¿verdad? También ejecuté un findfor cd, pero no se mostró ningún archivo ejecutable. ¿Cómo se implementa entonces?

Actualizar:

No sé si debería preguntar esto en otra publicación, pero como creo que es bueno aquí, estoy expandiendo (?) La publicación ... Entonces la respuesta fue bastante simple, no hay ejecutable para eso, porque es un builtin - ¡Pero he encontrado que algunos builtins (bash shell en Fedora) tienen los archivos ejecutables! Por lo tanto, supongo que no incluye ningún ejecutable. Tal vez una respuesta que explique qué son los builtins en realidad (¿comandos builtin?), Lo cual es el problema aquí, en lugar de centrarse más en cd... Algunos buenos enlaces publicados anteriormente indican que los builtins no son programas ... entonces, ¿qué son? ¿Cómo trabajan? ¿Son solo funciones o hilos del shell?

preciso
fuente
1
Lee esta respuesta. Se sugiere usar el typecomando
c0rp
77
Vea estas preguntas y respuestas sobre por qué cddebe ser un programa integrado: ¿Por qué el CD no es un programa? y este sobre por qué typees mejor que which: ¿Por qué no usar "which"? ¿Qué usar entonces?
terdon
Pregunta similar aquí: askubuntu.com/q/613470/178596
Wilf

Respuestas:

46

El comando cdno puede ser un ejecutable

En un shell, cdse usa para "ir a otro directorio", o más formalmente, para cambiar el directorio de trabajo actual (CWD). Es imposible implementar eso como un comando externo:

El directorio pertenece a un proceso.

El directorio de trabajo actual es el directorio que se utiliza para interpretar rutas relativas para obtener una ruta completa que se puede usar para acceder a los archivos. Las rutas relativas se utilizan en muchos lugares, y la interpretación en un proceso no debe influir en otro proceso.
Por esta razón, cada proceso tiene su propio directorio de trabajo actual.

cdse trata de cambiar el directorio de trabajo actual del proceso de shell, por ejemplo bash.

Si se tratara de un comando externo, un ejecutable en la ruta, ejecutar ese ejecutable crearía un proceso con su propio directorio de trabajo, sin influir en el del shell actual. Incluso si el comando externo cambiara su directorio, ese cambio desaparecerá cuando el proceso externo salga.

Comandos incorporados de Shell

Por lo tanto, no tiene sentido ejecutar un comando externo para la tarea de cd. El comando cddebe aplicar un cambio al proceso de shell actualmente en ejecución.

Para hacer eso, es un "comando incorporado" del shell.

Los comandos incorporados son comandos que se comportan de manera similar a los comandos externos, pero se implementan en el shell (por cdlo que no forma parte de los coreutils). Esto permite que el comando cambie el estado del shell, en este caso para llamar a chdir()see (see man 2 chdir);

Acerca de which

Ahora, la respuesta a la pregunta del título es fácil:
el comando ejecutable whichno puede decirnos que cd es un comando incorporado porque un comando ejecutable no sabe nada sobre los incorporados.

Alternativa type -a

Como alternativa a which, puede usar type -a; Puede ver comandos ejecutables y componentes incorporados; Además, ve alias y funciones, también implementados en el shell:

$ type -a cd
cd is a shell builtin
$ type -a type
type is a shell builtin
$ type -a which
which is /usr/bin/which
which is /bin/which
Volker Siegel
fuente
1
¡Gran explicación!
SaltyNuts
3
Mucho mejor que la respuesta actualmente aceptada, esto explica por qué cd es un shell incorporado.
Lily Chung
28

cdes un shell incorporado ordenado por POSIX :

Si un comando simple da como resultado un nombre de comando y una lista opcional de argumentos, se realizarán las siguientes acciones:

  1. Si el nombre del comando no contiene barras, se producirá el primer paso exitoso en la siguiente secuencia:
    ...
    • Si el nombre del comando coincide con el nombre de una utilidad que figura en la siguiente tabla, se invocará esa utilidad.
      ...
      cd
      ...
    • De lo contrario, se buscará el comando utilizando la RUTA ...

Si bien esto no dice explícitamente que tiene que ser incorporado, la especificación continúa diciendo, en la descripción decd :

Dado que cd afecta el entorno actual de ejecución del shell, siempre se proporciona como un shell integrado normal.

Del bashmanual :

Los siguientes comandos integrados de shell se heredan de Bourne Shell. Estos comandos se implementan según lo especificado por el estándar POSIX.
...

cd
       cd [-L|[-P [-e]]] [directory]

Supongo que se podría pensar en una arquitectura donde cdno tiene que ser un edificio. Sin embargo, tienes que ver lo que implica un incorporado. Si escribe un código especial en el shell para hacer algo para un comando, se está acercando a tener un builtin incorporado. Cuanto más lo hagas, mejor será simplemente tener un incorporado.

Por ejemplo, podría hacer que el shell tenga IPC para comunicarse con subprocesos, y habría un cdprograma que verificaría la existencia del directorio y si tiene permiso para acceder y luego se comunica con el shell para indicarle que cambie su directorio. Sin embargo, deberá verificar si el proceso de comunicación con usted es un niño (o hacer un medio especial de comunicación solo con los niños, como un descriptor de archivo especial, memoria compartida, etc.), y si el proceso es realmente ejecutando el cdprograma de confianza o alguna otra cosa. Esa es una lata entera de gusanos.

O podría tener un cdprograma que haga que el chdirsistema llame y comience un nuevo shell con todas las variables de entorno actuales aplicadas al nuevo shell, y luego mate su shell principal (de alguna manera) cuando termine. 1

Peor aún, incluso podría tener un sistema en el que un proceso pueda alterar los entornos de otros procesos (creo que técnicamente puede hacerlo con depuradores). Sin embargo, dicho sistema sería muy, muy vulnerable.

Te encontrarás agregando más y más código para asegurar tales métodos, y es considerablemente más simple simplemente hacer que esté incorporado.


Que algo sea un ejecutable no evita que sea un archivo incorporado. Caso en punto:

echo y test

echoy testson utilidades con mandato POSIX ( /bin/echoy /bin/test). Sin embargo, casi todos los depósitos popular tiene una orden interna echoy test. Del mismo modo, killtambién está incorporado que está disponible como un programa. Otros incluyen:

  • sleep (no tan común)
  • time
  • false
  • true
  • printf

Sin embargo, hay algunos casos en que un comando no puede ser otra cosa que un builtin incorporado. Uno de esos es cd. Por lo general, si no se especifica la ruta completa y el nombre del comando coincide con el de un builtin incorporado, se llama a una función adecuada para ese comando. Dependiendo de la shell, el comportamiento del builtin y el del ejecutable puede diferir (esto es particularmente un problema paraecho , que tiene comportamientos muy diferentes . Si desea estar seguro del comportamiento, es preferible llamar al ejecutable usando el ruta completa y establecer variables como POSIXLY_CORRECT(incluso entonces no hay garantía real).

Técnicamente, no hay nada que le impida proporcionar un sistema operativo que también sea un shell y que tenga todos los comandos como incorporado. Cerca de este extremo se encuentra el BusyBox monolítico . BusyBox es un binario único que (según el nombre con el que se llama) puede comportarse como cualquiera de los más de 240 programas , incluido un Shell de Almquist ( ash). Si desarma PATHmientras ejecuta BusyBox ash, los programas disponibles en BusyBox aún estarán accesibles para usted sin especificar a PATH. Se acercan a ser construcciones de conchas, excepto que la concha en sí es una especie de construcción para BusyBox.


Estudio de caso: The Debian Almquist Shell ( dash)

Si observa el dashorigen, el hilo de ejecución es algo así (por supuesto, con funciones adicionales involucradas cuando se utilizan tuberías y otras cosas):

maincmdloopevaltreeevalcommand

evalcommandluego se usa findcommandpara determinar cuál es el comando. Si es una construcción, entonces :

 case CMDBUILTIN:
     if (spclbltin > 0 || argc == 0) {
         poplocalvars(1);
         if (execcmd && argc > 1)
             listsetvar(varlist.list, VEXPORT);
     }
     if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) {
         if (exception == EXERROR && spclbltin <= 0) {
             FORCEINTON;
             break;

cmdentry.u.cmdes un struct( struct builtincmd), uno de cuyos miembros es un puntero de función, con una firma típica de main: (int, char **). La evalbltinfunción llama (dependiendo de si el evalcomando incorporado es el comando o no) evalcmdo este puntero de función. Las funciones reales se definen en varios archivos de origen. echo, por ejemplo, es :

int
echocmd(int argc, char **argv)
{
    int nonl;

    nonl = *++argv ? equal(*argv, "-n") : 0;
    argv += nonl;

    do {
        int c;

        if (likely(*argv))
            nonl += print_escape_str("%s", NULL, NULL, *argv++);
        if (nonl > 0)
            break;

        c = *argv ? ' ' : '\n';
        out1c(c);
    } while (*argv);
    return 0;
}

Todos los enlaces al código fuente en esta sección están basados ​​en números de línea, por lo que pueden cambiar sin previo aviso.


1 Los sistemas POSIX tienen un cdejecutable .


Nota al margen:

Hay muchas publicaciones excelentes en Unix y Linux que se ocupan del comportamiento de shell. En particular:

Si no ha notado un patrón en las preguntas enumeradas hasta ahora, casi todas ellas involucran a Stéphane Chazelas .

muru
fuente
44
Tenga en cuenta que puede obtener el cdtexto de ayuda con help cd(lo mismo para todos los comandos integrados de shell)
Sylvain Pineau
@SylvainPineau, aunque me he vinculado al manual de bash, ese consejo generalmente no es aplicable a otros shells, como zsh.
muru
Indeed helpes un bash incorporado (para zsh, es run-help cd)
Sylvain Pineau
La descripción vinculada de la especificación POSIX no dice explícitamente que cddebe ser un shell integrado ... sino que se basa en cómo funcionan las propiedades del proceso y su transferencia en UNIX cdcomo un shell incorporado es la única implementación sencilla. Ver la respuesta de Volker Siegel .
pabouk
@pabouk de hecho (lo llama una utilidad), y luego continúa diciendo: "Dado que cd afecta el entorno de ejecución de shell actual, siempre se proporciona como un shell incorporado regularmente".
muru
8

No puede encontrar un ejecutable cdporque no hay ninguno.

cdes un comando interno de su shell (por ejemplo bash).

Uwe Plonus
fuente
7

de man which:

que devuelve los nombres de ruta de los archivos (o enlaces) que se ejecutarían en el entorno actual, si sus argumentos se hubieran dado como comandos en un shell estrictamente compatible con POSIX. Lo hace buscando en la RUTA archivos ejecutables que coincidan con los nombres de los argumentos. No sigue enlaces simbólicos.

Como podemos ver en la descripción de which, solo está comprobando PATH. Entonces, si implementó algunos bash function, no le mostrará nada. Es mejor usar el typecomando junto con which.

Por ejemplo, en el lscomando Ubuntu con alias ls --color=auto.

$ type ls
ls is aliased to `ls --color=auto'

$ which ls
/bin/ls

Y si implementa la función de prueba hello:

$ function hello() { for i in {1,2,3}; do echo Hello $i;done }
$ which hello

whichno muestra nada Pero type:

$ type hello
hello is a function
hello () 
{ 
    for i in {1,2,3};
    do
        echo Hello $i;
    done
}

En tu caso:

$ type cd
cd is a shell builtin

Esto significa que cdes un caparazón incorporado , está dentro bash. Todos los componentes de bash descritos man bashen la sección SHELL BUILTIN Command

SHELL BUILTIN COMMANDS
       Unless otherwise noted, each builtin command documented in this section
       as accepting options preceded by - accepts -- to signify the end of the
       options.   The  :, true, false, and test builtins do not accept options
       and do not treat -- specially.  The exit, logout, break, continue, let,
       and  shift builtins accept and process arguments beginning with - with‐
       out requiring --.  Other builtins that accept  arguments  but  are  not
       specified  as accepting options interpret arguments beginning with - as
       invalid options and require -- to prevent this interpretation.
c0rp
fuente
2
Mmmm,manwhich .
IQAndreas
1
Quizás debería enfatizarse más: no usar which, usar type.
tripleee