Mendapatkan induk dari direktori di Bash

209

Jika saya memiliki jalur file seperti ...

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

Bagaimana cara mengubah string sehingga akan menjadi direktori induk?

misalnya

/home/smith/Desktop
/home/smith/Desktop/
YTKColumba
sumber
4
Anda cukup menggunakan ' ..', tapi mungkin itu tidak sesuai yang Anda pikirkan.
Jonathan Leffler

Jawaban:

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

Bekerja jika ada garis miring, juga.

Michael Hoffman
sumber
14
kutipan di dalamnya penting atau Anda akan kehilangan semua data Anda
catamphetamine
11
dan varian yang saya gunakan untuk mendapatkan induk dari direktori kerja saat ini: parentdir=$(dirname `pwd`)
TheGrimmScientist
3
Catatan, metode ini tidak aman untuk nama "jelek". Nama seperti "-rm -rf" akan mematahkan perilaku yang diinginkan. Mungkin "." akan juga. Saya tidak tahu apakah itu cara terbaik, tetapi saya telah membuat pertanyaan "fokus-benar" yang terpisah di sini: stackoverflow.com/questions/40700119/…
VasiliNovikov
4
@Christopher Shroba Ya, saya menghilangkan tanda kutip dan kemudian HDD saya sebagian terhapus. Saya tidak ingat persis apa yang terjadi: mungkin direktori whitespaced yang tidak dikutip membuatnya menghapus dir induk daripada yang target - skrip saya baru saja menghapus folder / home / xxx /.
catamphetamine
3
oh oke, jadi tidak ada apa-apa tentang perintah yang akan menyebabkan data hilang kecuali Anda sudah mengeluarkan perintah hapus, kan? Itu hanya masalah jalur yang Anda coba hapus untuk mendapatkan pemisahan pada karakter ruang dan menghapus lebih dari yang diharapkan?
Christopher Shroba
18

... tapi apa yang "terlihat di sini " rusak. Inilah solusinya:

> 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
sumber
14

Cukup gunakan echo $(cd ../ && pwd)saat bekerja di direktori yang direktori induknya ingin Anda ketahui. Rantai ini juga memiliki manfaat tambahan karena tidak memiliki garis miring.

Iklan Endu
sumber
Anda tidak perlu di echosini.
gniourf_gniourf
14

Jelas direktori induk diberikan hanya dengan menambahkan nama file dot-dot:

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

Tetapi Anda harus menginginkan jalur yang diselesaikan (jalur absolut tanpa komponen jalur titik-titik):

/home/smith/Desktop             # resolved path

Masalah dengan jawaban teratas yang digunakan dirname, adalah bahwa mereka tidak berfungsi ketika Anda memasukkan jalur dengan titik-titik:

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

Ini lebih kuat :

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

Anda bisa memberinya makan /home/smith/Desktop/Test/.. , tetapi juga jalur yang lebih kompleks seperti:

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

EDIT: Jika tidak berhasil, periksa apakah Anda cdbelum dimodifikasi, seperti yang biasa dilakukan,

$ 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
sumber
Menggunakan evalTIDAK bagus jika ada kemungkinan parsing input pengguna yang dapat menempatkan Anda di tempat yang tidak Anda harapkan atau menjalankan hal-hal buruk terhadap sistem Anda. Bisakah Anda menggunakan pushd $dir 2>&1 >/dev/null && pwd && popd 2>&1 >/dev/nullbukan eval?
dragon788
2
Anda tidak perlu evalsama sekali: lakukan saja parentdir=$(cd -- "$dir" && pwd). Karena cddijalankan dalam subkulit, Anda tidak perlu cdkembali ke tempat kami sebelumnya. Di --sini jika ekspansi $dirdimulai dengan tanda hubung. Ini &&hanya untuk mencegah parentdiragar konten tidak kosong ketika cdtidak berhasil.
gniourf_gniourf
8

Motivasi untuk jawaban lain

Saya suka kode yang sangat singkat, jelas, dan terjamin. Poin bonus jika tidak menjalankan program eksternal, karena pada hari Anda perlu memproses sejumlah besar entri, itu akan terasa lebih cepat.

Prinsip

Tidak yakin dengan jaminan apa yang Anda miliki dan inginkan, jadi tetaplah menawarkan.

Jika Anda memiliki jaminan, Anda dapat melakukannya dengan kode yang sangat singkat. Idenya adalah untuk menggunakan fitur substitusi teks bash untuk memotong garis miring terakhir dan apa pun yang mengikuti.

Jawab dari kasus sederhana hingga yang lebih kompleks dari pertanyaan awal.

Jika jalur dijamin berakhir tanpa garis miring (masuk dan keluar)

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

Jika jalur dijamin berakhir dengan tepat satu tebasan (masuk dan keluar)

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

Jika jalur input dapat berakhir dengan nol atau satu garis miring (tidak lebih) dan Anda ingin jalur keluaran berakhir tanpa garis miring

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

Jika jalur input mungkin memiliki banyak garis miring luar dan Anda ingin jalur keluaran berakhir tanpa garis miring

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
sumber
1
Jawaban yang fantastis. Karena sepertinya dia sedang mencari cara untuk melakukan ini dari dalam skrip (temukan direktori skrip saat ini dan itu adalah induknya dan lakukan sesuatu yang relatif terhadap tempat skrip itu tinggal) ini adalah jawaban yang bagus dan Anda bahkan dapat memiliki kontrol lebih besar atas apakah input memiliki garis miring atau tidak dengan menggunakan ekspansi parameter terhadap ${BASH_SOURCE[0]}yang akan menjadi path lengkap ke skrip jika tidak bersumber melalui sourceatau ..
dragon788
7

Jika /home/smith/Desktop/Test/../itu yang Anda inginkan:

dirname 'path/to/child/dir'

seperti yang terlihat di sini .

Jon Egeland
sumber
1
maaf, maksud saya seperti script bash, saya punya variabel dengan path file
YTKColumba
Menjalankan kode itu memberi saya kesalahan bash: dirname 'Test': syntax error: invalid arithmetic operator (error token is "'Test'"). Kode tertaut juga salah.
Michael Hoffman
coba jalankan dirname Testdari direktori Test.
Jon Egeland
1

Bergantung pada apakah Anda membutuhkan jalur absolut, Anda mungkin ingin mengambil langkah ekstra:

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

gunakan ini: export MYVAR="$(dirname "$(dirname "$(dirname "$(dirname $PWD)")")")"jika Anda ingin direktori induk ke-4

export MYVAR="$(dirname "$(dirname "$(dirname $PWD)")")" jika Anda ingin direktori induk ke-3

export MYVAR="$(dirname "$(dirname $PWD)")" jika Anda ingin direktori induk ke-2

Kaustubh
sumber
Terima kasih !, hanya apa yang saya cari.
Josue Abarca
0

jelek tapi efisien

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
sumber
0

Dimulai dari ide / komentar Charles Duffy - 17 Des '14 jam 5:32 pada topik Dapatkan nama direktori saat ini (tanpa path lengkap) dalam skrip 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;
keris
sumber
0

jika karena alasan apapun Anda tertarik dalam menjelajahi sampai jumlah tertentu direktori Anda juga bisa melakukan: nth_path=$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && cd ../../../ && pwd). Ini akan memberikan 3 direktori orang tua

ClimbingTheCurve
sumber
mengapa $ {BASH_SOURCE [0]} alih-alih lebih sederhana $ BASH_SOURCE
Nam G VU