Pourquoi ne puis-je pas rediriger la sortie d'un nom de chemin d'une commande vers «cd»?

27

J'essaie d' cdaccepter un nom de répertoire redirigé vers lui à partir d'une autre commande. Aucune de ces méthodes ne fonctionne:

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

Cela fonctionne:

$ cd $(echo $HOME)

Pourquoi le premier ensemble de commandes ne fonctionne-t-il pas et y en a-t-il d'autres qui échouent également de cette façon?

Jhonathan
la source
Par d'autres, faites-vous référence à d'autres commandes ou à d'autres méthodes pour utiliser des CD qui échouent de cette façon?
Didi Kohen
@DavidKohen Je me réfère à d'autres commandes
Jhonathan
Quelques exemples notables sont ulimit, umask, popd, pushd, set, export et read.
Didi Kohen

Réponses:

32

cdn'est pas une commande externe - c'est une fonction intégrée au shell. Il s'exécute dans le contexte du shell actuel, et non, comme le font les commandes externes, dans un contexte fork / exec'd en tant que processus séparé.

Votre troisième exemple fonctionne, car le shell développe la variable et la substitution de commande avant d'appeler le cdbuiltin, de sorte que cdreçoit la valeur de ${HOME}comme argument.

Les systèmes POSIX faire un binaire cd- sur ma machine FreeBSD, il est à /usr/bin/cd, mais il ne fait pas ce que vous pensez. L'appel du binaire cdamène le shell à bifurquer / exécuter le binaire, ce qui change en effet son répertoire de travail au nom que vous passez. Cependant, dès qu'il le fait, le binaire se ferme et le processus forké / exécuté disparaît, vous ramenant à votre shell, qui se trouve toujours dans le répertoire dans lequel il se trouvait avant de commencer.

D_Bye
la source
10
Ce qui fait qu'on se demande quel est l'intérêt de la cdcommande externe ?
kojiro
23

cdne lit pas l'entrée standard. C'est pourquoi votre premier exemple ne fonctionne pas.

xargsa besoin d'un nom de commande, c'est-à-dire du nom d'un exécutable indépendant. cddoit être une commande intégrée au shell et n'aurait aucun effet (autre que la vérification que vous pouvez passer à ce répertoire et les effets secondaires potentiels qu'il peut avoir comme pour les répertoires montables automatiquement) s'il s'agissait d'un exécutable. C'est pourquoi votre deuxième exemple ne fonctionne pas.

mouviciel
la source
4

En plus de la bonne réponse existante, il convient également de mentionner qu'un tube bifurque un nouveau processus, qui a son propre répertoire de travail séparé. Par conséquent, essayer de le faire ne fonctionnera pas:

echo test | cd /

Ainsi, vous ne serez pas dans le dossier / après le retour du shell à partir de cette commande.

Mark Rejhon
la source
Toutes les commandes d'un pipeline s'exécutent dans des processus différents, donc a | b, même si aet bsont intégrées, au moins une ne s'exécute pas dans le processus shell, mais il n'y a aucune garantie de laquelle il s'agit. Par exemple, dans AT&T ksh, zshou bash -O lastpipe, best exécuté dans le processus shell en cours, donc votre code vous amènerait à / là.
Stéphane Chazelas
4

En plus des bonnes réponses déjà données: Si vous exécutez bash et que vous voulez savoir ce qu'est une "commande" comme cd, vous pouvez utiliser type

$ type cd
cd is a shell builtin

ou pourquoi pas:

$ type time
time is a shell keyword

alors que par exemple le temps gnu est normalement déjà inclus dans votre distribution préférée:

$ which time
/usr/bin/time

Okey ok vous avez l'idée, alors qu'est-ce que c'est que le type?

$ type type
type is a shell builtin

Voici un extrait de manuel 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
la source
0

Comme d'autres l'ont dit, cela ne fonctionnera pas car cdc'est une commande intégrée au shell, pas un programme externe, donc il n'a pas d'entrée standard dans laquelle vous pouvez diriger quoi que ce soit.

Mais, même si cela fonctionnait, il ne ferait pas ce que vous souhaitez: un tuyau engendre un nouveau processus et redirige la sortie standard de la première commande vers l'entrée standard de la seconde, donc seul le nouveau processus changerait son fonctionnement actuel annuaire; cela ne pouvait en aucun cas affecter le premier processus.

Massimo
la source
2
Votre deuxième paragraphe a raison. Mais au sujet du premier paragraphe: pourquoi supposez-vous que les commandes internes de shell n'ont pas de stdin? readest généralement (toujours?) un shell intégré. Il est vrai que cdignore stdin, mais ce n'est pas dû au fait qu'il est intégré.
dubiousjim
-2

Une autre option est les backticks, qui placent la sortie standard d'une commande comme argument de ligne de commande d'une deuxième commande, et sont plus portables que $(...). Par exemple:

cd `echo $HOME`

ou plus généralement;

cd `anycommand -and whatever args`

Notez que l'utilisation de backticks dépend du shell pour exécuter la commande et substituer la sortie sur la ligne de commande. La plupart des obus le supportent.

Seth Noble
la source
3
Le PO a déjà déclaré, dans sa question, que cela $(...)fonctionne. Je ne pense pas que ce soit un bon conseil de recommander plutôt les backticks, car ils ont des règles de citation beaucoup plus compliquées et sont généralement plus sujettes aux erreurs. (Voir §3.5.4 «Substitution de commande» dans le manuel de référence de Bash .)
ruakh
$ () est agréable quand il est pris en charge, mais les backticks sont plus largement pris en charge sur différents shells et systèmes. Mais je devrais reformuler ceci comme "une autre option".
Seth Noble
Différents obus, oui; mais des "systèmes" différents? Existe-t-il vraiment des shells qui prendront en charge $(...)sur un système mais pas sur un autre ??
ruakh
4
-1, il ne répond pas du tout à la question.
Bernhard
3
@ruakh & Seth: prise en charge de tous les shells POSIX $(…). Les systèmes sans shell POSIX (c'est-à-dire avec un véritable shell Bourne) seraient extrêmement anciens. Même les systèmes où se /bin/shtrouve un shell Bourne et dont vous avez besoin d'un autre chemin /usr/xpg4/bin/shpour obtenir un shell POSIX sont rares de nos jours. Recommander des backticks à quiconque n'administre pas professionnellement les anciennes boîtes Unix leur rend un mauvais service.
Gilles 'SO- arrête d'être méchant'