Ottenere il genitore di una directory in Bash


209

Se ho un percorso file come ...

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

Come posso cambiare la stringa in modo che sia la directory principale?

per esempio

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

4
Puoi semplicemente usare ' ..', ma forse non è esattamente quello che avevi in ​​mente.
Jonathan Leffler l'

Risposte:


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

Funziona anche in presenza di una barra.


14
le citazioni all'interno sono importanti o perderai tutti i tuoi dati
catamphetamine

11
e la variante che sto usando per ottenere il genitore dell'attuale directory di lavoro: parentdir=$(dirname `pwd`)
TheGrimmScientist

3
Nota, questo metodo non è sicuro per i nomi "brutti". Un nome come "-rm -rf" interromperà il comportamento desiderato. Probabilmente "." anche. Non so se questo è il modo migliore, ma ho creato un separato "correttezza focalizzata" domanda qui: stackoverflow.com/questions/40700119/...
VasiliNovikov

4
@Christopher Shroba Sì, ho omesso le virgolette e poi il mio HDD è stato parzialmente cancellato. Non ricordo esattamente cosa sia successo: presumibilmente una directory bianca non quotata ha fatto cancellare la directory principale anziché quella di destinazione - il mio script ha appena cancellato la cartella / home / xxx /.
catamphetamine,

3
oh va bene, quindi non c'è nulla nel comando che provocherebbe la perdita di dati se non si sta già eseguendo un comando di eliminazione, giusto? Era solo una questione del percorso che stavi cercando di rimuovere per dividere un personaggio spaziale e rimuovere molto più del previsto?
Christopher Shroba,

18

... ma ciò che è "visto qui " è rotto. Ecco la soluzione:

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

14

Basta usare echo $(cd ../ && pwd)mentre si lavora nella directory di cui si desidera scoprire la directory principale. Questa catena ha anche l'ulteriore vantaggio di non avere barre finali.


Non hai bisogno di echoqui.
gniourf_gniourf,

14

Chiaramente la directory principale è data semplicemente aggiungendo il nome del file punto-punto:

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

Ma devi volere il percorso risolto (un percorso assoluto senza componenti del percorso punto-punto):

/home/smith/Desktop             # resolved path

Il problema con le risposte migliori che usano dirnameè che non funzionano quando si immette un percorso con punti-punti:

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

Questo è più potente :

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

Puoi dargli da mangiare /home/smith/Desktop/Test/.. , ma anche percorsi più complessi come:

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

EDIT: se non funziona, controlla che il tuo cdnon sia stato modificato, come talvolta è pratica comune,

$ 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'utilizzo evalNON è eccezionale se esiste la possibilità di analizzare l'input dell'utente che potrebbe portarti in un posto in cui non ti aspetti o eseguire cose brutte contro il tuo sistema. Puoi usare pushd $dir 2>&1 >/dev/null && pwd && popd 2>&1 >/dev/nullinvece di eval?
dragon788,

2
Non è necessario evalaffatto: basta parentdir=$(cd -- "$dir" && pwd). Poiché cdviene eseguito in una subshell, non è necessario cdtornare a dove eravamo. Il --è qui nel caso l'espansione $dirinizia con un trattino. L' &&è solo per evitare parentdirdi avere un contenuto non vuoto quando cdnon ha riuscine.
gniourf_gniourf

8

Motivazione per un'altra risposta

Mi piace un codice molto breve, chiaro e garantito. Punto bonus se non esegue un programma esterno, dal giorno in cui è necessario elaborare un numero enorme di voci, sarà notevolmente più veloce.

Principio

Non sei sicuro di ciò che ti garantisce e desideri, quindi offri comunque.

Se hai garanzie puoi farlo con un codice molto breve. L'idea è quella di utilizzare la funzione di sostituzione del testo bash per tagliare l'ultima barra e quanto segue.

Risposta da casi semplici a casi più complessi della domanda originale.

Se il percorso è garantito per terminare senza alcuna barra (dentro e fuori)

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

Se il percorso è garantito per terminare esattamente con una barra (dentro e fuori)

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

Se il percorso di input può terminare con zero o una barra (non più) e si desidera che il percorso di output termini senza barra

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

Se il percorso di input può avere molte barre estranee e si desidera che il percorso di output termini senza barra

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
Risposta fantastica. Dal momento che sembra che stia cercando un modo per farlo dall'interno di uno script (trova la directory corrente dello script e il suo genitore e fai qualcosa in relazione a dove vive lo script) questa è un'ottima risposta e puoi avere ancora più controllo se l'input ha una barra finale o meno usando l'espansione dei parametri rispetto alla ${BASH_SOURCE[0]}quale sarebbe il percorso completo dello script se non fosse stato fornito tramite sourceo ..
dragon788,

7

Se /home/smith/Desktop/Test/../è quello che vuoi:

dirname 'path/to/child/dir'

come visto qui .


1
scusa, intendo come uno script bash, ho una variabile con il percorso del file
YTKColumba

L'esecuzione di quel codice mi dà l'errore bash: dirname 'Test': syntax error: invalid arithmetic operator (error token is "'Test'"). Anche il codice collegato è sbagliato.
Michael Hoffman,

prova a correre dirname Testdalla directory Testin.
Jon Egeland l'

1

A seconda che tu abbia bisogno di percorsi assoluti, potresti voler fare un passo in più:

child='/home/smith/Desktop/Test/'
parent=$(dirname "$child")
abs_parent=$(realpath "$parent")

0

usa questo: export MYVAR="$(dirname "$(dirname "$(dirname "$(dirname $PWD)")")")"se vuoi la quarta directory principale

export MYVAR="$(dirname "$(dirname "$(dirname $PWD)")")" se si desidera la terza directory principale

export MYVAR="$(dirname "$(dirname $PWD)")" se si desidera la directory 2 ° genitore


Grazie! Proprio quello che stavo cercando.
Josue Abarca,

0

brutto ma efficiente

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

Iniziato dall'idea / commento Charles Duffy - 17 dic 14 alle 5:32 sull'argomento Ottieni il nome della directory corrente (senza percorso completo) in uno 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

Se per qualsiasi motivo si sono interessati a navigare su un determinato numero di directory si potrebbe anche fare: nth_path=$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && cd ../../../ && pwd). Ciò darebbe 3 directory dei genitori


perché $ {BASH_SOURCE [0]} invece del $ BASH_SOURCE più semplice
Nam G VU
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.