Obtenir le parent d'un répertoire dans Bash

209

Si j'ai un chemin de fichier tel que ...

/home/smith/Desktop/Test
/home/smith/Desktop/Test/

Comment changer la chaîne pour qu'elle devienne le répertoire parent?

par exemple

/home/smith/Desktop
/home/smith/Desktop/
YTKColumba
la source
4
Vous pouvez simplement utiliser ' ..', mais ce n'est peut-être pas tout à fait ce que vous aviez en tête.
Jonathan Leffler

Réponses:

332
dir=/home/smith/Desktop/Test
parentdir="$(dirname "$dir")"

Fonctionne également s'il y a une barre oblique.

Michael Hoffman
la source
14
les citations à l'intérieur sont importantes ou vous perdrez toutes vos données
catamphétamine
11
et la variante que j'utilise pour obtenir le parent du répertoire de travail actuel: parentdir=$(dirname `pwd`)
TheGrimmScientist
3
Notez que cette méthode n'est pas sécurisée pour les noms "moches". Un nom comme "-rm -rf" rompra le comportement souhaité. Probablement "." le fera aussi. Je ne sais pas si c'est la meilleure façon, mais j'ai créé une question distincte "axée sur l'exactitude" ici: stackoverflow.com/questions/40700119/…
VasiliNovikov
4
@Christopher Shroba Oui, j'ai omis les guillemets, puis mon disque dur s'est retrouvé partiellement essuyé. Je ne me souviens pas exactement de ce qui s'est passé: vraisemblablement un répertoire blanc non guidé l'a fait effacer le répertoire parent au lieu du répertoire cible - mon script vient d'effacer le dossier / home / xxx /.
catamphétamine
3
oh d'accord, il n'y a donc rien sur la commande qui entraînerait la perte de données, sauf si vous émettez déjà une commande de suppression, non? C'était juste une question de chemin que vous essayiez de supprimer, de vous séparer d'un personnage d'espace et de supprimer beaucoup plus que prévu?
Christopher Shroba
18

... mais ce qui est "vu ici " est cassé. Voici le correctif:

> pwd
/home/me
> x='Om Namah Shivaya'
> mkdir "$x" && cd "$x"
/home/me/Om Namah Shivaya
> parentdir="$(dirname "$(pwd)")"
> echo $parentdir
/home/me
Andreas Spindler
la source
14

Utilisez-le echo $(cd ../ && pwd)tout en travaillant dans le répertoire dont vous souhaitez découvrir le répertoire parent. Cette chaîne présente également l'avantage supplémentaire de ne pas avoir de barres obliques de fin.

Endu Ad
la source
Vous n'avez pas besoin echoici.
gniourf_gniourf
14

De toute évidence, le répertoire parent est donné en ajoutant simplement le nom de fichier point à point:

/home/smith/Desktop/Test/..     # unresolved path

Mais vous devez vouloir le chemin résolu (un chemin absolu sans aucun composant de chemin point à point):

/home/smith/Desktop             # resolved path

Le problème avec les meilleures réponses qui utilisent dirname, c'est qu'elles ne fonctionnent pas lorsque vous entrez un chemin avec des points:

$ dir=~/Library/../Desktop/../..
$ parentdir="$(dirname "$dir")"
$ echo $parentdir
/Users/username/Library/../Desktop/..   # not fully resolved

C'est plus puissant :

dir=/home/smith/Desktop/Test
parentdir=`eval "cd $dir;pwd;cd - > /dev/null"`

Vous pouvez le nourrir /home/smith/Desktop/Test/.., mais aussi des chemins plus complexes comme:

$ dir=~/Library/../Desktop/../..
$ parentdir=`eval "cd $dir;pwd;cd - > /dev/null"`
$ echo $parentdir
/Users                                  # the fully resolved path!

EDIT: Si cela ne fonctionne pas, vérifiez que votre cd n'a pas été modifié, comme c'est parfois le cas,

