Получение родительского каталога в Bash

209

Если у меня есть путь к файлу, например ...

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

Как изменить строку, чтобы она стала родительским каталогом?

например

/home/smith/Desktop
/home/smith/Desktop/
YTKColumba
источник
4
Вы можете просто использовать ' ..', но, возможно, это не совсем то, что вы имели в виду.
Джонатан Леффлер

Ответы:

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

Работает, если есть и косая черта.

Майкл Хоффман
источник
14
цитаты внутри важны, иначе вы потеряете все свои данные
катамфетамин
11
и вариант, который я использую, чтобы получить родителя текущего рабочего каталога: parentdir=$(dirname `pwd`)
TheGrimmScientist
3
Обратите внимание, этот метод небезопасен для «некрасивых» имен. Имя типа «-rm -rf» нарушит желаемое поведение. Наверное "." будет тоже. Я не знаю, является ли это лучшим способом, но я создал отдельный вопрос, «сфокусированный на правильности»: stackoverflow.com/questions/40700119/…
VasiliNovikov
4
@Christopher Shroba Да, я опустил кавычки, а затем мой HDD оказался частично стертым. Я точно не помню, что произошло: по-видимому, каталог без пробелов заставил его стереть родительский каталог вместо целевого - мой скрипт просто стер папку / home / xxx /.
катамфетамин
3
о хорошо, так что в команде нет ничего, что могло бы привести к потере любых данных, если вы уже не выполняете команду удаления, верно? Это был просто вопрос пути, который вы пытались удалить, разделив пробел на символ пробела и удалив гораздо больше, чем ожидалось?
Кристофер Шроба
18

... но то, что "видно здесь ", сломано. Вот исправление:

> 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)при работе в каталоге, родительский каталог которого вы хотите узнать. Эта цепь также имеет дополнительное преимущество, заключающееся в том, что она не имеет конечных слешей.

Endu Ad
источник
Вам не нужно echoздесь.
gniourf_gniourf
14

Понятно, что родительский каталог задается простым добавлением имени файла точка-точка:

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

Но вы должны хотеть разрешенный путь (абсолютный путь без каких-либо компонентов пути точка-точка):

/home/smith/Desktop             # resolved path

Проблема с основными ответами, которые используют dirname, заключается в том, что они не работают, когда вы вводите путь с точечными точками:

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

Это более мощный :

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

Вы можете кормить его /home/smith/Desktop/Test/.., но и более сложные пути, такие как:

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

РЕДАКТИРОВАТЬ: Если это не работает, проверьте, что ваш cdне был изменен, как это часто бывает,

$ which cd
cd is a function  #cd was modified to print extra info, so use "builtin cd" to override
cd ()
{
    builtin cd "$@";
    ls -FGAhp
}
...
Риаз Ризви
источник
Использование evalНЕЛЬЗЯ, если есть возможность проанализировать пользовательский ввод, который может привести вас туда, куда вы не ожидаете, или создать что-то плохое для вашей системы. Вы можете использовать pushd $dir 2>&1 >/dev/null && pwd && popd 2>&1 >/dev/nullвместо eval?
dragon788
2
Вам не нужно evalвообще: просто делай parentdir=$(cd -- "$dir" && pwd). Поскольку cdон запускается в подоболочке, вам не нужно cdвозвращаться туда, где мы были. Это --здесь в случае, если расширение $dirначинается с дефиса. Это &&просто для того, чтобы не parentdirиметь непустого контента, когда cdне получилось.
gniourf_gniourf
8

Мотивация для другого ответа

Мне нравится очень короткий, понятный, гарантированный код. Бонусный балл, если не запускается внешняя программа, поскольку в тот день, когда вам нужно обработать огромное количество записей, это будет заметно быстрее.

Принцип

Не уверен насчет того, какие гарантии у вас есть и чего вы хотите, так что предлагайте в любом случае.

Если у вас есть гарантии, вы можете сделать это с очень коротким кодом. Идея состоит в том, чтобы использовать функцию подстановки текста в bash, чтобы вырезать последнюю косую черту и все последующее.

Ответ от простых до более сложных случаев исходного вопроса.

Если путь гарантированно заканчивается без косой черты (внутри и снаружи)

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

Если путь гарантированно заканчивается ровно одной косой чертой (внутри и снаружи)

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

Если входной путь может заканчиваться нулем или одной косой чертой (не более), и вы хотите, чтобы выходной путь заканчивался без косой черты

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

Если входной путь может иметь много посторонних слешей и вы хотите, чтобы выходной путь заканчивался без слеша

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
Фантастический ответ. Поскольку кажется, что он искал способ сделать это из сценария (найдите текущий каталог сценария и его родительский элемент и сделайте что-нибудь относительно места расположения сценария), это отличный ответ, и вы можете иметь еще больший контроль над тем, ввод имеет косую черту или нет, используя расширение параметра, по ${BASH_SOURCE[0]}которому будет полный путь к сценарию, если он не был получен через sourceили ..
dragon788
7

Если /home/smith/Desktop/Test/../это то, что вы хотите:

dirname 'path/to/child/dir'

как видно здесь .

Джон Эгеланн
источник
1
извините, я имею в виду, как скрипт bash, у меня есть переменная с путем к файлу
YTKColumba
Запуск этого кода дает мне ошибку bash: dirname 'Test': syntax error: invalid arithmetic operator (error token is "'Test'"). Связанный код тоже неверен.
Майкл Хоффман
попробуйте просто запустить dirname Testиз каталога Testв.
Джон Эгеланн
1

В зависимости от того, нужны ли вам абсолютные пути, вы можете сделать дополнительный шаг:

child='/home/smith/Desktop/Test/'
parent=$(dirname "$child")
abs_parent=$(realpath "$parent")
Марсело Ласерда
источник
0

используйте это: export MYVAR="$(dirname "$(dirname "$(dirname "$(dirname $PWD)")")")"если вы хотите 4-й родительский каталог

export MYVAR="$(dirname "$(dirname "$(dirname $PWD)")")" если вы хотите третий родительский каталог

export MYVAR="$(dirname "$(dirname $PWD)")" если вы хотите 2-й родительский каталог

Kaustubh
источник
Спасибо !, именно то, что я искал.
Хосе Абарка
0

некрасиво, но эффективно

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
источник
0

Начато с идеи / комментария Чарльз Даффи - 17 декабря '14 в 5:32 по теме Получить текущее имя каталога (без полного пути) в скрипте 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

если по какой - либо причине вы заинтересованы в навигации вверх определенное количество каталогов , которые вы могли бы также сделать: nth_path=$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && cd ../../../ && pwd). Это дало бы 3 родительских справочника

ClimbingTheCurve
источник
почему $ {BASH_SOURCE [0]} вместо простого $ BASH_SOURCE
Nam G VU