如何在 Bash 中获得文件的绝对目录?

我已经编写了一个 Bash 脚本,它接受一个输入文件作为参数并读取它。
此文件包含到其他文件的一些路径(相对于其位置)。

我希望脚本转到包含输入文件的文件夹,以执行进一步的命令。

在 Linux 中,如何从输入文件中获取文件夹(只是文件夹) ?

135920 次浏览

To get the full path use:

readlink -f relative/path/to/file

To get the directory of a file:

dirname relative/path/to/file

You can also combine the two:

dirname $(readlink -f relative/path/to/file)

If readlink -f is not available on your system you can use this*:

function myreadlink() {
(
cd "$(dirname $1)"         # or  cd "${1%/*}"
echo "$PWD/$(basename $1)" # or  echo "$PWD/${1##*/}"
)
}

Note that if you only need to move to a directory of a file specified as a relative path, you don't need to know the absolute path, a relative path is perfectly legal, so just use:

cd $(dirname relative/path/to/file)

if you wish to go back (while the script is running) to the original path, use pushd instead of cd, and popd when you are done.


* While myreadlink above is good enough in the context of this question, it has some limitation relative to the readlink tool suggested above. For example it doesn't correctly follow a link to a file with different basename.

I have been using readlink -f works on linux

so

FULL_PATH=$(readlink -f filename)
DIR=$(dirname $FULL_PATH)


PWD=$(pwd)


cd $DIR


#<do more work>


cd $PWD

Take a look at realpath which is available on GNU/Linux, FreeBSD and NetBSD, but not OpenBSD 6.8. I use something like:

CONTAININGDIR=$(realpath ${FILEPATH%/*})

to do what it sounds like you're trying to do.

Try our new Bash library product realpath-lib over at GitHub that we have given to the community for free and unencumbered use. It's clean, simple and well documented so it's great to learn from. You can do:

get_realpath <absolute|relative|symlink|local file path>

This function is the core of the library:

if [[ -f "$1" ]]
then
# file *must* exist
if cd "$(echo "${1%/*}")" &>/dev/null
then
# file *may* not be local
# exception is ./file.ext
# try 'cd .; cd -;' *works!*
local tmppwd="$PWD"
cd - &>/dev/null
else
# file *must* be local
local tmppwd="$PWD"
fi
else
# file *cannot* exist
return 1 # failure
fi


# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success


}

It's Bash 4+, does not require any dependencies and also provides get_dirname, get_filename, get_stemname and validate_path.

Problem with the above answer comes with files input with "./" like "./my-file.txt"

Workaround (of many):

    myfile="./somefile.txt"
FOLDER="$(dirname $(readlink -f "${ARG}"))"
echo ${FOLDER}

This will work for both file and folder:

absPath(){
if [[ -d "$1" ]]; then
cd "$1"
echo "$(pwd -P)"
else
cd "$(dirname "$1")"
echo "$(pwd -P)/$(basename "$1")"
fi
}
$cat abs.sh
#!/bin/bash
echo "$(cd "$(dirname "$1")"; pwd -P)"

Some explanations:

  1. This script get relative path as argument "$1"
  2. Then we get dirname part of that path (you can pass either dir or file to this script): dirname "$1"
  3. Then we cd "$(dirname "$1"); into this relative dir
  4. pwd -P and get absolute path. The -P option will avoid symlinks
  5. As final step we echo it

Then run your script:

abs.sh your_file.txt