在超级用户运行的shell脚本中查找常规用户给出的路径的绝对路径。将存储在变量中的路径可移植地解析为绝对路径

时间:2016-02-20 22:13:34

标签: bash shell

编者注::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'无效。 dirnamebasename给出相同的相对路径。 readlink打印一个空字符串......

我希望解决方案可以移植。

我知道readlink不可移植

我不改变变量的情况,因为答案使用这个例子。但最好避免使用大写字母。

我以不安全的方式来源配置文件,因为格式检查的例子会非常复杂。

更新要求:用户指定的路径是该用户的任何有效路径,包括:

  • 绝对路径

  • 相对于main.sh workdir的路径

  • 带有波形语法的路径,如〜或〜/ mydir或~user2 / mydir

对于绝对路径:

  • 如果绝对路径有内部..或者无关紧要。喜欢/ home / user / dir1 /../ dir2

  • 我只需要扩展,〜用户名语法有效,对于普通用户和超级用户

2 个答案:

答案 0 :(得分:1)

注意:由于sudo和{{1默认情况下,在使用~时,仍会反映真实用户的主目录 - 除非您使用$HOME。 功能

  • 主要问题是输入路径(存储在sudo中)以文字 sudo -H开头(由于是 double的一部分) - 引用字符串),而shell仅在单词的开头将扩展应用于未加引号的 $REL_PATH实例。

  • 但是,必须在~中引用~ ,以便推迟扩展,直到路径确定为运行脚本的用户的上下文。

    • 请注意,OP希望同时支持~(当前用户的主目录。)和$REL_PATH扩展(~的主目录。)

因此,将~<username>简单地传递给<username> 整个是很诱人的,但这不仅仅是冒险 1 < / sup>,但可能会破坏包含shell元字符的路径,例如$REL_PATH,并且具有前导或尾随空格或包含两个或更多空格的运行。

因此,以下符合POSIX的解决方案:

  • 隔离路径开头的eval令牌,并使用&安全地扩展 it
  • 然后通过将路径的其余部分附加到扩展标记来将路径重新组合在一起。
~
  • 仅使用符合POSIX标准的参数扩展。
  • POSIX-utility 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" 在这里是安全的,因为已知输入是安全的或事先明确检查过。
  • 根据OP的要求,得到的绝对路径是:
    • 规范化;也就是说,保留expreval等组件。
    • 检查是否存在。

请注意,使用全大写变量名称(例如.)通常不是一个好主意,因为它们可能与特殊的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