在Bash中获取目录的父级

Getting the parent of a directory in Bash

如果我有一个文件路径,比如…

1
2
/home/smith/Desktop/Test
/home/smith/Desktop/Test/

如何更改字符串使其成为父目录?

例如

1
2
/home/smith/Desktop
/home/smith/Desktop/


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

如果有一个尾随斜杠也可以。


…但是"这里看到"的东西被破坏了。这里是修复:

1
2
3
4
5
6
7
8
> pwd
/home/me
> x='Om Namah Shivaya'
> mkdir"$x" && cd"$x"
/home/me/Om Namah Shivaya
> parentdir="$(dirname"$(pwd)")"
> echo $parentdir
/home/me


显然,父目录是通过简单地附加点文件名给出的:

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

但您必须要解析的路径(没有任何点路径组件的绝对路径):

1
/home/smith/Desktop             # resolved path

使用dirname的最上面的答案的问题是,当您输入带点的路径时,它们不起作用:

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

这是更强大的:

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

您可以使用/home/smith/Desktop/Test/..,但也可以使用更复杂的路径,例如:

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


只需使用echo $(cd ../ && pwd)在您要查找其父目录的目录中工作。这种链还有一个额外的好处,即没有尾部斜杠。


如果你想要的是/home/smith/Desktop/Test/../

1
dirname 'path/to/child/dir'

如这里所见。


另一个答案的动机

我喜欢非常短、清晰、有保证的代码。如果它不运行一个外部程序,就可以获得额外的积分,因为在您需要处理大量条目的那天,它会明显更快。

原理

不知道你有什么保证和想要什么,所以无论如何提供。

如果你有保证,你可以用很短的代码来完成。其思想是使用bash文本替换特性来剪切最后一个斜杠以及后面的内容。

回答从简单到复杂的原始问题。

如果保证路径结尾没有任何斜线(in和out)

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

如果保证路径以一个斜杠结尾(in和out)

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

如果输入路径可能以零或一个斜杠(不是多个)结尾,并且希望输出路径以无斜杠结尾

1
2
3
4
5
6
7
8
9
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

如果输入路径可能有许多多余的斜线,并且您希望输出路径不带斜线而结束

1
2
3
4
5
6
7
8
9
10
11
12
13
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


如果需要第四个父目录,请使用:export MYVAR="$(dirname"$(dirname"$(dirname"$(dirname $PWD)")")")"

export MYVAR="$(dirname"$(dirname"$(dirname $PWD)")")"如果你想要第三个父目录

export MYVAR="$(dirname"$(dirname $PWD)")"如果你想要第二个父目录


根据您是否需要绝对路径,您可能需要采取额外步骤:

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

从14年12月17日5:32关于bash脚本中获取当前目录名(不带完整路径)的想法/评论开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash
#INFO : https://stackoverflow.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
'"
$((${#dirs[@]} - 1))"  )"

for(( cnt=0 ; cnt < ${dirNr} ; cnt++))
  do
      dirName[$cnt]="$( IFS=/ read -r -a dirs <<<"$PWD"; printf '%s
'"
${dirs[${#dirs[@]} - $(( $cnt+1))]}"  )"
      #information – feedback
      echo"$cnt :  ${dirName[$cnt]}"
  done
}

dirTree=$PWD;
getDirNames;

丑陋但有效率

1
function Parentdir()

{

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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_
"

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
    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_: /