编者注::OP的问题最终证明无关与脚本以sudo
运行(作为超级用户) ,因为~
和$HOME
默认情况下在使用sudo
时仍然会反映真实的用户的主目录 - 除非您使用sudo -H
。这个问题的核心是如何以可移植的方式将存储在变量中的路径解析为绝对路径 :通过解析~
和~<user>
- 基于当前/指定用户的主目录的前缀路径,以及相对路径,基于当前目录的绝对路径。问题的标题是稍后修改以反映这一点。
功能
我有一个常规用户(非超级用户)给定路径,如:
cfg.sh:
REL_PATH="~/mydir"
(它可以包含相对于运行主脚本的目录的绝对路径或路径,或者包含常规用户homedir的'〜'的路径。通常它可以是某些普通用户的任何有效路径。包括任何一种代价扩展)。
我需要知道超级用户运行的主脚本中的绝对路径:
main.sh:
#! /bin/sh
# Check if this script ran by superuser
if [ `id -u` -ne 0 ]; then
printf "Error: This command requires administrative privileges!\n"
exit 1
fi
# Save current directory and source the configuration
# from the directory relative to main.sh placement
saved_path=$(pwd)
trap 'cd "$saved_path"; exit 1' 1 2 3 15
script_root_path=$(cd $(dirname $0) && pwd
cd "$script_root_path"
. ./cfg.sh
cd "$saved_path"
# Determine the regular user, that ran main.sh with administrative privileges
user=`who am i | awk '{print $1}'`
# Convert path given by user to absolute path (doesn't work)
ABS_PATH=`sudo -u $user readlink -f "$REL_PATH"`
# ABS_PATH=`sudo -u $user SLD=$REL_PATH sh -c 'cd $SLD; pwd'`
# Print absolute path
printf "$ABS_PATH\n"
# cd "$saved_path"
我尝试了与sudo -u $user
的不同组合。 cd
无法使用。 sh -c 'cd $REL_PATH'
无效。 dirname
和basename
给出相同的相对路径。 readlink
打印一个空字符串......
我希望解决方案可以移植。
我知道readlink不可移植
我不改变变量的情况,因为答案使用这个例子。但最好避免使用大写字母。
我以不安全的方式来源配置文件,因为格式检查的例子会非常复杂。
更新要求:用户指定的路径是该用户的任何有效路径,包括:
绝对路径
相对于main.sh workdir的路径
带有波形语法的路径,如〜或〜/ mydir或~user2 / mydir
对于绝对路径:
如果绝对路径有内部..或者无关紧要。喜欢/ home / user / dir1 /../ dir2
我只需要扩展,〜用户名语法有效,对于普通用户和超级用户
答案 0 :(得分:1)
注意:由于sudo
和{{1默认情况下,在使用~
时,仍会反映真实用户的主目录 - 除非您使用$HOME
。
功能
主要问题是输入路径(存储在sudo
中)以文字 sudo -H
开头(由于是 double的一部分) - 引用字符串),而shell仅在单词的开头将$REL_PATH
实例。
但是,必须在~
中引用~
,以便推迟扩展,直到路径确定为运行脚本的用户的上下文。
~
(当前用户的主目录。)和$REL_PATH
扩展(~
的主目录。)因此,将~<username>
简单地传递给<username>
整个是很诱人的,但这不仅仅是冒险 1 < / sup>,但可能会破坏包含shell元字符的路径,例如$REL_PATH
,并且具有前导或尾随空格或包含两个或更多空格的运行。
因此,以下符合POSIX的解决方案:
eval
令牌,并使用&
安全地扩展 it ,~
eval
用于正则表达式匹配。#!/bin/sh
# Note: Assumes that:
# * $REL_PATH is set to the input path.
# * $user is set to the real username.
case $REL_PATH in
\~|\~/*) # a ~ or ~/... path, expand the ~ based on the real user.
# NOTE: If you know that `sudo` is never invoked with `-H`,
# you could simply use `$HOME` instead of `$(eval "echo ~$user")`.
ABS_PATH="$(eval "echo ~$user")${REL_PATH#\~}"
;;
\~*) # a ~<username> or ~<username>/... path; expand the ~<username> part.
otherUserHomeDirRef=${REL_PATH%%/*}
# Make sure that $otherUserHomeDirRef is a well-formed
# ~/<username> token, so that `eval` can be safely applied.
if ! expr "$otherUserHomeDirRef" : '~[a-zA-Z0-9][a-zA-Z0-9._-]*$' >/dev/null; then
echo "ERROR: Malformed input path: $REL_PATH" 1>&2
exit 2
fi
otherUserHomeDir=$(eval echo "$otherUserHomeDirRef")
if [ "$otherUserHomeDirRef" = "$REL_PATH" ]; then
ABS_PATH=$otherUserHomeDir
else
ABS_PATH="$otherUserHomeDir/${REL_PATH#*/}"
fi
;;
/*) # already an absolute path, use as-is
ABS_PATH=$REL_PATH
;;
*) # a relative path, resolve based on *current* dir
ABS_PATH="$PWD/$REL_PATH"
esac
# Print the absolute path.
printf '%s\n' "$ABS_PATH"
在这里是安全的,因为已知输入是安全的或事先明确检查过。expr
和eval
等组件。 请注意,使用全大写变量名称(例如.
)通常不是一个好主意,因为它们可能与特殊的shell和环境变量冲突。
[1]当然,OP脚本中的更大风险是配置文件的源代码,它可能包含任何 shell代码。
答案 1 :(得分:-1)
请尝试使用realpath
。
对于Ubuntu / Debian:
$ sudo apt-get install realpath
$ cd /tmp && touch a
$ realpath a
/tmp/a
对于Mac:
$ cd /tmp && touch a
$ realpath a
/private/tmp/a