Est-il possible d'effectuer une substitution de command shell sans utiliser de sous-shell?

J'ai un scénario qui appelle à la substitution de command sans utiliser un sous-shell. J'ai une construction comme celle-ci:

pushd $(mktemp -d) 

Maintenant, je veux quitter et supprimer le directory temporaire en une seule fois:

 rmdir $(popd) 

Cependant, cela ne fonctionne pas car popd ne returnne pas le directory sauté (il renvoie le nouveau directory maintenant en cours) et aussi parce qu'il est exécuté dans un sous-shell.

Quelque chose comme

 dirs -l -1 ; popd &> /dev/null 

returnnera le directory sauté mais il ne peut pas être utilisé comme ceci:

 rmdir $(dirs -l -1 ; popd &> /dev/null) 

parce que le popd n'affectera que le sous-shell. Ce qui est appelé est la capacité de faire ceci:

 rmdir { dirs -l -1 ; popd &> /dev/null; } 

mais c'est une syntaxe invalide. Est-il possible d'atteindre cet effet?

(note: je sais que je peux save le directory temporaire dans une variable, j'essayais d'éviter de le faire et d'apprendre quelque chose de nouveau dans le process!)

Le choix du titre de votre question est un peu déroutant.

pushd / popd , une fonctionnalité csh copiée par bash et zsh , permet de gérer une stack de directorys mémorisés.

 pushd /some/dir 

pousse le directory de travail en cours sur une stack, puis modifie le directory de travail en cours (puis imprime /some/dir suivi du contenu de cette stack (séparés par des espaces)).

 popd 

imprime le contenu de la stack (encore une fois, séparé par des espaces), puis change l'élément supérieur de la stack et l'extrait de la stack.

(attention aussi que certains directorys y seront représentés avec leur notation ~/x ou ~user/x ).

Donc, si la stack a actuellement /a et /b , le directory courant est /here et vous exécutez:

  pushd /tmp/whatever popd 

pushd imprimera /tmp/whatever /here /a /b et popd affichera /here /a /b , pas /tmp/whatever . C'est indépendant de l'utilisation de la substitution de command ou non. popd ne peut pas être utilisé pour get le path du directory précédent et en général, sa sortie ne peut pas être post-traitée (voir le $dirstack ou $DIRSTACK de certains shells pour accéder aux éléments de cette stack de directorys)

Peut-être que vous voulez:

 pushd "$(mktemp -d)" && popd && rmdir "$OLDPWD" 

Ou

 cd "$(mktemp -d)" && cd - && rmdir "$OLDPWD" 

Bien, j'utiliserais:

 tmpdir=$(mktemp -d) || exit ( cd "$tmpdir" || exit # in a subshell # do what you have to do in that tmpdir ) rmdir "$tmpdir" 

Dans tous les cas, pushd "$(mktemp -d)" n'exécute pas pushd dans un sous-shell. Si c'est le cas, il ne peut pas changer le directory de travail. C'est mktemp qui fonctionne dans un sous-shell. Comme il s'agit d'une command séparée, elle doit s'exécuter dans un process distinct. Il écrit sa sortie sur un tuyau, et le process de la coquille le lit à l'autre bout du tuyau.

ksh93 peut éviter le process séparé lorsque la command est embeddede, mais même là, c'est encore un sous-shell (un environnement de travail différent) qui est émulé cette fois plutôt que de countr sur l'environnement séparé normalement fourni par forking. Par exemple, dans ksh93 , a=0; echo "$(a=1; echo test)"; echo "$a" a=0; echo "$(a=1; echo test)"; echo "$a" a=0; echo "$(a=1; echo test)"; echo "$a" , aucune fourchette n'est impliquée, mais echo "$a" toujours echo "$a" sorties 0 .

Ici, si vous voulez stocker la sortie de mktemp dans une variable, en même time que vous la transmettez à pushd , avec zsh , vous pourriez faire:

 pushd ${tmpdir::="$(mktemp -d)"} 

Avec d'autres coquilles de Bourne:

 unset tmpdir pushd "${tmpdir=$(mktemp -d)}" 

Ou pour utiliser la sortie de $(mktemp -d) plusieurs fois sans la stocker explicitement dans une variable, vous pouvez utiliser les fonctions anonymes de zsh :

 (){pushd ${1?} && cd - && rmdir $1} "$(mktemp -d)" 

Vous pouvez d'abord dissocier le directory avant de le quitter:

 rmdir "$(pwd -P)" && popd 

ou

 rmdir "$(pwd -P)" && cd .. # yes, .. is still usable 

mais notez que pushd et popd sont vraiment des outils pour les shells interactifs, pas pour les scripts (c'est pourquoi ils sont tellement bavard, les commands de script réelles sont silencieuses quand elles réussissent).

Dans bash, dirs fournit une list de directorys mémorisés par la méthode pushd / popd.

En outre, dirs -1 affiche le dernier directory inclus dans la list.

Donc, pour supprimer le directory créé précédemment en exécutant pushd $(mktmp -d) , utilisez:

 rmdir $(dirs -1) 

Et puis, popd le popd déjà retiré de la list:

 popd > /dev/null 

Tout en une ligne:

 rmdir $(dirs -1); popd > /dev/null 

Et en ajoutant l'option (-l) pour éviter la notation ~/x ou ~user/x :

 rmdir $(dirs -l -1); popd > /dev/null 

Ce qui est remarquablement similaire à la ligne que vous avez demandée.
Sauf que je n'utiliserais pas &> comme il cacherait tout rapport d'erreur par popd .

Note: Le directory restra après rmdir car c'est le pwd à ce point. Et sera effectivement dissocié après la command popd (aucun lien restant utilisé).


Il y a une option à utiliser, pour les shells qui supportent la variable "OLDPWD" (la plupart des shells de type bourne: ksh, bash, zsh ont $OLDPWD ). Il est intéressant de noter que ksh n'implémente pas dirs, pod, pushd par défaut (lksh, dash et d'autres n'ont pas de popd disponible, donc ne sont pas utilisables ici):

 popd && rmdir "$OLDPWD" 

Ou, plus idiomatique (même list de coquilles que ci-dessus):

 popd && rmdir ~-