关于macos:OS X的Bash脚本绝对路径

Bash script absolute path with OS X

我正在尝试获取OS X上当前运行脚本的绝对路径。

我看到很多回复都是针对readlink -f $0。然而,由于OSX的readlink与BSD的相同,它只是不起作用(它与GNU的版本一起工作)。

有现成的解决方案吗?


这三个简单步骤将解决这一问题和许多其他OS X问题:

  • 安装自制
  • brew install coreutils
  • grealpath .
  • (3)可改为仅realpath,见(2)输出


    有一个realpath()c函数可以完成这项工作,但在命令行上没有看到任何可用的功能。这里有一个快速而肮脏的替代品:

    1
    2
    3
    4
    5
    6
    7
    #!/bin/bash

    realpath() {
        [[ $1 = /* ]] && echo"$1" || echo"$PWD/${1#./}"
    }

    realpath"$0"

    如果路径以/开头,则会逐字打印路径。如果不是,它必须是一个相对路径,因此它会将$PWD提前到前面。#./部分从$1前部剥离./


    呃。我发现之前的答案有点欠缺,原因有几个:特别是,它们不能解析多个层次的符号链接,而且它们是非常"bash-y"。虽然最初的问题确实明确要求使用"bash脚本",但它也提到了mac os x类似于BSD的非GNUreadlink。所以这里有一个合理的可移植性的尝试(我用bash检查了它作为'sh'和dash),解析任意数量的符号链接;它也应该在路径中使用空格,尽管我不确定是否存在空格(实用程序本身的基名),那么也许,嗯,避免这样做?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #!/bin/sh
    realpath() {
      OURPWD=$PWD
      cd"$(dirname"$1")"
      LINK=$(readlink"$(basename"$1")")
      while ["$LINK" ]; do
        cd"$(dirname"$LINK")"
        LINK=$(readlink"$(basename"$1")")
      done
      REALPATH="$PWD/$(basename"$1")"
      cd"$OURPWD"
      echo"$REALPATH"
    }
    realpath"$@"

    希望这对某人有用。


    python解决方案的一个更友好的命令行变体:

    1
    python -c"import os; print(os.path.realpath('$1'))"

    因为有一条真正的道路,正如其他人所指出的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // realpath.c
    #include <stdio.h>
    #include <stdlib.h>

    int main (int argc, char* argv[])
    {
      if (argc > 1) {
        for (int argIter = 1; argIter < argc; ++argIter) {
          char *resolved_path_buffer = NULL;
          char *result = realpath(argv[argIter], resolved_path_buffer);

          puts(result);

          if (result != NULL) {
            free(result);
          }
        }
      }

      return 0;
    }

    Makefile:

    1
    2
    3
    4
    5
    6
    7
    8
    #Makefile
    OBJ = realpath.o

    %.o: %.c
          $(CC) -c -o $@ $< $(CFLAGS)

    realpath: $(OBJ)
          gcc -o $@ $^ $(CFLAGS)

    然后用make编译,并与以下对象建立软链接:ln -s $(pwd)/realpath /usr/local/bin/realpath


    使用python获取:

    1
    2
    3
    4
    5
    #!/usr/bin/env python
    import os
    import sys

    print(os.path.realpath(sys.argv[1]))


    我正在寻找一个可用于系统配置脚本的解决方案,即在安装自制之前运行。由于缺乏适当的解决方案,我只需将任务卸载到跨平台语言,例如Perl:

    1
    script_abspath=$(perl -e 'use Cwd"abs_path"; print abs_path(@ARGV[0])' --"$0")

    通常我们真正想要的是包含目录:

    1
    here=$(perl -e 'use File::Basename; use Cwd"abs_path"; print dirname(abs_path(@ARGV[0]));' --"$0")


    正如你在上面看到的,大约6个月前我拍了一张照片。我完全忘了它,直到我发现自己又需要类似的东西。我是完全震惊地看到它是多么的初级;我一直在教书我自己已经花了大约一年的时间集中编写代码了,但我经常觉得也许在最坏的时候我什么都没学到。

    我会删除上面的"解决方案",但我真的希望它是在过去的几个月里我真正学到了多少。

    但我离题了。我昨天晚上坐下来,把一切都解决了。解释这些评论应该足够了。如果你想追踪副本,我会继续为了继续工作,你可以遵循这个要点。这可能是你需要的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    #!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

    ## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
    ## dereference symbolic links (ala 'readlink') until the originating file
    ## is found. This is effectively the same function provided in stdlib.h as
    ## 'realpath' and on the command line in GNU 'readlink -f'.

    ## Neither of these tools, however, are particularly accessible on the many
    ## systems that do not have the GNU implementation of readlink, nor ship
    ## with a system compiler (not to mention the requisite knowledge of C).

    ## This script is written with portability and (to the extent possible, speed)
    ## in mind, hence the use of printf for echo and case statements where they
    ## can be substituded for test, though I've had to scale back a bit on that.

    ## It is (to the best of my knowledge) written in standard POSIX shell, and
    ## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
    ## issues with it, though I'm not sure why; so probably best to avoid for now.

    ## Particularly useful (in fact, the reason I wrote this) is the fact that
    ## it can be used within a shell script to find the path of the script itself.
    ## (I am sure the shell knows this already; but most likely for the sake of
    ## security it is not made readily available. The implementation of"$0"
    ## specificies that the $0 must be the location of **last** symbolic link in
    ## a chain, or wherever it resides in the path.) This can be used for some
    ## ...interesting things, like self-duplicating and self-modifiying scripts.

    ## Currently supported are three errors: whether the file specified exists
    ## (ala ENOENT), whether its target exists/is accessible; and the special
    ## case of when a sybolic link references itself"foo -> foo": a common error
    ## for beginners, since 'ln' does not produce an error if the order of link
    ## and target are reversed on the command line. (See POSIX signal ELOOP.)

    ## It would probably be rather simple to write to use this as a basis for
    ## a pure shell implementation of the 'symlinks' util included with Linux.

    ## As an aside, the amount of code below **completely** belies the amount
    ## effort it took to get this right -- but I guess that's coding for you.

    ##===-------------------------------------------------------------------===##

    for argv; do :; done # Last parameter on command line, for options parsing.

    ## Error messages. Use functions so that we can sub in when the error occurs.

    recurses(){ printf"Self-referential:
    \t$argv ->
    \t$argv
    "
    ;}
    dangling(){ printf"Broken symlink:
    \t$argv ->
    \t"
    $(readlink"$argv")"
    "
    ;}
    errnoent(){ printf"No such file:"$@"
    "
    ;} # Borrow a horrible signal name.

    # Probably best not to install as 'pathfull', if you can avoid it.

    pathfull(){ cd"$(dirname"$@")"; link="$(readlink"$(basename"$@")")"

    ## 'test and 'ls' report different status for bad symlinks, so we use this.

     if [ ! -e"
    $@" ]; then if $(ls -d"$@" 2>/dev/null) 2>/dev/null;  then
        errnoent 1>&2; exit 1; elif [ ! -e"
    $@" -a"$link" ="$@" ];   then
        recurses 1>&2; exit 1; elif [ ! -e"
    $@" ] && [ ! -z"$link" ]; then
        dangling 1>&2; exit 1; fi
     fi

    ## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

     if [ -z"
    $link" ]; then if ["$(dirname"$@" | cut -c1)" = '/' ]; then
       printf"
    $@
    "; exit 0; else printf"$(pwd)/$(basename"$@")
    "; fi; exit 0
     fi

    ## Walk the symlinks back to the origin. Calls itself recursivly as needed.

     while ["
    $link" ]; do
       cd"
    $(dirname"$link")"; newlink="$(readlink"$(basename"$link")")"
       case"
    $newlink" in
       "
    $link") dangling 1>&2 && exit 1                                       ;;
             '') printf"
    $(pwd)/$(basename"$link")
    "; exit 0                 ;;
              *) link="
    $newlink" && pathfull"$link"                           ;;
       esac
     done
     printf"
    $(pwd)/$(basename"$newlink")
    "
    }

    ## Demo. Install somewhere deep in the filesystem, then symlink somewhere
    ## else, symlink again (maybe with a different name) elsewhere, and link
    ## back into the directory you started in (or something.) The absolute path
    ## of the script will always be reported in the usage, along with"
    $0".

    if [ -z"
    $argv" ]; then scriptname="$(pathfull"$0")"

    # Yay ANSI l33t codes! Fancy.
     printf"

    \033[3mfrom/as: \033[4m$0\033[0m

    \033[1mUSAGE:\033[0m  "
     printf"
    \033[4m$scriptname\033[24m [ link | file | dir ]

            "
     printf"
    Recursive readlink for the authoritative file, symlink after"
     printf"
    symlink.


             \033[4m$scriptname\033[24m

           "
     printf"
    From within an invocation of a script, locate the script's"
     printf"own file
             (no matter where it has been linked or"
     printf"from where it is being called).

    "

    else pathfull"$@"
    fi


    Mac OS X的realpath

    1
    2
    3
    4
    5
    realpath() {
        path=`eval echo"$1"`
        folder=$(dirname"$path")
        echo $(cd"$folder"; pwd)/$(basename"$path");
    }

    具有相关路径的示例:

    1
    realpath"../scripts/test.sh"

    主文件夹示例

    1
    realpath"~/Test/../Test/scripts/test.sh"


    基于与评论者的沟通,我同意这是非常困难的,没有繁琐的方式来实现一个真正的路径行为完全相同的Ubuntu。

    但下面的版本,可以处理角箱的最佳答案,不能满足我对MacBook的日常需求。将此代码放入~/.bashrc并记住:

    • arg只能是1个文件或目录,没有通配符
    • 目录或文件名中没有空格
    • 至少存在文件或目录的父目录
    • 请随意使用。…/东西,这些是安全的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
        # 1. if is a dir, try cd and pwd
        # 2. if is a file, try cd its parent and concat dir+file
        realpath() {
         ["$1" ="" ] && return 1

         dir=`dirname"$1"`
         file=`basename"$1"`

         last=`pwd`

         [ -d"$dir" ] && cd $dir || return 1
         if [ -d"$file" ];
         then
           # case 1
           cd $file && pwd || return 1
         else
           # case 2
           echo `pwd`/$file | sed 's/\/\//\//g'
         fi

         cd $last
        }