私のbashスクリプトには、次のような値を持つ変数があります。
~/a/b/c
展開されていないチルダであることに注意してください。この変数に対してls -ltを実行すると($ VARと呼びます)、そのようなディレクトリは取得できません。この変数を実行せずにbashに解釈/展開させたいと思います。つまり、bashでevalを実行し、評価されたコマンドは実行しないようにします。これはbashで可能ですか?
これを拡張せずにスクリプトに渡すにはどうすればよいですか?引数を二重引用符で囲んで渡しました。
このコマンドを試して、意味を確認してください。
ls -lt "~"
これはまさに私がいる状況です。チルダを拡大したいです。言い換えると、これらの2つのコマンドを同一にするために、マジックを何に置き換える必要がありますか。
ls -lt ~/abc/def/ghi
そして
ls -lt $(magic "~/abc/def/ghi")
〜/ abc/def/ghiは存在する場合と存在しない場合があります。
StackOverflowの性質上、この答えを受け入れられないようにすることはできませんが、これを投稿してから5年の間、明らかに初歩的でかなり悪い答えよりもはるかに良い答えがありました(私は若かった、私を殺さないでください)。
このスレッドの他のソリューションは、より安全で優れたソリューションです。できれば、次の2つのいずれかを使用します。
歴史的な目的のためのオリジナルの答え(しかし、これを使用しないでください)
間違っていない場合、"~"
は、リテラル文字列"~"
として扱われるため、bashスクリプトによってその方法で展開されません。このようにeval
を介して強制的に展開できます。
#!/bin/bash
homedir=~
eval homedir=$homedir
echo $homedir # prints home path
または、ユーザーのホームディレクトリが必要な場合は、${HOME}
を使用します。
変数var
がユーザーによって入力された場合、eval
はnotを使用してチルダを展開します。
_eval var=$var # Do not use this!
_
その理由は、ユーザーが、var="$(rm -rf $HOME/)"
などのタイプを誤って(または目的によって)入力して、悲惨な結果を招く可能性があるためです。
より良い(そしてより安全な)方法は、Bashパラメーター展開を使用することです:
_var="${var/#\~/$HOME}"
_
eval
に関連するセキュリティリスクなしでこれを堅牢に行うために、 以前の回答 から自分をPlaめる
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}
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
引数を囲む二重引用符は、スペースを含むファイル名をサポートするためのものです。もっとエレガントなソリューションがあるかもしれませんが、これが私が思いつくことができたものです。
これはどう:
path=`realpath "$1"`
または:
path=`readlink -f "$1"`
これがあなたが探しているものだと思う
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
HåkonHæglandのBashに相当するPOSIX関数を次に示します answer
expand_tilde() {
tilde_less="${1#\~/}"
[ "$1" != "$tilde_less" ] && tilde_less="$HOME/$tilde_less"
printf '%s' "$tilde_less"
}
2017-12-10編集:コメントに'%s'
を@CharlesDuffyごとに追加します。
私のソリューションは次のとおりです。
#!/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\"}"
Read -e(とりわけ)を使用してパスを読み取った後、可変パラメーター置換でこれを実行しました。そのため、ユーザーはパスをタブ補完することができ、ユーザーが〜パスを入力するとソートされます。
read -rep "Enter a path: " -i "${testpath}" testpath
testpath="${testpath/#~/${HOME}}"
ls -al "${testpath}"
追加の利点は、チルダがない場合、変数には何も起こらないことです。また、チルダがあっても最初の位置にない場合は無視されます。
(問題がある場合にユーザーがパスを修正できるように、ループでこれを使用するため、読み取り用に-iを含めます。)
スペースを含むパスに対する 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
が文字シーケンス"_spc_"
を決して含まないという仮定に依存しています。
Pythonでこれを行う方が簡単な場合があります。
(1)unixコマンドラインから:
python -c 'import os; import sys; print os.path.expanduser(sys.argv[1])' ~/fred
結果:
/Users/someone/fred
(2)bashスクリプト内で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
Simplest:「magic」を「eval echo」に置き換えます。
_$ eval echo "~"
/whatever/the/f/the/home/directory/is
_
問題: evalは悪であるため、他の変数で問題が発生します。例えば:
_$ # 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回展開されたとカウントされるため、すぐに注入が可能になります。