Bash'te bir dizinin üst öğesini alma


207

Böyle bir dosya yolum varsa ...

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

Dizeyi üst dizin olacak şekilde nasıl değiştirebilirim?

Örneğin

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

4
Sadece ' ..' kullanabilirsiniz , ancak belki de aklınızdaki şey bu olmayabilir.
Jonathan Leffler

Yanıtlar:


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

Sonunda eğik çizgi varsa da çalışır.


14
içindeki alıntılar önemlidir veya tüm verilerinizi kaybedersiniz
catamphetamine

10
ve mevcut çalışma dizininin üst öğesini almak için kullandığım değişken: parentdir=$(dirname `pwd`)
TheGrimmScientist

3
Bu yöntem "çirkin" adlar için güvensizdir. "-Rm -rf" gibi bir ad istenen davranışı bozacaktır. Muhtemelen "." olacak. Bunun en iyi yol olup olmadığını bilmiyorum, ancak burada ayrı bir "doğruluk odaklı" soru oluşturdum: stackoverflow.com/questions/40700119/…
VasiliNovikov

4
@Christopher Shroba Evet, alıntıları atladım ve HDD'm kısmen sildi. Tam olarak ne olduğunu hatırlamıyorum: büyük olasılıkla sıralanmamış beyaz dizini hedef yerine ana dizini sildi - betiğim sadece / home / xxx / klasörünü sildi.
catamphetamine

3
oh tamam, bu yüzden zaten bir silme komutu vermedikçe herhangi bir verinin kaybolmasına neden olacak komut hakkında hiçbir şey yok, değil mi? Bu sadece bir boşluk karakterine bölünmek ve beklenenden çok daha fazlasını kaldırmak için kaldırmaya çalıştığınız yol meselesiydi?
Christopher Shroba

18

... ama burada görülen şey bozuldu. İşte düzeltme:

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

14

echo $(cd ../ && pwd)Üst dizinini bulmak istediğiniz dizinde çalışırken kullanın . Bu zincir aynı zamanda sondaki eğik çizgilere sahip olmama avantajına sahiptir.


echoBurada ihtiyacın yok.
gniourf_gniourf

14

Üst dizin açıkça nokta-nokta dosya adı eklenerek verilir:

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

Ancak çözümlenmiş yolu (herhangi bir nokta-nokta yolu bileşeni olmayan mutlak bir yol) istemeniz gerekir :

/home/smith/Desktop             # resolved path

Kullanan en iyi yanıtlardaki sorun, dirnamenokta noktalı bir yol girdiğinizde çalışmaz olmalarıdır:

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

Bu daha güçlü :

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

Besleyebilirsiniz /home/smith/Desktop/Test/.., ancak daha karmaşık yollar da olabilir:

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

EDIT: Eğer işe yaramazsa cd, bazen yaygın uygulama gibi,

$ which cd
cd is a function  #cd was modified to print extra info, so use "builtin cd" to override
cd ()
{
    builtin cd "$@";
    ls -FGAhp
}
...

evalSizi beklemediğiniz bir yere koyabilecek veya sisteminize karşı kötü şeyler çalıştırabilecek kullanıcı girişlerini ayrıştırma şansı varsa kullanmak harika DEĞİLDİR. pushd $dir 2>&1 >/dev/null && pwd && popd 2>&1 >/dev/nullBunun yerine kullanabilir misiniz eval?
dragon788

2
Hiç ihtiyacınız evalyok: sadece yapın parentdir=$(cd -- "$dir" && pwd). Yana cdbir kabuktaki çalıştırılır, sen gerekmez cdkaldığımız yerden devam arkasına. --Diye burada genişlemesi olan $dirbir tire ile başlar. Bu &&, işe yaramadığında parentdirboş olmayan bir içeriğe sahip cdolmaktan kaçınmaktır.
gniourf_gniourf

8

Başka bir cevap için motivasyon

Çok kısa, net, garantili kodu seviyorum. Harici bir program çalıştırmazsa bonus noktası, çok sayıda girişi işlemden geçirmeniz gereken günden beri belirgin şekilde daha hızlı olacaktır.

Prensip

Ne garanti ve istediğinizi emin değilim, bu yüzden zaten teklif.

Garantileriniz varsa bunu çok kısa kod ile yapabilirsiniz. Buradaki fikir, son eğik çizgiyi ve ardından gelenleri kesmek için bash metin değiştirme özelliğini kullanmaktır.

Orijinal sorunun basit ve daha karmaşık vakalarına cevap verin.

Yolun eğik çizgi olmadan biteceği garanti edilirse (içeri ve dışarı)

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

Yolun tam olarak bir eğik çizgi ile bitmesi garanti edilirse (içeri ve dışarı)

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

Giriş yolu sıfır veya bir eğik çizgi ile bitebilirse (daha fazla değil) ve çıktı yolunun eğik çizgi olmadan bitmesini istiyorsanız

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

Giriş yolunda çok sayıda eğik çizgi varsa ve çıktı yolunun eğik çizgi olmadan bitmesini istiyorsanız

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
Harika bir cevap. Bir komut dosyasından bunu yapmanın bir yolunu arıyor gibi göründüğü için (komut dosyasının geçerli dizinini ve üst öğesini bulun ve komut dosyasının yaşadığı yere göre bir şeyler yapın) bu harika bir cevaptır ve daha fazla kontrol sahibi olabilirsiniz. giriş karşı parametre genişleme kullanarak eğik çizgi ya da değil ${BASH_SOURCE[0]}bunun üzerinden kaynaklı değildi eğer komut için tam yolu olurdu sourceveya ..
dragon788

7

İstediğiniz /home/smith/Desktop/Test/../şey ise:

dirname 'path/to/child/dir'

burada görüldüğü gibi .


1
üzgünüm, bir bash betiği gibi yani, dosya yolu ile bir değişkenim var
YTKColumba

Bu kodu çalıştırmak bana hata veriyor bash: dirname 'Test': syntax error: invalid arithmetic operator (error token is "'Test'"). Bağlantılı kod da yanlış.
Michael Hoffman

Sadece denemenizi dirname Testdizinden Testiçindedir.
Jon Egeland

1

Mutlak yollara ihtiyacınız olup olmadığına bağlı olarak, fazladan bir adım atmak isteyebilirsiniz:

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

0

bunu kullanın: export MYVAR="$(dirname "$(dirname "$(dirname "$(dirname $PWD)")")")"4. üst dizin istiyorsanız

export MYVAR="$(dirname "$(dirname "$(dirname $PWD)")")" 3. üst dizin istiyorsanız

export MYVAR="$(dirname "$(dirname $PWD)")" 2. üst dizin istiyorsanız


Teşekkürler !, tam aradığım şey.
Josue Abarca

0

çirkin ama verimli

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

Başlayan fikir / yorum Charles Duffy - Dec 17 '14 zaman 5:32 konusunda Konuyla ilgili Bash script geçerli dizin adı (tam yol olmadan) alın

#!/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

Sebebi ne olursa olsun sen dizinleri belirli sayıda yukarı gezinme ilgilenen varsa da yapabilirdi: nth_path=$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && cd ../../../ && pwd). Bu, 3 ebeveyn dizinini bırakacaktır


neden daha basit $ BASH_SOURCE yerine $ {BASH_SOURCE [0]}
Nam G VU
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.