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 ~-