Получение родительского каталога в Bash
Если у меня есть путь к файлу, например...
/home/smith/Desktop/Test
/home/smith/Desktop/Test/
Как изменить строку, чтобы она стала родительским каталогом?
например
/home/smith/Desktop
/home/smith/Desktop/
14 ответов
dir=/home/smith/Desktop/Test
parentdir="$(dirname "$dir")"
Работает, если есть и косая черта.
Понятно, что родительский каталог задается простым добавлением имени файла точка-точка:
/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!
Просто используйте echo $(cd ../ && pwd)
во время работы в каталоге, родительский каталог которого вы хотите узнать. Эта цепь также имеет дополнительное преимущество, заключающееся в том, что она не имеет конечных слешей.
... но то, что "видно здесь", сломано. Вот исправление:
> pwd
/home/me
> x='Om Namah Shivaya'
> mkdir "$x" && cd "$x"
/home/me/Om Namah Shivaya
> parentdir="$(dirname "$(pwd)")"
> echo $parentdir
/home/me
Мотивация для другого ответа
Мне нравится очень короткий, понятный, гарантированный код. Бонусный пункт, если не запускается внешняя программа, поскольку в день, когда вам нужно обработать огромное количество записей, это будет заметно быстрее.
Принцип
Не уверен насчет того, какие гарантии у вас есть и чего вы хотите, так что предлагайте в любом случае.
Если у вас есть гарантии, вы можете сделать это с очень коротким кодом. Идея состоит в том, чтобы использовать функцию подстановки текста в 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
Если вам нужно только имя родительского каталога:
parent_dir_name=$(basename $(dirname $PWD))
Если /home/smith/Desktop/Test/../
это то, что вы хотите:
dirname 'path/to/child/dir'
как видно здесь.
В зависимости от того, нужны ли вам абсолютные пути, вы можете сделать дополнительный шаг:
child='/home/smith/Desktop/Test/'
parent=$(dirname "$child")
abs_parent=$(realpath "$parent")
Начато с идеи / комментария Чарльз Даффи - 17 декабря '14 в 5:32 по теме Получить текущее имя каталога (без полного пути) в скрипте Bash
#!/bin/bash
#INFO : https://stackru.com/questions/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;
Если по какой-либо причине вы заинтересованы в навигации по определенному количеству каталогов, вы также можете сделать: nth_path=$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && cd ../../../ && pwd)
. Это даст 3 родительских каталога
Некрасиво, но эффективно
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_: /
Использовать этот: export MYVAR="$(dirname "$(dirname "$(dirname "$(dirname $PWD)")")")"
если вы хотите 4-й родительский каталог
export MYVAR="$(dirname "$(dirname "$(dirname $PWD)")")"
если вы хотите третий родительский каталог
export MYVAR="$(dirname "$(dirname $PWD)")"
если вы хотите 2-й родительский каталог
Как уже говорилось выше, исходя из/some/folder/foo/bar/..
к/some/folder/foo
довольно тривиально, используяbuildin cd <...>; pwd
.
Однако это будет иметь побочные эффекты при работе с относительным путем, поскольку cd + pwd преобразуется в абсолютный путь.
Если вы хотите решитьfolder/foo/bar/..
вfolder/foo
, вы можете использоватьsed
:
relative_path=folder/foo/bar/..
echo $relative_path | sed -E 's/(.*)\/(.*)\/\.\./\1/g'
Используемое выше регулярное выражение по сути заменяет любоеpat1/pat2/..
кpat1
.
Это также можно использовать как функцию:
function relative_parent() {
echo "$1" | sed -E 's/(.*)\/(.*)\/\.\./\1/g'
}
и называется:
relative_parent $relative_path