如何在bash中获取文件路径中最外层的父目录?

时间:2017-08-31 01:19:43

标签: bash

我尝试使用while循环,但我搞砸了。

#!/bin/bash
parent=$(dirname "$1")
while ["$parent" -ne "/"]
do
    parent=$(dirname "$parent")
done  
echo "$parent"

路径可以是任意长度,并通过控制台传入。

./ script.bash /file/path/can/be/huge.bash 所需的输出是文件

4 个答案:

答案 0 :(得分:2)

我有点困惑,我想这就是你想要的。

#!/bin/bash
echo "$1" | sed -e 's=^/==' -e 's=/.*=='

或者

#!/bin/bash
path=${1#/}
echo "${path%%/*}"

对于第一种情况,因为它是一个单行,你可能真的不需要外部脚本。

如果路径可以有前导.(例如./file/path/can/be/huge.bash),则需要稍加修改。

如果路径可能很大,那么你最不想要的就是循环。

答案 1 :(得分:1)

你几乎拥有它:

#!/bin/bash
parent=$(dirname "$1")
# Use until instead of while (just a style thing to avoid having negated logic)
# Use == for string comparison
# Use [[ ]] because it's a shell builtin and generally is nicer to use
# Use $(dirname "$parent") instead of $parent. Without it, the loop runs until
# parent=/, and that destroys any useful information
# Now, it runs until the parent of $parent is /, which is what you wanted
until [[ $(dirname "$parent") == "/" ]]; do
    parent=$(dirname "$parent")
done
# Strip off the leading /
# ${var#pat} means "strip off the shortest prefix of $var that matches pat"
parent=${parent#/}
echo "$parent"

请注意,这将在输入.上进入无限循环。您应该在开始时检查绝对路径。

答案 2 :(得分:0)

只是使用bash我就已经开始HTNW完成了:)

parent=${1#/}        # strip leading slash, if there is one
echo "${parent%%/*}" # strip everything from slash closest to the start til end

答案 3 :(得分:0)

无论您是使用dirname还是使用参数扩展和子串删除(例如parent="${1%/*}")来逐步删除最终的路径规范,您需要测试几个条件处理绝对相对路径名。具体而言,您需要确定是否:

  • 路径名称是绝对,例如"/path/to/file"
  • 路径名称是 relative ,例如"path/to/file"
  • 路径名称使用".."
  • 引用父目录
  • 路径名称使用"."
  • 引用当前目录

为此,您可以使用复合语句,例如[ ... ] && [ ... ] ...(或较旧的,不太受欢迎的[ ... -a .... -a .... ])。您还需要考虑是否使用 bashisms (非POSIX,仅限bash函数和测试子句,如[[ ... ]]),或者使脚本可移植,将语法限制为POSIX兼容元素。 (参数扩展符合POSIX标准)

将所有这些部分放在一起,你可以做类似以下的事情:

#!/bin/bash

parent="$1"
tmp="$(dirname "$parent")"

## if only filename given, parent is "./"
if [ "$tmp" = "." ]; then
    parent="./"
else
    ## find parent for absolute and relative paths
    while [ "$parent" != "$tmp" ] && [ "$tmp" != "/" ] && 
          [ "$tmp" != "." ] && [ "$tmp" != ".." ]; do
        parent="$tmp"
        tmp="$(dirname "$parent")"
    done
fi

printf "%s\n%s\n" "$1" "$parent"

示例使用/输出

$ bash parent.sh ../a/b/c.d
../a/b/c.d
../a

$ bash parent.sh ./a/b/c.d
./a/b/c.d
./a

$ bash parent.sh /a/b/c.d
/a/b/c.d
/a

$ bash parent.sh a/b/c.d
a/b/c.d
a

$ bash parent.sh c.d
c.d
./

注意:您可以使用tmp="${parent%/*}"代替tmp="$(dirname "$parent")",这取决于您。 (您只需将文件名的第一个测试仅调整为[ "$parent" = "$tmp" ],并将"$tmp" != "/"替换为"$tmp" != ""

您可以像隔离父母一样富有创造力。没有一种正确的方法,而其他所有方法都是错误的。只是尝试使用良好的脚本测试和验证来涵盖您可能遇到的尽可能多的角落案例。

如果您想使用参数扩展并使用旧的条件-a运算符,您可以执行以下操作:

#!/bin/bash

parent="$1"
tmp="${parent%/*}"

## if only filename given, parent is "./"
if [ "$parent" = "$tmp" ]; then
    parent="./"
else
    ## find parent for absolute and relative paths
    while [ "$parent" != "$tmp" -a "$tmp" != "" -a \
            "$tmp" != "." -a "$tmp" != ".." ]; do
        parent="$tmp"
        tmp="${parent%/*}"
    done
fi

printf "%s\n%s\n" "$1" "$parent"

仔细看看,如果您有其他问题,请告诉我。