Bash: получить абсолютный путь, указанный относительный

Есть ли команда для получения абсолютного пути с учетом относительного пути?

Например, я хочу, чтобы строка $содержала абсолютный путь для каждого файла в каталоге ./etc/

find ./ -type f | while read line; do
   echo $line
done

Ответ 1

использование:

find $(pwd)/ -type f

чтобы получить все файлы или

echo $(pwd)/$line

для отображения полного пути (если относительный путь имеет значение)

Ответ 2

Попробуйте realpath.

~ $ sudo apt-get install realpath  # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc

Чтобы избежать расширения символических ссылок, используйте realpath -s.

Ответ приходит от команды bash/fish для вывода абсолютного пути к файлу.

Ответ 3

Если у вас установлен пакет coreutils, вы можете обычно использовать readlink -f relative_file_name для получения абсолютного (со всеми разрешенными символическими ссылками)

Ответ 4

#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"

UPD Некоторые объяснения

  • Этот script получает относительный путь как аргумент "$1"
  • Затем мы получаем часть dirname этого пути (вы можете передать либо dir или файл в этот script): dirname "$1"
  • Затем мы cd "$(dirname "$1") в этот относительный dir и получаем для него абсолютный путь, запустив pwd команду оболочки
  • После этого мы добавляем базовое имя в абсолютный путь: $(basename "$1")
  • В качестве последнего шага мы echo it

Ответ 5

Для чего это стоит, я проголосовал за ответ, который был выбран, но хотел поделиться решением. Недостатком является то, что это только 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 ничего не вернет и в конечном итоге вы получите двойные слэши.

Ответ 6

Я думаю, что это самый портативный:

abspath() {                                               
    cd "$(dirname "$1")"
    printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
    cd "$OLDPWD"
}

Он не сработает, если путь не существует.

Ответ 7

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 - лучшая команда для использования с максимальной гибкостью, по крайней мере, для запрошенного здесь использования.

Ответ 8

Ответ Евгения не совсем сработал для меня, но это сработало:

    absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"

Примечание: ваш текущий рабочий каталог не затронут.

Ответ 9

Мое любимое решение было один на @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
}

Ответ 10

В случае find, возможно, проще всего просто указать абсолютный путь для поиска, например:

find /etc
find `pwd`/subdir_of_current_dir/ -type f

Ответ 11

Если вы используете 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

Ответ 12

Наилучшим решением, imho, является то, которое размещено здесь: fooobar.com/questions/4942/....

Для работы требуется python, но, похоже, он охватывает все или большинство крайних случаев и является очень портативным решением.

  1. С разрешением символических ссылок:
python -c "import os,sys; print os.path.realpath(sys.argv[1])" path/to/file
  1. или без него:
python -c "import os,sys; print os.path.abspath(sys.argv[1])" path/to/file

Ответ 13

То, что они сказали, кроме find $PWD или (в bash) find ~+, немного более удобно.

Ответ 14

Подобно @ernest-ответу, но не затрагивая $OLDPWD или определяя новую функцию, вы можете запустить подселочку (cd <path>; pwd)

$ pwd
/etc/apache2
$ cd ../cups 
$ cd -
/etc/apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups

Ответ 15

Если относительный путь - путь к каталогу, тогда попробуйте мой, должен быть лучшим:

absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)

echo $absPath

Ответ 16

echo "mydir/doc/ mydir/usoe ./mydir/usm" |  awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'

Ответ 17

Если вы хотите преобразовать переменную, содержащую относительный путь в абсолютную, это работает:

   dir=`cd "$dir"`

"cd" эхо, не меняя рабочий каталог, потому что выполняется здесь в под-оболочке.

Ответ 18

Это 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")" "${?}"