¿Por qué no puedo redirigir una salida de nombre de ruta de un comando a "cd"?

27

Estoy tratando de cdaceptar un nombre de directorio redirigido a él desde otro comando. Ninguno de estos métodos funciona:

$ echo $HOME | cd
$ echo $HOME | xargs cd

Esto funciona:

$ cd $(echo $HOME)

¿Por qué el primer conjunto de comandos no funciona, y hay otros que también fallan de esta manera?

Jhonathan
fuente
Por otros, ¿te refieres a otros comandos u otros métodos para usar cd que fallan de esta manera?
Didi Kohen
@DavidKohen Me refiero a otros comandos
Jhonathan
Algunos ejemplos notables son ulimit, umask, popd, pushd, set, export y read.
Didi Kohen

Respuestas:

32

cdno es un comando externo, es una función interna de shell. Se ejecuta en el contexto del shell actual, y no, como lo hacen los comandos externos, en un contexto fork / exec'd como un proceso separado.

Su tercer ejemplo funciona, porque el shell expande la variable y la sustitución del comando antes de llamar al cdbuiltin, de modo que cdrecibe el valor de ${HOME}como argumento.

Los sistemas POSIX hacer tener un binario cd- en mi máquina FreeBSD, está en /usr/bin/cd, pero no hace lo que usted piensa. Llamar al binario cdhace que el shell bifurque / ejecute el binario, lo que de hecho cambia su directorio de trabajo al nombre que pasa. Sin embargo, tan pronto como lo hace, el binario se cierra y el proceso bifurcado / ejecutado desaparece, regresando a su shell, que todavía está en el directorio en el que estaba antes de comenzar.

D_Bye
fuente
10
Lo que hace que uno se pregunte ¿Cuál es el punto del cdcomando externo ?
kojiro
23

cdno lee la entrada estándar. Es por eso que tu primer ejemplo no funciona.

xargsnecesita un nombre de comando, es decir, un nombre de un ejecutable independiente. cddebe ser un comando integrado de shell y no tendría ningún efecto (aparte de verificar que puede cambiar a ese directorio y los posibles efectos secundarios que podría tener para los directorios automontables) si fuera un ejecutable. Es por eso que su segundo ejemplo no funciona.

Mouviciel
fuente
4

Además de la buena respuesta existente, también vale la pena mencionar que una tubería bifurca un nuevo proceso, que tiene su propio directorio de trabajo separado. Por lo tanto, tratar de hacer esto no funcionará:

echo test | cd /

Por lo tanto, no estará en la carpeta / después de que el shell regrese de este comando.

Mark Rejhon
fuente
Todos los comandos en una tubería de ejecución en diferentes procesos, por lo que en a | b, incluso si ay bestán builtin, al menos uno, entonces no se ejecuta en el proceso de shell, pero no hay garantía de lo que de cuál se trata. Por ejemplo, en AT&T ksh, zsho bash -O lastpipe, bse ejecuta en el proceso de shell actual, por lo que su código lo llevaría a / allí.
Stéphane Chazelas
4

Además de las respuestas correctas ya dadas: si ejecuta bash y desea saber qué es un "comando" como cd , puede usar type

$ type cd
cd is a shell builtin

o por qué no:

$ type time
time is a shell keyword

mientras que, por ejemplo, el tiempo de GNU normalmente ya está incluido en su distribución favorita:

$ which time
/usr/bin/time

Okey okey tienes la idea, entonces, ¿qué diablos es el tipo?

$ type type
type is a shell builtin

Aquí hay un fragmento de manual de bash:

       type [-aftpP] name [name ...]
          With no options, indicate how each name would be interpreted  if  used  as  a
          command name.  If the -t option is used, type prints a string which is one of
          alias, keyword, function, builtin,  or  file  if  name  is  an  alias,  shell
          reserved word, function, builtin, or disk file, respectively.  If the name is
          not found, then nothing is printed, and an exit status of false is  returned.
          If  the -p option is used, type either returns the name of the disk file that
          would be executed if name were specified as a command  name,  or  nothing  if
          ‘‘type  -t  name’’ would not return file.  The -P option forces a PATH search
          for each name, even if ‘‘type -t name’’ would not return file.  If a  command
          is  hashed,  -p  and -P print the hashed value, not necessarily the file that
          appears first in PATH.  If the -a option is used,  type  prints  all  of  the
          places  that  contain  an  executable  named name.  This includes aliases and
          functions, if and only if the -p option is  not  also  used.   The  table  of
          hashed  commands  is  not  consulted when using -a.  The -f option suppresses
          shell function lookup, as with the command builtin.  type returns true if any
          of the arguments are found, false if none are found.
3molo
fuente
0

Como han dicho otros, no funcionará porque cdes un comando integrado de shell, no un programa externo, por lo que no tiene ninguna entrada estándar en la que pueda canalizar nada.

Pero, incluso si funcionara, no haría lo que desea: una tubería genera un nuevo proceso y redirige la salida estándar del primer comando a la entrada estándar del segundo, por lo que solo el nuevo proceso cambiaría su funcionamiento actual directorio; Esto no podría afectar el primer proceso de ninguna manera.

Massimo
fuente
2
Tu segundo párrafo es correcto. Pero volvamos al primer párrafo: ¿por qué supones que las conchas incorporadas no tienen stdin? reades generalmente (¿siempre?) Es cierto que cdignora el stdin, pero esto no se debe a que sea un incorporado.
dubiousjim
-2

Otra opción son los backticks, que colocan la salida estándar de un comando como argumento de línea de comando de un segundo comando, y son más portátiles que $(...). Por ejemplo:

cd `echo $HOME`

o más generalmente;

cd `anycommand -and whatever args`

Tenga en cuenta que el uso de backticks depende del shell para ejecutar el comando y sustituir la salida en la línea de comando. La mayoría de los proyectiles lo soportan.

Seth Noble
fuente
3
El OP ya declaró, en su pregunta, que $(...)funciona. No creo que sea un buen consejo recomendar backticks, ya que tienen reglas de cotización mucho más complicadas y generalmente son más propensas a errores. (Ver § 3.5.4 "Sustitución de comandos" en el Manual de referencia de Bash .)
ruakh
$ () es bueno cuando es compatible, pero los backticks son más compatibles en diferentes shells y sistemas. Pero debería reformular esto como "otra opción".
Seth Noble
Conchas diferentes, sí; pero diferentes "sistemas"? ¿Realmente hay shells que admitan $(...)en un sistema pero no en otro?
ruakh
44
-1, no responde a la pregunta en absoluto.
Bernhard
3
@ruakh y Seth: Todos los shells POSIX son compatibles $(…). Los sistemas sin un shell POSIX (es decir, con un shell Bourne genuino) serían extremadamente antiguos. Incluso los sistemas donde /bin/shhay un shell Bourne y necesita otra ruta, como /usr/xpg4/bin/shobtener un shell POSIX, son raros hoy en día. Recomendar backticks a cualquiera que no administre profesionalmente unix boxen antiguo les está perjudicando.
Gilles 'SO- deja de ser malvado'