如何在路径中扩展波浪号 (~)

How to expand tilde (~) in path

我有一个 shell 脚本可以从用户那里获取目录路径,但我需要检查目录是否为空。如果用户用 ~ 而不是绝对路径放置他的主路径,那么我无法用 ls

检查它
  echo "Specify your project root directory. For example: ~/Base/project1"

  read directory

  if [ ! -z "$directory" ]
  then

    if [ "$(ls -A "$directory")" ]
    then
      echo Directory $directory is not empty
    else
      echo The directory $directory is empty '(or non-existent)'
    fi
    directory="$directory"

  else

    echo "No root directory specified. Exiting.."
    exit;

  fi

我收到错误:ls 无法使用 ~ 读取路径,如何在检查目录为空之前展开它?

原回答

试试这个:

eval directory="$directory"

因为没有什么比 shell 本身更能解释特殊的 shell 字符,所以让 shell 为我们计算表达式是个好主意。 eval 只是计算 shell 表达式的命令。

备选方案 #1:C 语言程序

但是,eval 是不安全的,因为它已被多次提及,- 它可能会执行恶意代码,或造成不良影响。然后,对于POSIX环境,你可以在C:

中写一个简单的程序

tildeexp.c

#include <stdio.h>
#include <stdlib.h>
#include <wordexp.h>

int
main(int argc, char **argv)
{
  wordexp_t p;
  int rc;

  rc = wordexp(argv[1], &p, 0);
  if (rc) {
    fprintf(stderr, "Failed to expand %s: %d\n",
        argv[1], rc);
  } else {
    printf("%s\n", p.we_wordc ? p.we_wordv[0] : "");
  }
  wordfree(&p);

  return (rc ? 1 : 0);
}

正在编译

gcc -Wall -g -O2 -o tildeexp tildeexp.c

用法

directory=$(/path/to/tildeexp "$directory")
if [ $? -eq 0 ]; then
  # success
else
  # failed to expand
fi

备选方案 #2:Perl 的 glob

directory="${directory//$"'"/$"\'"}"
directory=$(perl -e "print glob('$directory')")

Ruslan 关于使用 eval 的建议只要目录规范有效就保证有效。但是,如果用户输入恶意内容(或只是不小心输入会导致副作用的内容),它确实会使您面临任意代码执行。

如果您的 shell 有一个 printf 支持 %q(Bash 支持),您可以使用它来转义路径中的所有危险字符在让 Bash 用 eval:

扩展它之前
if [ "${directory:0:1}" == \~ ]; then
    eval directory="$(printf '~%q' "${directory#\~}")"
fi

否则,您可以手动扩展波浪号。赛勒斯的答案适用于 ~/some/path 之类的路径(未指定用户),但对于 ~somebody/some/path.

之类的路径将失败

为了处理这种情况,我们可以使用 getent 查找用户的主目录并手动扩展路径:

prefix=${directory%%/*}
if [ "$prefix" == \~ ]; then
    # Implicitly use current user.
    user=$USER
else
    # Parse user from tilde prefix.
    user=${prefix#\~}
fi

# Get the home directory of the user.  Only expand if the expanded directory exists.
homedir=$(getent passwd -- "$user" | cut -d: -f6)
if [ -d "$homedir" ]; then
    # Replace the tilde prefix with the absolute path to the home directory.
    directory=$homedir${directory#$prefix}
fi

这模仿了 shell 的行为,无效的主目录规范(例如,~baduser/)将保持原样。