$ which cd
cd is a function  #cd was modified to print extra info, so use "builtin cd" to override
cd ()
{
    builtin cd "$@";
    ls -FGAhp
}
...
Riaz Rizvi
la source
L'utilisation evaln'est PAS géniale s'il y a une chance d'analyser les entrées des utilisateurs qui pourraient vous mettre dans un endroit inattendu ou exécuter de mauvaises choses contre votre système. Pouvez-vous utiliser à la pushd $dir 2>&1 >/dev/null && pwd && popd 2>&1 >/dev/nullplace du eval?
dragon788
2
Vous n'avez pas besoin evaldu tout: faites-le parentdir=$(cd -- "$dir" && pwd). Puisque le cdest exécuté en sous-shell, vous n'avez pas besoin de cdrevenir là où nous étions. Le --est ici au cas où l'expansion de $dircommence par un trait d'union. Le but &&est simplement d'empêcher parentdird'avoir un contenu non vide lorsque le cdn'a pas réussi.
gniourf_gniourf
8

Motivation pour une autre réponse

J'aime le code très court, clair et garanti. Point bonus s'il n'exécute pas de programme externe, car le jour où vous aurez besoin de traiter un grand nombre d'entrées, il sera sensiblement plus rapide.

Principe

Je ne sais pas quelles garanties vous avez et voulez, donc offrez quand même.

Si vous avez des garanties, vous pouvez le faire avec un code très court. L'idée est d'utiliser la fonction de substitution de texte bash pour couper la dernière barre oblique et tout ce qui suit.

Réponse de cas simples à plus complexes de la question d'origine.

Si le chemin est garanti de se terminer sans aucune barre oblique (entrée et sortie)

P=/home/smith/Desktop/Test ; echo "${P%/*}"
/home/smith/Desktop

Si le chemin est garanti de se terminer avec exactement une barre oblique (entrée et sortie)

P=/home/smith/Desktop/Test/ ; echo "${P%/*/}/"
/home/smith/Desktop/

Si le chemin d'entrée peut se terminer par zéro ou une barre oblique (pas plus) et que vous souhaitez que le chemin de sortie se termine sans barre oblique

for P in \
    /home/smith/Desktop/Test \
    /home/smith/Desktop/Test/
do
    P_ENDNOSLASH="${P%/}" ; echo "${P_ENDNOSLASH%/*}"
done

/home/smith/Desktop
/home/smith/Desktop

Si le chemin d'entrée peut avoir de nombreuses barres obliques et que vous souhaitez que le chemin de sortie se termine sans barre oblique

for P in \
    /home/smith/Desktop/Test \
    /home/smith/Desktop/Test/ \
    /home/smith///Desktop////Test// 
do
    P_NODUPSLASH="${P//\/*(\/)/\/}"
    P_ENDNOSLASH="${P_NODUPSLASH%%/}"
    echo "${P_ENDNOSLASH%/*}";   
done

