Есть ли команда для получения абсолютного пути с учетом относительного пути?
Например, я хочу, чтобы строка $содержала абсолютный путь для каждого файла в каталоге ./etc/
find ./ -type f | while read line; do
echo $line
done
Есть ли команда для получения абсолютного пути с учетом относительного пути?
Например, я хочу, чтобы строка $содержала абсолютный путь для каждого файла в каталоге ./etc/
find ./ -type f | while read line; do
echo $line
done
использование:
find $(pwd)/ -type f
чтобы получить все файлы или
echo $(pwd)/$line
для отображения полного пути (если относительный путь имеет значение)
Попробуйте realpath
.
~ $ sudo apt-get install realpath # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc
Чтобы избежать расширения символических ссылок, используйте realpath -s
.
Ответ приходит от команды bash/fish для вывода абсолютного пути к файлу.
Если у вас установлен пакет coreutils, вы можете обычно использовать readlink -f relative_file_name
для получения абсолютного (со всеми разрешенными символическими ссылками)
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
UPD Некоторые объяснения
"$1"
dirname "$1"
cd "$(dirname "$1")
в этот относительный dir и получаем для него абсолютный путь, запустив pwd
команду оболочки$(basename "$1")
echo
itДля чего это стоит, я проголосовал за ответ, который был выбран, но хотел поделиться решением. Недостатком является то, что это только Linux - я потратил около 5 минут, пытаясь найти эквивалент OSX, прежде чем перейти к переполнению стека. Я уверен, что это там.
В 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
ничего не вернет
и в конечном итоге вы получите двойные слэши.
Я думаю, что это самый портативный:
abspath() {
cd "$(dirname "$1")"
printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
cd "$OLDPWD"
}
Он не сработает, если путь не существует.
realpath
, вероятно, лучше
Но...
Начальный вопрос был очень запутанным, с примера, плохо связанного с вопросом, как заявлено.
Выбранный ответ на самом деле отвечает приведенному примеру, а не на вопрос в заголовке. Первая команда - это ответ (действительно ли это? Я сомневаюсь), и она может обойтись без '/'. И я не вижу, что делает вторая команда.
Несколько вопросов смешаны:
изменение относительного пути в абсолютное, что бы оно ни обозначало, возможно, ничего. (Как правило, если вы запускаете команду, например touch foo/bar
, путь foo/bar
должен существовать для вас и, возможно, использоваться в вычислениях до того, как файл будет фактически создан.)
может быть несколько абсолютных путей, обозначающих один и тот же файл (или потенциальный файл), особенно из-за символических ссылок (символических ссылок) на пути, но, возможно, по другим причинам (устройство может быть смонтировано дважды только для чтения). Кто-то может хотеть или не хотеть разрешать эксплицитность таких символических ссылок.
добраться до конца цепочки символических ссылок на файл или имя, отличное от -s ymlink. Это может или не может привести к абсолютному пути, в зависимости от того, как это делается. И кто-то может или не может хотеть разрешить его в абсолютном пути.
Команда readlink foo
без опции дает ответ, только если ее аргумент foo
является символической ссылкой, и этот ответ является значением этой символической ссылки. Никакой другой ссылки не последовало. Ответ может быть относительным путем: каким бы ни было значение аргумента символической ссылки.
Тем не менее, readlink
имеет параметры (-f -e или -m), которые будут работать для всех файлов и давать одно абсолютное имя пути (без символьных ссылок) для файла, фактически обозначенного аргументом.
Это прекрасно работает для всего, что не является символической ссылкой, хотя можно использовать абсолютный путь без разрешения промежуточных символических ссылок в пути. Это делается командой realpath -s foo
В случае аргумента символической ссылки readlink
с его параметрами снова разрешит все символические ссылки на абсолютном пути к аргументу, но это также будет включать все символические ссылки, которые могут встретиться после следования значения аргумента. Возможно, вы не захотите этого, если вам нужен абсолютный путь к самой символической ссылке на аргумент, а не к тому, на что он может ссылаться. Опять же, если foo
является символической realpath -s foo
, realpath -s foo
получит абсолютный путь без разрешения символических ссылок, включая ссылку, заданную в качестве аргумента.
Без опции -s
realpath
делает почти то же самое, что и readlink
, за исключением простого чтения значения ссылки, а также некоторых других вещей. Мне просто не понятно, почему у readlink
есть свои опции, создающие нежелательную избыточность с помощью realpath
.
Изучение Интернета не говорит о многом, за исключением того, что могут быть некоторые различия между системами.
Вывод: realpath
- лучшая команда для использования с максимальной гибкостью, по крайней мере, для запрошенного здесь использования.
Ответ Евгения не совсем сработал для меня, но это сработало:
absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"
Примечание: ваш текущий рабочий каталог не затронут.
Мое любимое решение было один на @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
}
В случае find
, возможно, проще всего просто указать абсолютный путь для поиска, например:
find /etc
find `pwd`/subdir_of_current_dir/ -type f
Если вы используете bash в Mac OS X, у которого не существует realpath, и его readlink не может печатать абсолютный путь, у вас может быть выбор, кроме как для кода собственную версию для печати. Вот моя реализация:
(чистый bash)
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
Наилучшим решением, imho, является то, которое размещено здесь: fooobar.com/questions/4942/....
Для работы требуется python, но, похоже, он охватывает все или большинство крайних случаев и является очень портативным решением.
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
То, что они сказали, кроме find $PWD
или (в bash) find ~+
, немного более удобно.
Подобно @ernest-ответу, но не затрагивая $OLDPWD
или определяя новую функцию, вы можете запустить подселочку (cd <path>; pwd)
$ pwd
/etc/apache2
$ cd ../cups
$ cd -
/etc/apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups
Если относительный путь - путь к каталогу, тогда попробуйте мой, должен быть лучшим:
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") } }'
Если вы хотите преобразовать переменную, содержащую относительный путь в абсолютную, это работает:
dir=`cd "$dir"`
"cd" эхо, не меняя рабочий каталог, потому что выполняется здесь в под-оболочке.
Это realpath
решение от всех остальных, например, когда realpath
сбой 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")" "${?}"