Bash: получить абсолютный путь, заданный относительным - PullRequest
119 голосов
/ 14 ноября 2010

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

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

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

Ответы [ 18 ]

131 голосов
/ 15 февраля 2013

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

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

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

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

94 голосов
/ 14 ноября 2010

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

52 голосов
/ 14 ноября 2010

используйте:

find $(pwd)/ -type f

для получения всех файлов или

echo $(pwd)/$line

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

45 голосов
/ 24 июля 2015
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"

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

  1. Этот скрипт получает относительный путь в качестве аргумента "$1"
  2. Тогда мы получаем dirname часть этого пути (вы можете передать dir или файл в этот скрипт): dirname "$1"
  3. Затем мы cd "$(dirname "$1") в этот относительный dir и получим абсолютный путь для него, запустив pwd shell shell
  4. После этого мы добавляем basename к абсолютному пути: $(basename "$1")
  5. В качестве последнего шага мы echo it
31 голосов
/ 28 ноября 2012

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

yields

/etc/passwd

Вы в безопасности, если вы нацеливаетесь на каталог, basename ничего не вернет, и вы просто получите двойную косую черту в конечном выводе.

22 голосов
/ 10 декабря 2013

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

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

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

16 голосов
/ 17 мая 2013

realpath, вероятно, лучше

Но ...

Первоначальный вопрос был очень запутанным, с примером плохо связанные с вопросом, как указано.

Выбранный ответ фактически отвечает приведенному примеру, а не совсем вопрос в названии. Первая команда - это ответ (это действительно ? Я сомневаюсь), и мог бы обойтись без '/'. И я не вижу что делает вторая команда.

Смешано несколько вопросов:

  • изменение относительного пути к абсолютному обозначает, возможно, ничего. ( Как правило, если вы выполните команду, такую ​​как touch foo/bar, путь foo/bar должен существовать для вас и, возможно, использоваться в вычисление до того, как файл будет фактически создан. )

  • может быть несколько абсолютных путей, обозначающих один и тот же файл (или потенциальный файл), особенно из-за символических ссылок (символических ссылок) на пути, но, возможно, по другим причинам (устройство может смонтирован дважды только для чтения). Один может или не может хотеть решить простота таких символических ссылок.

  • получение до конца цепочки символических ссылок на несимвольную ссылку файл или имя. Это может или не может привести к абсолютному пути, в зависимости от того, как это сделано. И можно, а может и не хотеть разрешите его в абсолютный путь.

Команда readlink foo без опции дает ответ, только если ее аргумент foo является символической ссылкой, и этот ответ является значением этого символическая. Никакой другой ссылки не последовало. Ответ может быть относительным путем: каким бы ни было значение аргумента символической ссылки.

Однако у readlink есть опции (-f -e или -m), которые будут работать для всех файлы, и дать один абсолютный путь (тот, без символических ссылок) для файл фактически обозначается аргументом.

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

В случае аргумента символической ссылки, readlink с его параметрами снова разрешите все символические ссылки на абсолютном пути к аргументу, но это также будет включать в себя все символические ссылки, которые могут встретиться после значения аргумента. Вы можете не хотеть этого, если хотите абсолютный путь к самой символической ссылке аргумента, а не к чему-либо это может ссылаться на. Опять же, если foo является символической ссылкой, realpath -s foo будет получить абсолютный путь без разрешения символических ссылок, включая в качестве аргумента.

Без опции -s, realpath почти так же, как readlink, за исключением простого чтения значения ссылки, а также нескольких другие вещи. Мне просто не понятно, почему у readlink есть варианты, создавая, по-видимому, нежелательную избыточность с realpath.

Изучение Интернета не говорит о многом, кроме того, что может быть некоторые различия в системах.

Вывод: realpath - лучшая команда для использования, с наибольшим гибкость, по крайней мере, для использования, запрашиваемого здесь.

6 голосов
/ 13 февраля 2017

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

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

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

5 голосов
/ 10 июля 2018

Моим любимым решением было решение от @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
}
3 голосов
/ 22 февраля 2014

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

(чистый BSK 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
...