Чи є команда для отримання абсолютного шляху, заданого відносним шляхом?
Наприклад, я хочу, щоб $ line містив абсолютний шлях кожного файлу в dir ./etc/
find ./ -type f | while read line; do
echo $line
done
Чи є команда для отримання абсолютного шляху, заданого відносним шляхом?
Наприклад, я хочу, щоб $ line містив абсолютний шлях кожного файлу в dir ./etc/
find ./ -type f | while read line; do
echo $line
done
Відповіді:
використання:
find "$(pwd)"/ -type f
щоб отримати всі файли або
echo "$(pwd)/$line"
відображення повного шляху (якщо відносний шлях має значення)
$(pwd)
замість $PWD
? (так, я знаю pwd
, що це вбудований)
Спробуйте realpath
.
~ $ sudo apt-get install realpath # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc
Щоб уникнути розширення посилань, використовуйте realpath -s
.
Відповідь виходить з " команда bash / fish для друку абсолютного шляху до файлу ".
realpath
не здається доступним на Mac (OS X 10.11 "El Capitan"). :-(
realpath
здається, що це також не доступне для CentOS 6
brew install coreutils
привезутьrealpath
realpath
вже присутній. Мені не довелося встановлювати його окремо.
realpath
він доступний на Git для Windows (принаймні для мене).
Якщо у вас встановлений пакет coreutils, ви можете, як правило, використовувати readlink -f relative_file_name
для отримання абсолютного (з усіма посиланнями, що вирішені)
brew install coreutils
. Однак виконуваний файл попередньо попереджує:greadlink -f relative_file_name
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
UPD Деякі пояснення
"$1"
dirname "$1"
cd "$(dirname "$1")
переходимо до цього відносного режиму і отримуємо абсолютний шлях до нього, запустивши pwd
команду shell$(basename "$1")
echo
це зробимоrealpath
або readlink -f
робити. Наприклад, він не працює на шляхах, де останнім компонентом є симпосилання.
-P
можливість pwd
командувати:echo "$(cd "$(dirname "$1")"; pwd -P)/$(basename "$1")"
Що варто, я проголосував за обрану відповідь, але хотів поділитися рішенням. Мінус у тому, що це лише Linux - я витратив близько 5 хвилин, намагаючись знайти еквівалент OSX, перш ніж прийти до переповнення Stack. Я впевнений, що все-таки там.
У Linux ви можете використовувати readlink -e
в тандемі з dirname
.
$(dirname $(readlink -e ../../../../etc/passwd))
врожайність
/etc/
А потім ви використовуєте dirname
сестру, basename
щоб просто отримати ім'я файлу
$(basename ../../../../../passwd)
врожайність
passwd
Зберіть це все разом ..
F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"
врожайність
/etc/passwd
Ви безпечні, якщо орієнтуєтесь на каталог, basename
нічого не повернете, і ви просто закінчитеся з подвійною косою рискою в кінцевому результаті.
dirname
, readlink
і basename
. Це допомогло мені отримати абсолютний шлях символічного зв’язку - не його цілі.
/home/GKFX
і я друкую touch newfile
, то , перш ніж я натискаю увійти ви могли б працювати, що я маю в виду «створити / будинок / GKFX / NewFile», який являє собою абсолютний шлях до файлу , який ще не існує.
Я думаю, що це самий портативний:
abspath() {
cd "$(dirname "$1")"
printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
cd "$OLDPWD"
}
Він не зможе, якщо шлях не існує.
dirname
є основною утилітою GNU, не загальною для всіх unixen, я вважаю.
dirname
- стандартна утиліта POSIX, дивіться тут: pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html
${1##*/}
вже цілий день, і тепер, коли я замінив цей смітник, basename "$1"
він, здається, нарешті належним чином обробляє контури, які закінчуються в /.
../..
realpath
це, мабуть, найкраще
Але ...
Початкове запитання було дуже заплутано для початку, на прикладі погано пов'язаного із зазначеним питанням.
Вибрана відповідь насправді відповідає наведеному прикладу, а зовсім не на запитання в заголовку. Перша команда - це відповідь (це насправді? Я сумніваюся), і могла б обійтися і без '/'. І я не бачу, що робить друга команда.
Кілька питань змішані:
зміни відносної назви шляху на абсолютну, що б вона не позначала, можливо, нічого. ( Як правило, якщо ви видаєте таку команду, як touch foo/bar
, наприклад , ім'я контуру foo/bar
повинно існувати для вас і, можливо, використовуватись у обчисленні, перш ніж файл буде фактично створений. )
може бути кілька абсолютних імен траєкторій, які позначають один і той же файл (або потенційний файл), особливо через символічні посилання (символьні посилання) на шляху, але, можливо, з інших причин (пристрій може бути встановлено вдвічі, ніж лише для читання). Можна або не хочеться вирішувати чіткість таких посилань.
дістатися до кінця ланцюжка символічних посилань на файл або ім'я, яке не є символьним посиланням. Це може або не може дати абсолютну назву шляху, залежно від того, як це робиться. І можна, а може і не захоче вирішити це в абсолютне ім'я шляху.
Команда readlink foo
без параметра дає відповідь лише у тому випадку, якщо її аргумент foo
є символічним посиланням, і ця відповідь є значенням цього посилання. Жодного іншого посилання не виконується. Відповідь може бути відносним шляхом: яким би не було значення аргументу symlink.
Однак readlink
є параметри (-f -e або -m), які працюватимуть для всіх файлів, і дають одне абсолютне ім'я шляху (те, що не має посилань) на файл, фактично позначений аргументом.
Це добре працює для будь-якого, що не є символьним посиланням, хоча, можливо, хочеться використовувати абсолютне ім'я шляху, не вирішуючи проміжні посилання на шляху. Це робиться командоюrealpath -s foo
У випадку аргументу symlink, readlink
його параметри знову будуть вирішувати всі посилання на абсолютний шлях до аргументу, але це також буде включати всі символьні посилання, які можуть виникнути, слідуючи за значенням аргументу. Ви можете не хотіти, щоб, якщо ви хотіли, щоб абсолютний шлях до аргументу символізував себе, а не до того, до чого він може посилатися. Знову ж таки, якщо foo
це симпосилання, realpath -s foo
отримає абсолютний шлях без вирішення символьних посилань, включаючи таку, яку подано як аргумент.
Без цього -s
варіанту, realpath
майже все те ж саме, що
readlink
, за винятком простого читання значення посилання, а також кількох інших речей. Мені просто не зрозуміло, чому readlink
є свої варіанти, створюючи, мабуть, небажане надмірність
realpath
.
Дослідження в Інтернеті не говорить набагато більше, за винятком того, що можуть бути деякі варіанти в різних системах.
Висновок: realpath
найкраща команда для використання, яка має найбільшу гнучкість, принаймні для запиту на використання тут.
Моє улюблене рішення було один на @EugenKonkov , тому що це не означає наявність інших комунальних послуг (пакет Coreutils).
Але це не вдалося для відносних шляхів ". та "..", ось ось дещо вдосконалена версія обробки цих особливих випадків.
Він все ще не працює, якщо користувач не має дозволу cd
вноситись у батьківський каталог відносного шляху.
#! /bin/sh
# Takes a path argument and returns it as an absolute path.
# No-op if the path is already absolute.
function to-abs-path {
local target="$1"
if [ "$target" == "." ]; then
echo "$(pwd)"
elif [ "$target" == ".." ]; then
echo "$(dirname "$(pwd)")"
else
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
fi
}
Відповідь Євгена не дуже спрацювала для мене, але це:
absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"
Бічна примітка, ваш поточний робочий каталог не впливає.
Найкраще рішення, імхо, - це таке, розміщене тут: https://stackoverflow.com/a/3373298/9724628 .
Це вимагає роботи пітона, але, схоже, охоплює всі або більшість крайових випадків і є дуже портативним рішенням.
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
Якщо ви використовуєте bash на Mac OS X, який не існував ні realpath, ні його посилання для читання не може надрукувати абсолютний шлях, у вас може бути вибір, крім кодування власної версії для його друку. Ось моя реалізація:
(чистий баш)
abspath(){
local thePath
if [[ ! "$1" =~ ^/ ]];then
thePath="$PWD/$1"
else
thePath="$1"
fi
echo "$thePath"|(
IFS=/
read -a parr
declare -a outp
for i in "${parr[@]}";do
case "$i" in
''|.) continue ;;
..)
len=${#outp[@]}
if ((len==0));then
continue
else
unset outp[$((len-1))]
fi
;;
*)
len=${#outp[@]}
outp[$len]="$i"
;;
esac
done
echo /"${outp[*]}"
)
}
(використовувати gawk)
abspath_gawk() {
if [[ -n "$1" ]];then
echo $1|gawk '{
if(substr($0,1,1) != "/"){
path = ENVIRON["PWD"]"/"$0
} else path = $0
split(path, a, "/")
n = asorti(a, b,"@ind_num_asc")
for(i in a){
if(a[i]=="" || a[i]=="."){
delete a[i]
}
}
n = asorti(a, b, "@ind_num_asc")
m = 0
while(m!=n){
m = n
for(i=1;i<=n;i++){
if(a[b[i]]==".."){
if(b[i-1] in a){
delete a[b[i-1]]
delete a[b[i]]
n = asorti(a, b, "@ind_num_asc")
break
} else exit 1
}
}
}
n = asorti(a, b, "@ind_num_asc")
if(n==0){
printf "/"
} else {
for(i=1;i<=n;i++){
printf "/"a[b[i]]
}
}
}'
fi
}
(чистий bsd awk)
#!/usr/bin/env awk -f
function abspath(path, i,j,n,a,b,back,out){
if(substr(path,1,1) != "/"){
path = ENVIRON["PWD"]"/"path
}
split(path, a, "/")
n = length(a)
for(i=1;i<=n;i++){
if(a[i]==""||a[i]=="."){
continue
}
a[++j]=a[i]
}
for(i=j+1;i<=n;i++){
delete a[i]
}
j=0
for(i=length(a);i>=1;i--){
if(back==0){
if(a[i]==".."){
back++
continue
} else {
b[++j]=a[i]
}
} else {
if(a[i]==".."){
back++
continue
} else {
back--
continue
}
}
}
if(length(b)==0){
return "/"
} else {
for(i=length(b);i>=1;i--){
out=out"/"b[i]
}
return out
}
}
BEGIN{
if(ARGC>1){
for(k=1;k<ARGC;k++){
print abspath(ARGV[k])
}
exit
}
}
{
print abspath($0)
}
приклад:
$ abspath I/am/.//..//the/./god/../of///.././war
/Users/leon/I/the/war
Якщо відносний шлях - це шлях до каталогу, то спробуйте мій, має бути найкращим:
absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)
echo $absPath
echo "mydir/doc/ mydir/usoe ./mydir/usm" | awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'
Покращення досить приємної версії @ ernest-a:
absolute_path() {
cd "$(dirname "$1")"
case $(basename $1) in
..) echo "$(dirname $(pwd))";;
.) echo "$(pwd)";;
*) echo "$(pwd)/$(basename $1)";;
esac
}
Це правильно стосується випадку, коли є останній елемент шляху ..
, і в цьому випадку відповідь "$(pwd)/$(basename "$1")"
у @ ernest-a отримає як accurate_sub_path/spurious_subdirectory/..
.
Якщо ви хочете перетворити змінну, що містить відносний шлях, в абсолютну, це працює:
dir=`cd "$dir"`
"cd" відлунюється, не змінюючи робочий каталог, тому що виконується тут у підколонці.
dir=`cd ".."` && echo $dir
Це є ланцюговим рішенням від усіх інших, наприклад, коли realpath
не вдається, або тому, що він не встановлений, або тому, що він виходить з кодом помилки, тоді наступне рішення намагається здійснити, поки він не отримає правильний шлях.
#!/bin/bash
function getabsolutepath() {
local target;
local changedir;
local basedir;
local firstattempt;
target="${1}";
if [ "$target" == "." ];
then
printf "%s" "$(pwd)";
elif [ "$target" == ".." ];
then
printf "%s" "$(dirname "$(pwd)")";
else
changedir="$(dirname "${target}")" && basedir="$(basename "${target}")" && firstattempt="$(cd "${changedir}" && pwd)" && printf "%s/%s" "${firstattempt}" "${basedir}" && return 0;
firstattempt="$(readlink -f "${target}")" && printf "%s" "${firstattempt}" && return 0;
firstattempt="$(realpath "${target}")" && printf "%s" "${firstattempt}" && return 0;
# If everything fails... TRHOW PYTHON ON IT!!!
local fullpath;
local pythoninterpreter;
local pythonexecutables;
local pythonlocations;
pythoninterpreter="python";
declare -a pythonlocations=("/usr/bin" "/bin");
declare -a pythonexecutables=("python" "python2" "python3");
for path in "${pythonlocations[@]}";
do
for executable in "${pythonexecutables[@]}";
do
fullpath="${path}/${executable}";
if [[ -f "${fullpath}" ]];
then
# printf "Found ${fullpath}\\n";
pythoninterpreter="${fullpath}";
break;
fi;
done;
if [[ "${pythoninterpreter}" != "python" ]];
then
# printf "Breaking... ${pythoninterpreter}\\n"
break;
fi;
done;
firstattempt="$(${pythoninterpreter} -c "import os, sys; print( os.path.abspath( sys.argv[1] ) );" "${target}")" && printf "%s" "${firstattempt}" && return 0;
# printf "Error: Could not determine the absolute path!\\n";
return 1;
fi
}
printf "\\nResults:\\n%s\\nExit: %s\\n" "$(getabsolutepath "./asdfasdf/ asdfasdf")" "${?}"
Ви можете використовувати заміну рядка bash для будь-якого відносного шляху $ line:
line=$(echo ${line/#..\//`cd ..; pwd`\/})
line=$(echo ${line/#.\//`pwd`\/})
echo $line
Основна підстановка переднього рядка відповідає формулі
$ {string / # substring / substitu },
яка тут добре обговорюється: https://www.tldp.org/LDP/abs/html/string-manipulation.html
\
Характер заперечує , /
коли ми хочемо, щоб бути частиною рядка , яку ми знаходимо / замінити.
Мені не вдалося знайти рішення, яке було б чітко переносимо між Mac OS Catalina, Ubuntu 16 та Centos 7, тому я вирішив зробити це з вбудованим python, і це добре працювало для моїх скриптів bash.
to_abs_path() {
python -c "import os; print os.path.abspath('$1')"
}
to_abs_path "/some_path/../secrets"