تحويل المسار النسبي إلى المسار المطلق؟
-
27-09-2019 - |
سؤال
لست متأكدًا مما إذا كانت هذه المسارات مكررة. بالنظر إلى المسار النسبي ، كيف يمكنني تحديد المسار المطلق باستخدام برنامج نصي shell؟
مثال:
relative path: /x/y/../../a/b/z/../c/d
absolute path: /a/b/c/d
المحلول
من عند هذا المصدر يأتي:
#!/bin/bash
# Assume parameter passed in is a relative path to a directory.
# For brevity, we won't do argument type or length checking.
ABS_PATH=`cd "$1"; pwd` # double quotes for paths that contain spaces etc...
echo "Absolute path: $ABS_PATH"
يمكنك أيضًا القيام بيرل واحد ، على سبيل المثال باستخدام Cwd::abs_path
نصائح أخرى
الطريقة الأكثر موثوقية التي صادفتها في UNIX هي readlink -f
:
$ readlink -f /x/y/../../a/b/z/../c/d
/a/b/c/d
تحذيرات زوجين:
- هذا أيضا التأثير الجانبي لحل جميع الارتباطات. قد يكون هذا أو لا يكون مرغوبًا فيه ، ولكن عادةً ما يكون كذلك.
readlink
سيعطي نتيجة فارغة إذا أشرت إلى دليل غير موجود. إذا كنت ترغب في دعم المسارات غير الموجودة ، فاستخدمreadlink -m
في حين أن. لسوء الحظ ، لا يوجد هذا الخيار في إصدارات Readlink التي تم إصدارها قبل عام 2005.
استخدام سحق
# Directory
relative_dir="folder/subfolder/"
absolute_dir="$( cd "$relative_dir" && pwd )"
# File
relative_file="folder/subfolder/file"
absolute_file="$( cd "${relative_file%/*}" && pwd )"/"${relative_file##*/}"
${relative_file%/*}
هي نفس النتيجة مثلdirname "$relative_file"
${relative_file##*/}
هي نفس النتيجة مثلbasename "$relative_file"
تحفظات: لا يحل الروابط الرمزية (أي لا يجوز للمسار الكنسي) => قد لا يفرق جميع التكرارات إذا كنت تستخدم الروابط الرمزية.
استخدام realpath
أمر realpath
لا وظيفة. البديل هو الاستخدام readlink -e
(أو readlink -f
). لكن realpath
لا يتم تثبيته في كثير من الأحيان بشكل افتراضي. إذا لم تتمكن من التأكد realpath
أو readlink
موجود ، يمكنك استبداله باستخدام Perl (انظر أدناه).
استخدام بيرل
ستيفن كرامر يقترح الاسم المستعار للقذيفة إذا realpath
غير متوفر في نظامك:
$ alias realpath="perl -MCwd -e 'print Cwd::realpath(\$ARGV[0]),qq<\n>'"
$ realpath path/folder/file
/home/user/absolute/path/folder/file
أو إذا كنت تفضل استخدام Perl مباشرة:
$ perl -MCwd -e 'print Cwd::realpath($ARGV[0]),qq<\n>' path/folder/file
/home/user/absolute/path/folder/file
يستخدم أمر PERL خط واحد هذا Cwd::realpath
. هناك في الواقع ثلاث وظائف بيرل. يأخذون حجة واحدة وإعادة اسم المسار المطلق. أدناه التفاصيل من الوثائق Perl5> الوحدات الأساسية> CWD.
abs_path()
يستخدم نفس الخوارزمية كماgetcwd()
. الروابط الرمزية ومكونات المسار النسبي (.
و..
) يتم حلها لإعادة اسم المسار الكنسي ، تمامًا مثلrealpath
.use Cwd 'abs_path'; my $abs_path = abs_path($file);
realpath()
هو مرادف لabs_path()
use Cwd 'realpath'; my $abs_path = realpath($file);
fast_abs_path()
هي نسخة أكثر خطورة ، ولكن من المحتمل أن تكون أسرع منabs_path()
use Cwd 'fast_abs_path'; my $abs_path = fast_abs_path($file);
يتم تصدير هذه الوظائف فقط عند الطلب => لذلك استخدم Cwd
لتجنب "روتين فرعي غير محدد" خطأ كما أشار من قبل arielf. إذا كنت ترغب في استيراد جميع هذه الوظائف الثلاث ، يمكنك استخدام واحد use Cwd
خط:
use Cwd qw(abs_path realpath fast_abs_path);
ألق نظرة على "RealPath".
$ realpath
usage: realpath [-q] path [...]
$ realpath ../../../../../
/data/home
منذ أن واجهت هذه المرات عدة مرات على مر السنين ، وهذه المرة كنت بحاجة إلى نسخة محمولة باش نقية يمكنني استخدامها على OSX و Linux ، تقدمت إلى الأمام وكتبت واحدة:
تعيش النسخة الحية هنا:
https://github.com/keen99/shell-functions/tree/master/resolve_path
ولكن من أجل ذلك ، إليك الإصدار الحالي (أشعر أنه تم اختباره جيدًا .. لكنني منفتح على التعليقات!)
قد لا يكون من الصعب جعلها تعمل مع Plain Bourne Shell (SH) ، لكنني لم أحاول ... أحب $ funcname كثيرًا. قون
#!/bin/bash
resolve_path() {
#I'm bash only, please!
# usage: resolve_path <a file or directory>
# follows symlinks and relative paths, returns a full real path
#
local owd="$PWD"
#echo "$FUNCNAME for $1" >&2
local opath="$1"
local npath=""
local obase=$(basename "$opath")
local odir=$(dirname "$opath")
if [[ -L "$opath" ]]
then
#it's a link.
#file or directory, we want to cd into it's dir
cd $odir
#then extract where the link points.
npath=$(readlink "$obase")
#have to -L BEFORE we -f, because -f includes -L :(
if [[ -L $npath ]]
then
#the link points to another symlink, so go follow that.
resolve_path "$npath"
#and finish out early, we're done.
return $?
#done
elif [[ -f $npath ]]
#the link points to a file.
then
#get the dir for the new file
nbase=$(basename $npath)
npath=$(dirname $npath)
cd "$npath"
ndir=$(pwd -P)
retval=0
#done
elif [[ -d $npath ]]
then
#the link points to a directory.
cd "$npath"
ndir=$(pwd -P)
retval=0
#done
else
echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2
echo "opath [[ $opath ]]" >&2
echo "npath [[ $npath ]]" >&2
return 1
fi
else
if ! [[ -e "$opath" ]]
then
echo "$FUNCNAME: $opath: No such file or directory" >&2
return 1
#and break early
elif [[ -d "$opath" ]]
then
cd "$opath"
ndir=$(pwd -P)
retval=0
#done
elif [[ -f "$opath" ]]
then
cd $odir
ndir=$(pwd -P)
nbase=$(basename "$opath")
retval=0
#done
else
echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2
echo "opath [[ $opath ]]" >&2
return 1
fi
fi
#now assemble our output
echo -n "$ndir"
if [[ "x${nbase:=}" != "x" ]]
then
echo "/$nbase"
else
echo
fi
#now return to where we were
cd "$owd"
return $retval
}
إليك مثال كلاسيكي ، بفضل Brew:
%% ls -l `which mvn`
lrwxr-xr-x 1 draistrick 502 29 Dec 17 10:50 /usr/local/bin/mvn@ -> ../Cellar/maven/3.2.3/bin/mvn
استخدم هذه الوظيفة وسيعود المسار الحقيقي:
%% cat test.sh
#!/bin/bash
. resolve_path.inc
echo
echo "relative symlinked path:"
which mvn
echo
echo "and the real path:"
resolve_path `which mvn`
%% test.sh
relative symlinked path:
/usr/local/bin/mvn
and the real path:
/usr/local/Cellar/maven/3.2.3/libexec/bin/mvn
قد يكون هذا يساعد:
$path = "~user/dir/../file"
$resolvedPath = glob($path); # (To resolve paths with '~')
# Since glob does not resolve relative path, we use abs_path
$absPath = abs_path($path);