Lấy cha mẹ của một thư mục trong Bash


207

Nếu tôi có đường dẫn tệp như ...

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

Làm thế nào để tôi thay đổi chuỗi để nó sẽ là thư mục cha?

ví dụ

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

4
Bạn chỉ có thể sử dụng ' ..', nhưng có thể đó không hoàn toàn là những gì bạn nghĩ.
Jonathan Leffler

Câu trả lời:


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

Hoạt động nếu có một dấu gạch chéo, quá.


14
các trích dẫn bên trong rất quan trọng hoặc bạn sẽ mất tất cả dữ liệu của mình
catamphetamine

10
và biến thể tôi đang sử dụng để có được cha mẹ của thư mục làm việc hiện tại: parentdir=$(dirname `pwd`)
TheGrimmSellectist 11/07/2015

3
Lưu ý, phương pháp này không an toàn cho các tên "xấu xí". Một tên như "-rm -rf" sẽ phá vỡ hành vi mong muốn. Có lẽ "." cũng sẽ Tôi không biết đó có phải là cách tốt nhất không, nhưng tôi đã tạo ra một câu hỏi "tập trung chính xác" riêng biệt ở đây: stackoverflow.com/questions
4000119/46

4
@Christopher Shroba Vâng, tôi đã bỏ qua các trích dẫn và sau đó ổ cứng của tôi đã bị xóa một phần. Tôi không nhớ chính xác những gì đã xảy ra: có lẽ là một thư mục trắng không được trích dẫn đã khiến nó xóa thư mục gốc thay vì mục tiêu - tập lệnh của tôi chỉ xóa thư mục / home / xxx /.
catamphetamine

3
ồ được rồi, vậy không có gì về lệnh sẽ khiến bất kỳ dữ liệu nào bị mất trừ khi bạn đã ban hành lệnh xóa, phải không? Đó chỉ là vấn đề của con đường bạn đang cố gắng loại bỏ việc chia tách nhân vật không gian và loại bỏ nhiều hơn mong đợi?
Christopher Shroba

18

... nhưng những gì "nhìn thấy ở đây " đã bị phá vỡ. Đây là cách khắc phục:

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

14

Chỉ cần sử dụng echo $(cd ../ && pwd)trong khi làm việc trong thư mục có thư mục cha bạn muốn tìm hiểu. Chuỗi này cũng có thêm lợi ích của việc không có dấu gạch chéo.


Bạn không cần echoở đây.
gniourf_gniourf

14

Rõ ràng thư mục mẹ được đưa ra chỉ bằng cách nối thêm tên tệp dấu chấm:

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

Nhưng bạn phải muốn đường dẫn được giải quyết (một đường dẫn tuyệt đối không có bất kỳ thành phần đường dẫn dấu chấm nào):

/home/smith/Desktop             # resolved path

Vấn đề với các câu trả lời hàng đầu được sử dụng dirnamelà chúng không hoạt động khi bạn nhập một đường dẫn có dấu chấm:

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

Điều này mạnh hơn :

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

Bạn có thể nuôi nó /home/smith/Desktop/Test/.., nhưng cũng có những đường dẫn phức tạp hơn như:

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

EDIT: Nếu nó không hoạt động, hãy kiểm tra xem bạn cdđã không được sửa đổi chưa, như đôi khi là thông lệ,

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

Việc sử dụng evalKHÔNG tuyệt vời nếu có bất kỳ cơ hội phân tích cú pháp đầu vào của người dùng có thể đưa bạn đến một nơi nào đó mà bạn không mong đợi hoặc chạy những điều xấu đối với hệ thống của bạn. Bạn có thể sử dụng pushd $dir 2>&1 >/dev/null && pwd && popd 2>&1 >/dev/nullthay vì eval?
dragon788

2
Bạn không cần evalgì cả: chỉ cần làm parentdir=$(cd -- "$dir" && pwd). Vì cdđược chạy trong một mạng con, bạn không cần phải cdquay lại nơi chúng tôi đã ở. Đây --là ở đây trong trường hợp mở rộng $dirbắt đầu với một dấu gạch nối. Điều &&này chỉ là để ngăn chặn parentdircó một nội dung không trống khi cdkhông thành công.
gniourf_gniourf

