3

I have a shell script that get's directory path from user, but i need to check directory empty or not. In case user put his home path with ~ instead of absolute path, so I can't check it with 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

I'm getting error: ls cannot read path with ~ , how to expand it before checking directory is empty?

Will Vousden
  • 32,488
  • 9
  • 84
  • 95
alvery
  • 1,863
  • 2
  • 15
  • 35

2 Answers2

4

Ruslan's suggestion of using eval is guaranteed to work as long as the directory specification is valid. However, it does expose you to arbitrary code execution if the user enters something malicious (or just accidentally enters something that causes side-effects).

If your shell has a printf that supports %q (which Bash does), you can use it to escape all of the dangerous characters in the path before letting Bash expand it with eval:

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

Otherwise, you can expand the tilde manually. Cyrus's answer works well for paths like ~/some/path (where the user isn't specified), but will fail for paths like ~somebody/some/path.

To handle this case, we can look up the user's home directory using getent and expand the path manually:

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

This mimics the shell's behaviour in that invalid home directory specifications (e.g., ~baduser/) will be left as is.

Will Vousden
  • 32,488
  • 9
  • 84
  • 95
3

Original answer

Try this:

eval directory="$directory"

Since nothing can interpret the special shell characters better than the shell itself, it is a good idea to ask the shell to evaluate the expression for us. And eval is just the command that evaluates shell expressions.

Alternative #1: program in C

However, eval is unsafe, as it has been mentioned many times, - it may execute malicious code, or cause unwanted effects. Then, for a POSIX environment, you can write a simple program in 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);
}

Compiling

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

Usage

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

Alternative #2: Perl's glob

directory="${directory//$"'"/$"\\'"}"
directory=$(perl -e "print glob('$directory')")
Ruslan Osmanov
  • 20,486
  • 7
  • 46
  • 60
  • It works! So simple, thanks – alvery Oct 19 '16 at 10:18
  • 2
    Consider what happens if `$directory` is `$(ls)`. The command will execute `ls`. You may be 100% certain of the safety of your inputs, in which case this isn't terrible. In all other cases this is a major security flaw. If `$directory` comes from some untrusted location or if this script runs as a different user, it's either a command injection or elevation of privilege vulnerability. – Eric Oct 19 '16 at 10:44
  • @Eric, then he can write a simple tool, such as in the answer :) – Ruslan Osmanov Oct 19 '16 at 11:38
  • You can do it safely in-process using `eval`, just as long as you escape the relevant characters (assuming your shell has such a facility). – Will Vousden Oct 19 '16 at 14:46