170

私のbashスクリプトには、値が次のような変数があります。

~/a/b/c

展開されていないチルダであることに注意してください。この変数 ($VAR と呼びます) で ls -lt を実行すると、そのようなディレクトリは取得されません。この変数を実行せずにbashに解釈/展開させたい。つまり、bash で eval を実行するが、評価されたコマンドは実行しないようにする必要があります。これはbashで可能ですか?

これを展開せずにスクリプトに渡すにはどうすればよいですか? 引数を二重引用符で囲んで渡しました。

このコマンドを試して、私が何を意味するかを確認してください。

ls -lt "~"

これはまさに私がいる状況です。チルダを展開したいです。言い換えれば、これら 2 つのコマンドを同一にするために、magic を何に置き換えればよいでしょうか。

ls -lt ~/abc/def/ghi

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

~/abc/def/ghi が存在する場合と存在しない場合があることに注意してください。

4

16 に答える 16

146

変数varがユーザーによって入力された場合、使用してチルダを展開するために使用しないevalでください

eval var=$var  # Do not use this!

その理由は、ユーザーが誤って (または意図的に) 入力して、たとえばvar="$(rm -rf $HOME/)"悲惨な結果を招く可能性があるためです。

より良い (そしてより安全な) 方法は、Bash パラメーター展開を使用することです。

var="${var/#\~/$HOME}"
于 2014-12-15T13:26:18.433 に答える
105

StackOverflow の性質上、この回答を受け入れられなくすることはできませんが、これを投稿してから 5 年の間に、明らかに初歩的でかなり悪い回答よりもはるかに優れた回答がありました (私は若かったので、殺さないでください)。自分)。

このスレッドの他の解決策は、より安全で優れた解決策です。できれば、次の2つのいずれかを使用します。


歴史的な目的のための元の回答(ただし、これは使用しないでください)

私が間違っていなければ"~"、リテラル string として扱われるため、そのように bash スクリプトによって展開されることはありません"~"。このように強制的に展開できますeval

#!/bin/bash

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

${HOME}または、ユーザーのホーム ディレクトリが必要な場合にのみ使用します。

于 2010-10-18T21:59:37.100 に答える
27

に関連するセキュリティリスクなしでこれを確実に行うために、以前の回答から自分自身を盗用し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
}
于 2015-03-27T21:26:57.800 に答える
11

これはどう:

path=`realpath "$1"`

または:

path=`readlink -f "$1"`
于 2010-10-18T22:31:35.660 に答える
10

eval を使用する安全な方法は"$(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}

于 2013-02-28T18:04:46.473 に答える
7

birryreeとhalloleoの答えを拡張する(しゃれは意図されていません):一般的なアプローチはを使用することですが、変数に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スペースを含むファイル名をサポートするためのものです。

もっとエレガントな解決策があるかもしれませんが、これが私が思いついたものです。

于 2012-08-14T09:56:34.273 に答える
2

HåkonHæglandのBashの回答に相当するPOSIX関数は次のとおりです

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

2017-12-10 編集:'%s'コメントに @CharlesDuffy ごとに追加。

于 2016-08-25T19:06:20.037 に答える
2

私はこれがあなたが探しているものだと信じています

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
于 2015-06-11T01:32:02.240 に答える
1

これが私の解決策です:

#!/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"
于 2015-08-21T14:08:16.287 に答える
0

正しく使用evalしてください:検証付き。

case $1${1%%/*} in
([!~]*|"$1"?*[!-+_.[:alnum:]]*|"") ! :;;
(*/*)  set "${1%%/*}" "${1#*/}"       ;;
(*)    set "$1" 
esac&& eval "printf '%s\n' $1${2+/\"\$2\"}"
于 2015-12-13T04:49:57.747 に答える
0

スペースを含むパスに対するbirryreeの回答を拡張するだけevalです。評価をスペースで区切るため、コマンドをそのまま使用することはできません。1 つの解決策は、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_"

于 2011-12-26T04:36:07.750 に答える