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/

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.


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

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.


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
}
...

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

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 .


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")

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


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_: /

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;

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


pourquoi $ {BASH_SOURCE [0]} au lieu de $ BASH_SOURCE plus simple
Nam G VU
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.