バッシュで特別な変数(例:〜ティルド)を手動で拡張する方法
-
09-10-2019 - |
質問
私は私のバッシュスクリプトに変数を持っていますその値は次のようなものです:
~/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
注入の問題は最初の拡張では発生しないことに注意してください。したがって、単に交換する場合 magic
と eval 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を含めて、問題がある場合はユーザーがパスを修正できるようにします。)