質問

私は私のバッシュスクリプトに変数を持っていますその値は次のようなものです:

~/a/b/c

それは未装備のチルドであることに注意してください。この変数($ varと呼ぶ)でls -ltを実行すると、そのようなディレクトリはありません。 Bashに実行せずにこの変数を解釈/展開させたいと思います。言い換えれば、私はbashに評価を実行しますが、評価されたコマンドを実行しません。これはバッシュで可能ですか?

どのようにしてこれを拡張せずにスクリプトに渡すことができましたか?私はそれを取り巻く議論を二重引用符で渡しました。

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

ls -lt "~"

これはまさに私がいる状況です。私はティルドを拡張したいです。言い換えれば、これらの2つのコマンドを同一にするために魔法を置き換える必要があるものは次のとおりです。

ls -lt ~/abc/def/ghi

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

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

役に立ちましたか?

解決

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

このスレッドの他のソリューションは、より安全で優れたソリューションです。できれば、私はこれら2つのいずれかと一緒に行きます:


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

私が間違っていない場合、 "~" 文字通りの文字列として扱われているため、その方法でバッシュスクリプトによって拡張されません "~". 。あなたは介して拡大を強制することができます 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
}

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}

これはどう:

path=`realpath "$1"`

または:

path=`readlink -f "$1"`

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 議論は、スペースを持つファイル名のサポートに関するものです。

おそらくもっとエレガントなソリューションがあるかもしれませんが、これは私が思いつくことができたものです。

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

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のBashに相当するPOSIX関数です 答え

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

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

拡張するためだけに ビリリースペースのあるパスに対する回答:使用できません eval スペースごとに評価を分離するため、コマンド。解決策の1つは、評価コマンドのために一時的にスペースを置き換えることです。

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)1回限りのバッシュスクリプト内 - これを保存します 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 "~", 、それは2回拡張されているとカウントされるため、すぐに注射が可能になります。

read -e(とりわけ)を使用してパスで読み取りした後、変数パラメーター置換でこれを行いました。したがって、ユーザーはパスをタブに複製でき、ユーザーがパスを入力するとソートされます。

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

追加の利点は、チルドがない場合、変数に何も起こらないこと、そしてチルドがあるが最初の位置にない場合、それは無視されることです。

(ループでこれを使用しているので、読み取りに-iを含めて、問題がある場合はユーザーがパスを修正できるようにします。)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top