/home/smith/Desktop
/home/smith/Desktop
/home/smith/Desktop
Stéphane Gourichon
la source
1
Réponse fantastique. Comme il semble qu'il cherchait un moyen de le faire à partir d'un script (trouver le répertoire actuel du script et son parent et faire quelque chose par rapport à l'emplacement du script), c'est une excellente réponse et vous pouvez encore plus contrôler si l'entrée a une barre oblique de fin ou non en utilisant l'expansion des paramètres par rapport à ${BASH_SOURCE[0]}laquelle serait le chemin d'accès complet au script s'il n'était pas fourni via sourceou ..
dragon788
7

Si /home/smith/Desktop/Test/../c'est ce que vous voulez:

dirname 'path/to/child/dir'

comme on le voit ici .

Jon Egeland
la source
1
désolé, je veux dire dans comme un script bash, j'ai une variable avec le chemin du fichier
YTKColumba
L'exécution de ce code me donne l'erreur bash: dirname 'Test': syntax error: invalid arithmetic operator (error token is "'Test'"). Le code lié est également incorrect.
Michael Hoffman
essayez de simplement lancer à dirname Testpartir du répertoire Test.
Jon Egeland
1

Selon que vous avez besoin de chemins absolus, vous voudrez peut-être faire un pas supplémentaire:

child='/home/smith/Desktop/Test/'
parent=$(dirname "$child")
abs_parent=$(realpath "$parent")
Marcelo Lacerda
la source
0

utilisez ceci: export MYVAR="$(dirname "$(dirname "$(dirname "$(dirname $PWD)")")")"si vous voulez le 4ème répertoire parent

export MYVAR="$(dirname "$(dirname "$(dirname $PWD)")")" si vous voulez le 3ème répertoire parent

export MYVAR="$(dirname "$(dirname $PWD)")" si vous voulez le 2ème répertoire parent

Kaustubh
la source
Merci !, juste ce que je cherchais.
Josue Abarca
0

moche mais efficace

function Parentdir()

{

local lookFor_ parent_ switch_ i_

lookFor_="$1"

#if it is not a file, we need the grand parent
[ -f "$lookFor_" ] || switch_="/.."

#length of search string
i_="${#lookFor_}"

#remove string one by one until it make sens for the system
while [ "$i_" -ge 0 ] && [ ! -d "${lookFor_:0:$i_}" ];
do
    let i_--
done

#get real path
parent_="$(realpath "${lookFor_:0:$i_}$switch_")" 

#done
echo "
lookFor_: $1
{lookFor_:0:$i_}: ${lookFor_:0:$i_}
realpath {lookFor_:0:$i_}: $(realpath ${lookFor_:0:$i_})
parent_: $parent_ 
"

}

    lookFor_: /home/Om Namah Shivaya
{lookFor_:0:6}: /home/
realpath {lookFor_:0:6}: /home
parent_: /home 


lookFor_: /var/log
{lookFor_:0:8}: /var/log
realpath {lookFor_:0:8}: /UNIONFS/var/log
parent_: /UNIONFS/var 


lookFor_: /var/log/
{lookFor_:0:9}: /var/log/
realpath {lookFor_:0:9}: /UNIONFS/var/log
parent_: /UNIONFS/var 


lookFor_: /tmp//res.log/..
{lookFor_:0:6}: /tmp//
realpath {lookFor_:0:6}: /tmp
parent_: / 


lookFor_: /media/sdc8/../sdc8/Debian_Master//a
{lookFor_:0:35}: /media/sdc8/../sdc8/Debian_Master//
realpath {lookFor_:0:35}: /media/sdc8/Debian_Master
parent_: /media/sdc8 


lookFor_: /media/sdc8//Debian_Master/../Debian_Master/a
{lookFor_:0:44}: /media/sdc8//Debian_Master/../Debian_Master/
realpath {lookFor_:0:44}: /media/sdc8/Debian_Master
parent_: /media/sdc8 


lookFor_: /media/sdc8/Debian_Master/../Debian_Master/For_Debian
{lookFor_:0:53}: /media/sdc8/Debian_Master/../Debian_Master/For_Debian
realpath {lookFor_:0:53}: /media/sdc8/Debian_Master/For_Debian
parent_: /media/sdc8/Debian_Master 


lookFor_: /tmp/../res.log
{lookFor_:0:8}: /tmp/../
realpath {lookFor_:0:8}: /
parent_: /
magoofromparis
la source
0

Partant de l'idée / du commentaire Charles Duffy - 17 décembre 14 à 17h32 sur le sujet Obtenir le nom du répertoire actuel (sans chemin complet) dans un script Bash

#!/bin/bash
#INFO : /programming/1371261/get-current-directory-name-without-full-path-in-a-bash-script
# comment : by Charles Duffy - Dec 17 '14 at 5:32
# at the beginning :



declare -a dirName[]

function getDirNames(){
dirNr="$(  IFS=/ read -r -a dirs <<<"${dirTree}"; printf '%s\n' "$((${#dirs[@]} - 1))"  )"

for(( cnt=0 ; cnt < ${dirNr} ; cnt++))
  do
      dirName[$cnt]="$( IFS=/ read -r -a dirs <<<"$PWD"; printf '%s\n' "${dirs[${#dirs[@]} - $(( $cnt+1))]}"  )"
      #information – feedback
      echo "$cnt :  ${dirName[$cnt]}"
  done
}

dirTree=$PWD;
getDirNames;
kris
la source
0

si , pour quelque raison que ce qui vous intéresse pour naviguer dans un certain nombre spécifique de répertoires que vous pouvez également faire: nth_path=$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && cd ../../../ && pwd). Cela donnerait 3 répertoires aux parents

ClimbingTheCurve
la source
pourquoi $ {BASH_SOURCE [0]} au lieu de $ BASH_SOURCE plus simple
Nam G VU