我的bash脚本中有一个变量,其价值就是这样:

~/a/b/c

请注意,它是未来的Tilde。当我在此变量上执行LS -LT(称为$ var)时,我没有这样的目录。我想让Bash解释/扩展此变量而不执行该变量。换句话说,我希望BASH运行评估,但不运行评估命令。这可以在狂欢中吗?

我如何在没有扩展的情况下将其传递到我的脚本中?我通过双引号传递了围绕它的论点。

尝试此命令以查看我的意思:

ls -lt "~"

这正是我所处的情况。我希望tilde扩大。换句话说,我应该用什么代替魔术,以使这两个命令相同:

ls -lt ~/abc/def/ghi

ls -lt $(magic "~/abc/def/ghi")

请注意,〜/abc/def/ghi可能存在也可能不存在。

有帮助吗?

解决方案

由于stackoverflow的性质,我不能仅仅让这个答案不被接受,但是在我发布此答案以来的5年中,答案比我公认的基本和相当糟糕的答案更好(我还很年轻,不要杀人我)。

该线程中的其他解决方案是更安全,更好的解决方案。最好是,我会选择这两个中的任何一个:


出于历史目的的原始答案(但请不要使用)

如果我没错的话, "~" 不会以这种方式来扩展bash脚本,因为它被视为字符串 "~". 。您可以通过 eval 像这样。

#!/bin/bash

homedir=~
eval homedir=$homedir
echo $homedir # prints home path

或者,只需使用 ${HOME} 如果您想要用户的主目录。

其他提示

如果变量 var 是用户输入的 eval 应该 不是 用于使用

eval var=$var  # Do not use this!

原因是:例如,用户可以偶然(或故意)类型 var="$(rm -rf $HOME/)" 可能带来灾难性的后果。

更好(更安全)的方法是使用BASH参数扩展:

var="${var/#\~/$HOME}"

骗我自己 先前的答案, ,在没有相关的安全风险的情况下进行此操作 eval:

expandPath() {
  local path
  local -a pathElements resultPathElements
  IFS=':' read -r -a pathElements <<<"$1"
  : "${pathElements[@]}"
  for path in "${pathElements[@]}"; do
    : "$path"
    case $path in
      "~+"/*)
        path=$PWD/${path#"~+/"}
        ;;
      "~-"/*)
        path=$OLDPWD/${path#"~-/"}
        ;;
      "~"/*)
        path=$HOME/${path#"~/"}
        ;;
      "~"*)
        username=${path%%/*}
        username=${username#"~"}
        IFS=: read -r _ _ _ _ _ homedir _ < <(getent passwd "$username")
        if [[ $path = */* ]]; then
          path=${homedir}/${path#*/}
        else
          path=$homedir
        fi
        ;;
    esac
    resultPathElements+=( "$path" )
  done
  local result
  printf -v result '%s:' "${resultPathElements[@]}"
  printf '%s\n' "${result%:}"
}

...用作...

path=$(expandPath '~/hello')

或者,一种使用的简单方法 eval 小心:

expandPath() {
  case $1 in
    ~[+-]*)
      local content content_q
      printf -v content_q '%q' "${1:2}"
      eval "content=${1:0:2}${content_q}"
      printf '%s\n' "$content"
      ;;
    ~*)
      local content content_q
      printf -v content_q '%q' "${1:1}"
      eval "content=~${content_q}"
      printf '%s\n' "$content"
      ;;
    *)
      printf '%s\n' "$1"
      ;;
  esac
}

一种安全使用评估的方法是 "$(printf "~/%q" "$dangerous_path")". 。注意特定于bash。

#!/bin/bash

relativepath=a/b/c
eval homedir="$(printf "~/%q" "$relativepath")"
echo $homedir # prints home path

这个问题 有关详细信息

另外,请注意,在ZSH下,这将与 echo ${~dangerous_path}

这个怎么样:

path=`realpath "$1"`

或者:

path=`readlink -f "$1"`

扩展Birryree和Haloleo的答案(不打算双关语):一般方法是使用 eval, ,但它带有一些重要的警告,即空间和输出重定向(>)在变量中。以下似乎对我有用:

mypath="$1"

if [ -e "`eval echo ${mypath//>}`" ]; then
    echo "FOUND $mypath"
else
    echo "$mypath NOT FOUND"
fi

尝试以下每个参数:

'~'
'~/existing_file'
'~/existing file with spaces'
'~/nonexistant_file'
'~/nonexistant file with spaces'
'~/string containing > redirection'
'~/string containing > redirection > again and >> again'

