【问题标题】:Getting the parent of a directory in Bash在 Bash 中获取目录的父级
【发布时间】:2012-01-15 14:21:44
【问题描述】:

如果我有一个文件路径,例如...

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

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

例如

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

【问题讨论】:

  • 你可以简单地使用'..',但也许这不是你的想法。
  • '..' 只能用于目录路径,不能用于文件路径。

标签: bash directory dirname


【解决方案1】:
dir=/home/smith/Desktop/Test
parentdir="$(dirname "$dir")"

如果有尾部斜线也可以。

【讨论】:

  • 里面的引号很重要,否则你会丢失所有数据
  • 以及我用来获取当前工作目录父级的变体:parentdir=$(dirname `pwd`)
  • 注意,这种方法对于“丑陋”的名字是不安全的。像“-rm -rf”这样的名称将破坏所需的行为。大概 ”。”也会。我不知道这是否是最好的方法,但我在这里创建了一个单独的“以正确性为中心”的问题:stackoverflow.com/questions/40700119/…
  • @Christopher Shroba 是的,我省略了引号,然后我的硬盘最终被部分擦除。我不记得到底发生了什么:大概是一个未加引号的空白目录使它删除了父目录而不是目标目录 - 我的脚本刚刚删除了 /home/xxx/ 文件夹。
  • 哦,好吧,除非您已经发出删除命令,否则该命令不会导致任何数据丢失,对吧?这只是您尝试删除的路径在空格字符上拆分并删除的内容超出预期的问题?
【解决方案2】:

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

/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=$(builtin cd $dir; pwd)

你可以喂它/home/smith/Desktop/Test/..,也可以喂更复杂的路径,比如:

$ dir=~/Library/../Desktop/../..
$ parentdir=$(builtin cd $dir; pwd)
$ echo $parentdir
/Users                                  # the fully resolved path!
 

注意:使用builtin 确保不会调用cd 的用户定义函数变体,而是调用没有输出的默认实用程序形式。

【讨论】:

  • 使用eval 不是很好,如果有任何机会解析用户输入可能会将您置于您不期望的地方或对您的系统运行坏事。你能用pushd $dir 2>&1 >/dev/null && pwd && popd 2>&1 >/dev/null代替eval吗?
  • 你根本不需要eval:只需要parentdir=$(cd -- "$dir" && pwd)。由于cd 在子shell 中运行,您不需要将cd 回到我们原来的位置。 -- 在这里是为了防止$dir 的扩展以连字符开头。 && 只是为了防止parentdircd 没有成功时出现非空内容。
  • @gniourf_gniourf 谢谢。根据您的建议更新了答案
  • 我用它来获取应用程序的父目录:dir=$(builtin cd "../../.."; pwd),因为路径名中有空格,其他一切都失败了。该应用程序是由 Platypus 构建的伪应用程序,其中隐藏了实际脚本。谢谢
【解决方案3】:

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

【讨论】:

  • 这里不需要echo
【解决方案4】:

...但是“看到的here”被破坏了。这是修复:

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

【讨论】:

    【解决方案5】:

    另一个答案的动机

    我喜欢非常简短、清晰、有保证的代码。如果它不运行外部程序,则加分,因为您需要处理大量条目的那一天,它会明显更快。

    原则

    不确定您拥有和想要什么保证,所以还是要提供。

    如果你有保证,你可以用非常短的代码来做到这一点。这个想法是使用 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
    

    【讨论】:

    • 很棒的答案。由于他似乎正在寻找一种从脚本中执行此操作的方法(找到脚本的当前目录及其父目录并执行与脚本所在位置相关的操作),这是一个很好的答案,您可以更好地控制是否通过对${BASH_SOURCE[0]} 使用参数扩展,输入是否有尾部斜杠,如果它不是通过source. 获取的,这将是脚本的完整路径。
    【解决方案6】:

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

    dirname 'path/to/child/dir'
    

    here所见。

    【讨论】:

    • 对不起,我的意思是像 bash 脚本一样,我有一个带有文件路径的变量
    • 运行该代码会给我错误bash: dirname 'Test': syntax error: invalid arithmetic operator (error token is "'Test'")。链接代码也是错误的。
    • 尝试从Test所在的目录运行dirname Test
    【解决方案7】:

    如果您只需要父目录的名称:

    parent_dir_name=$(basename $(dirname $PWD))
    

    【讨论】:

      【解决方案8】:

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

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

      【讨论】:

      • 真实路径不是posix
      • 很高兴知道。它是 gnu core utils 的一个软件部分,可能会或可能不会安装在您的系统上。
      【解决方案9】:

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

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

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

      【讨论】:

        【解决方案10】:

        丑陋但高效

        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_: /
        

        【讨论】:

          【解决方案11】:

          开始于 Charles Duffy 的想法/评论 - 2014 年 12 月 17 日 5:32,主题为 Get current directory name (without full path) in a Bash script

          #!/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\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;
          

          【讨论】:

            【解决方案12】:

            如果出于某种原因您有兴趣向上导航特定数量的目录,您也可以这样做:nth_path=$(cd "$( dirname "${BASH_SOURCE[0]}" )" &gt;/dev/null 2&gt;&amp;1 &amp;&amp; cd ../../../ &amp;&amp; pwd)。这将提供 3 个父目录

            【讨论】:

            • 为什么是 ${BASH_SOURCE[0]} 而不是更简单的 $BASH_SOURCE
            【解决方案13】:

            这将转到父文件夹

            cd ../
            

            【讨论】:

              猜你喜欢
              • 2017-04-03
              • 1970-01-01
              • 1970-01-01
              • 2018-07-30
              • 1970-01-01
              • 2016-05-18
              • 1970-01-01
              • 2014-02-11
              • 1970-01-01
              相关资源
              最近更新 更多