鉴于:
some.txt
dir
|-cat.sh
Run Code Online (Sandbox Code Playgroud)
使用cat.sh的内容:
cat ../some.txt
Run Code Online (Sandbox Code Playgroud)
然后在./cat.sh
内部运行dir
工作正常,而不是./dir/cat.sh
在同一级别上运行dir
.我希望这是由于不同的工作目录.是否有一个容易的是../some.txt
相对于位置的路径cat.sh
?
Mar*_*cny 118
你想要做的是获取脚本的绝对路径(可通过${BASH_SOURCE[0]}
),然后使用它来获取父目录,并cd
在脚本的开头获取它.
#!/bin/bash
parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
cd "$parent_path"
cat ../some.text
Run Code Online (Sandbox Code Playgroud)
这将使您的shell脚本独立于您调用它的位置.每次运行它,就好像你在./cat.sh
里面跑dir
.
请注意,此脚本仅在您直接调用脚本时才有效(即不通过符号链接),否则查找脚本的当前位置会变得有点棘手)
mkl*_*nt0 26
@Martin Konecny的答案提供了正确的答案,但是 - 正如他所提到的 - 只有在不通过驻留在不同目录中的符号链接调用实际脚本时它才有效.
这个答案涵盖案例:一个解决方案时,脚本通过调用也适用符号链接甚至一个符号链接的链条:
Linux/GNUreadlink
解决方案:
如果您的脚本只需要在Linux上运行,或者您知道GNUreadlink
在$PATH
,则使用readlink -f
,这可以方便地将符号链接解析为其最终目标:
scriptDir=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
Run Code Online (Sandbox Code Playgroud)
请注意,GNU readlink
有3个相关选项,用于将符号链接解析为其最终目标的完整路径: -f
( --canonicalize
),-e
(--canonicalize-existing
)和-m
(--canonicalize-missing
) - 请参阅man readlink
.
由于在此场景中存在定义的目标,因此可以使用3个选项中的任何一个; 我选择了-f
这里,因为它是最知名的一个.
多(类Unix)平台解决方案(包括仅具有POSIX的实用程序集的平台):
如果您的脚本必须在以下任何平台上运行:
有一个readlink
实用程序,但缺乏-f
选项(在GNU意义上解析符号链接到其最终目标) - 例如,macOS.
readlink
; 请注意,最新版本的FreeBSD/PC-BSD 确实支持-f
.甚至没有readlink
,但有POSIX兼容的实用程序 - 例如,HP-UX(谢谢,@ Charles Duffy).
以下解决方案受/sf/answers/78182331/的启发,定义了辅助shell函数,rreadlink()
它将给定的符号链接解析为循环中的最终目标 - 此函数实际上是符合POSIX的实现GNU readlink
的-e
选项,类似于-f
选项,但最终目标必须存在.
注意:该函数是一个bash
函数,仅在使用POSIX兼容POSIX实用程序的意义上才符合POSIX标准.对于此函数的一个版本,它本身是用符合POSIX的shell代码(for /bin/sh
)编写的,请参见此处.
如果readlink
可用,则使用它(没有选项) - 在大多数现代平台上都是如此.
否则,ls -l
解析输出,这是唯一符合POSIX的方法来确定符号链接的目标.
警告:如果文件名或路径包含文字子字符串->
,这将会中断- 但这不太可能.
(请注意,缺少的平台readlink
可能仍然提供其他非POSIX方法来解析符号链接;例如,@ Charles Duffy提到HP-UX的find
实用程序支持%l
使用其-printf
主要格式char ;为简洁起见,该函数不会尝试检测这种情况.)
可以在npm注册表中找到以下函数的可安装实用程序(脚本)形式(具有附加功能); 在Linux和macOS上,安装它; 在其他平台上(假设他们有),请按照手动安装说明进行操作. rreadlink
[sudo] npm install -g rreadlink
bash
如果参数是符号链接,则返回最终目标的规范路径; 否则,返回参数自己的规范路径.
#!/usr/bin/env bash
# Helper function.
rreadlink() ( # execute function in a *subshell* to localize the effect of `cd`, ...
local target=$1 fname targetDir readlinkexe=$(command -v readlink) CDPATH=
# Since we'll be using `command` below for a predictable execution
# environment, we make sure that it has its original meaning.
{ \unalias command; \unset -f command; } &>/dev/null
while :; do # Resolve potential symlinks until the ultimate target is found.
[[ -L $target || -e $target ]] || { command printf '%s\n' "$FUNCNAME: ERROR: '$target' does not exist." >&2; return 1; }
command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
fname=$(command basename -- "$target") # Extract filename.
[[ $fname == '/' ]] && fname='' # !! curiously, `basename /` returns '/'
if [[ -L $fname ]]; then
# Extract [next] target path, which is defined
# relative to the symlink's own directory.
if [[ -n $readlinkexe ]]; then # Use `readlink`.
target=$("$readlinkexe" -- "$fname")
else # `readlink` utility not available.
# Parse `ls -l` output, which, unfortunately, is the only POSIX-compliant
# way to determine a symlink's target. Hypothetically, this can break with
# filenames containig literal ' -> ' and embedded newlines.
target=$(command ls -l -- "$fname")
target=${target#* -> }
fi
continue # Resolve [next] symlink target.
fi
break # Ultimate target reached.
done
targetDir=$(command pwd -P) # Get canonical dir. path
# Output the ultimate target's canonical path.
# Note that we manually resolve paths ending in /. and /.. to make sure we
# have a normalized path.
if [[ $fname == '.' ]]; then
command printf '%s\n' "${targetDir%/}"
elif [[ $fname == '..' ]]; then
# Caveat: something like /var/.. will resolve to /private (assuming
# /var@ -> /private/var), i.e. the '..' is applied AFTER canonicalization.
command printf '%s\n' "$(command dirname -- "${targetDir}")"
else
command printf '%s\n' "${targetDir%/}/$fname"
fi
)
# Determine ultimate script dir. using the helper function.
# Note that the helper function returns a canonical path.
scriptDir=$(dirname -- "$(rreadlink "$BASH_SOURCE")")
Run Code Online (Sandbox Code Playgroud)
只要一行就可以了。
cat "`dirname $0`"/../some.txt
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
81519 次 |
最近记录: |