8

Động lực cho một câu trả lời khác

Tôi thích mã rất ngắn, rõ ràng, được đảm bảo. Điểm thưởng nếu nó không chạy một chương trình bên ngoài, vì ngày bạn cần xử lý một số lượng lớn các mục, nó sẽ nhanh hơn đáng kể.

Nguyên tắc

Không chắc chắn về những gì đảm bảo bạn có và muốn, vì vậy cung cấp nào.

Nếu bạn có đảm bảo bạn có thể làm điều đó với mã rất ngắn. Ý tưởng là sử dụng tính năng thay thế văn bản bash để cắt dấu gạch chéo cuối cùng và bất cứ điều gì tiếp theo.

Trả lời từ các trường hợp đơn giản đến phức tạp hơn của câu hỏi ban đầu.

Nếu đường dẫn được đảm bảo kết thúc mà không có dấu gạch chéo (vào và ra)

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

Nếu đường dẫn được đảm bảo kết thúc bằng chính xác một dấu gạch chéo (vào và ra)

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

Nếu đường dẫn đầu vào có thể kết thúc bằng 0 hoặc một dấu gạch chéo (không nhiều hơn) và bạn muốn đường dẫn đầu ra kết thúc mà không có dấu gạch chéo

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

Nếu đường dẫn đầu vào có thể có nhiều dấu gạch chéo và bạn muốn đường dẫn đầu ra kết thúc mà không có dấu gạch chéo

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
Câu trả lời tuyệt vời. Vì có vẻ như anh ta đang tìm cách để thực hiện điều này từ trong một tập lệnh (tìm thư mục hiện tại của tập lệnh và nó là cha mẹ và làm một cái gì đó liên quan đến nơi tập lệnh sống), đây là một câu trả lời tuyệt vời và bạn thậm chí có thể kiểm soát nhiều hơn xem đầu vào có dấu gạch chéo hay không bằng cách sử dụng mở rộng tham số dựa vào ${BASH_SOURCE[0]}đó sẽ là đường dẫn đầy đủ đến tập lệnh nếu nó không có nguồn gốc thông qua sourcehoặc ..
dragon788

7

Nếu /home/smith/Desktop/Test/../là những gì bạn muốn:

dirname 'path/to/child/dir'

như đã thấy ở đây .


1
xin lỗi, ý tôi là giống như một tập lệnh bash, tôi có một biến với đường dẫn tệp
YTKColumba

Chạy mã đó cho tôi lỗi bash: dirname 'Test': syntax error: invalid arithmetic operator (error token is "'Test'"). Mã liên kết cũng sai.
Michael Hoffman

hãy thử chạy dirname Testtừ thư mục Testđang ở.
Jon Egeland

1

Tùy thuộc vào việc bạn có cần đường dẫn tuyệt đối hay không, bạn có thể muốn thực hiện thêm một bước:

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

0

dùng cái này : export MYVAR="$(dirname "$(dirname "$(dirname "$(dirname $PWD)")")")" nếu bạn muốn thư mục cha thứ 4

export MYVAR="$(dirname "$(dirname "$(dirname $PWD)")")" nếu bạn muốn thư mục cha thứ 3

export MYVAR="$(dirname "$(dirname $PWD)")" nếu bạn muốn thư mục cha thứ 2


Cảm ơn!, Chỉ những gì tôi đang tìm kiếm.
Josue Abarca

0

xấu nhưng hiệu quả

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

Bắt đầu từ ý tưởng / bình luận Charles Duffy - 17/12/14 lúc 5:32 về chủ đề Lấy tên thư mục hiện tại (không có đường dẫn đầy đủ) trong tập lệnh 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

nếu vì bất kỳ lý do gì bạn quan tâm đến việc điều hướng một số lượng thư mục cụ thể, bạn cũng có thể làm : nth_path=$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && cd ../../../ && pwd). Điều này sẽ cung cấp cho 3 thư mục cha mẹ lên


tại sao $ {BASH_SOURCE [0]} thay vì đơn giản hơn $ BASH_SOURCE
Nam G VU
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.