解释

  • ${mypath//>} 脱掉 > 可以在 eval.
  • eval echo ... 是实际的蒂尔德扩展
  • 围绕 -e 论点是为了支持带有空间的文件名。

也许有一个更优雅的解决方案,但这就是我能想到的。

我相信这就是您想要的

magic() { # returns unexpanded tilde express on invalid user
    local _safe_path; printf -v _safe_path "%q" "$1"
    eval "ln -sf ${_safe_path#\\} /tmp/realpath.$$"
    readlink /tmp/realpath.$$
    rm -f /tmp/realpath.$$
}

示例用法:

$ magic ~nobody/would/look/here
/var/empty/would/look/here

$ magic ~invalid/this/will/not/expand
~invalid/this/will/not/expand

这是我的解决方案:

#!/bin/bash


expandTilde()
{
    local tilde_re='^(~[A-Za-z0-9_.-]*)(.*)'
    local path="$*"
    local pathSuffix=

    if [[ $path =~ $tilde_re ]]
    then
        # only use eval on the ~username portion !
        path=$(eval echo ${BASH_REMATCH[1]})
        pathSuffix=${BASH_REMATCH[2]}
    fi

    echo "${path}${pathSuffix}"
}



result=$(expandTilde "$1")

echo "Result = $result"

只是使用 eval 正确:通过验证。

case $1${1%%/*} in
([!~]*|"$1"?*[!-+_.[:alnum:]]*|"") ! :;;
(*/*)  set "${1%%/*}" "${1#*/}"       ;;
(*)    set "$1" 
esac&& eval "printf '%s\n' $1${2+/\"\$2\"}"

这是HåkonHægland'sBash的POSIX功能 回答

expand_tilde() {
    tilde_less="${1#\~/}"
    [ "$1" != "$tilde_less" ] && tilde_less="$HOME/$tilde_less"
    printf '%s' "$tilde_less"
}

2017-12-10编辑:添加 '%s' 评论中的@charlesduffy。

只是为了扩展 Birryree有空间的路径的答案:您不能使用 eval 命令是因为它将评估分开。一种解决方案是临时替换空格以替换命令:

mypath="~/a/b/c/Something With Spaces"
expandedpath=${mypath// /_spc_}    # replace spaces 
eval expandedpath=${expandedpath}  # put spaces back
expandedpath=${expandedpath//_spc_/ }
echo "$expandedpath"    # prints e.g. /Users/fred/a/b/c/Something With Spaces"
ls -lt "$expandedpath"  # outputs dir content

当然,这个示例依赖于这样的假设 mypath 永远不要包含char序列 "_spc_".

您可能会发现在Python中这样做更容易。

(1)来自UNIX命令行:

python -c 'import os; import sys; print os.path.expanduser(sys.argv[1])' ~/fred

结果是:

/Users/someone/fred

(2)在BASH脚本中作为一次性 - 将其保存为 test.sh:

#!/usr/bin/env bash

thepath=$(python -c 'import os; import sys; print os.path.expanduser(sys.argv[1])' $1)

echo $thepath

跑步 bash ./test.sh 结果是:

/Users/someone/fred

(3)作为实用程序 - 将其保存为 expanduser 在您的道路上的某个地方,执行权限:

#!/usr/bin/env python

import sys
import os

print os.path.expanduser(sys.argv[1])

然后可以在命令行上使用:

expanduser ~/fred

或用脚本:

#!/usr/bin/env bash

thepath=$(expanduser $1)

echo $thepath

最简单: :用“ eval echo”替换“魔术”。

$ eval echo "~"
/whatever/the/f/the/home/directory/is

问题: 您将遇到其他变量问题,因为评估是邪恶的。例如:

$ # home is /Users/Hacker$(s)
$ s="echo SCARY COMMAND"
$ eval echo $(eval echo "~")
/Users/HackerSCARY COMMAND

请注意,注射问题不会在第一次扩展中发生。因此,如果您简单地替换 magiceval echo, ,你应该没事。但是如果你这样做 echo $(eval echo ~), ,那将容易注射。

同样,如果你这样做 eval echo ~ 代替 eval echo "~", ,这将算作两次扩展,因此可以立即进行注射。

我使用read -e(以及其他)在路径上读取路径后进行了可变参数替换。因此,用户可以将路径列入路径,如果用户输入〜路径,则可以对路径进行排序。

read -rep "Enter a path:  " -i "${testpath}" testpath 
testpath="${testpath/#~/${HOME}}" 
ls -al "${testpath}" 

额外的好处是,如果变量没有任何tilde,那么如果有tilde,但在第一个位置没有tilde,它也被忽略了。

(我将读取的-i包含在循环中,因为我在循环中使用此信息,因此如果存在问题,用户可以修复路径。)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top