代码之家  ›  专栏  ›  技术社区  ›  The Mighty Rubber Duck

带有OS X的bash脚本绝对路径

  •  68
  • The Mighty Rubber Duck  · 技术社区  · 14 年前

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

    我看到很多回复 readlink -f $0 . 但是自从OS X readlink 与BSD相同,它只是不工作(它与GNU的版本一起工作)。

    有现成的解决方案吗?

    10 回复  |  直到 6 年前
        1
  •  70
  •   John Kugelman Michael Hodel    14 年前

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

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

    如果路径以 / . 如果不是,它必须是一个相对路径,因此它会提前 $PWD 到前面去。这个 #./ 零件剥离 ./ 从前面 $1 .

        2
  •  81
  •   Peter Mortensen Abd Al-Kareem Attiya    6 年前

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

    1. 安装 Homebrew
    2. brew install coreutils
    3. grealpath .

    (3)可改为公正 realpath ,见(2)输出

        3
  •  20
  •   Geoff Nixon    11 年前

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

    #!/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 "$@"
    

    希望这对某人有用。

        4
  •  9
  •   slm Vikas Hardia    7 年前

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

    python -c "import os; print(os.path.realpath('$1'))"
    
        5
  •  6
  •   4ae1e1    9 年前

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

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

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

    here=$(perl -e 'use File::Basename; use Cwd "abs_path"; print dirname(abs_path(@ARGV[0]));' -- "$0")
    
        6
  •  6
  •   WaffleSouffle    7 年前

    因为有一个 realpath 正如其他人指出的那样:

    // 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:

    #Makefile
    OBJ = realpath.o
    
    %.o: %.c
          $(CC) -c -o $@ $< $(CFLAGS)
    
    realpath: $(OBJ)
          gcc -o $@ $^ $(CFLAGS)
    

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

        7
  •  6
  •   Peter Mortensen Abd Al-Kareem Attiya    6 年前

    使用python获取:

    #!/usr/bin/env python
    import os
    import sys
    
    print(os.path.realpath(sys.argv[1]))
    
        8
  •  2
  •   Geoff Nixon    11 年前

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

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

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

    #!/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:\n\t$argv ->\n\t$argv\n" ;}
    dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
    errnoent(){ printf "No such file: "$@"\n" ;} # 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 "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\n"; 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")\n"; exit 0                 ;;
              *) link="$newlink" && pathfull "$link"                           ;;
       esac
     done
     printf "$(pwd)/$(basename "$newlink")\n"
    }
    
    ## 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 "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
     printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
     printf "Recursive readlink for the authoritative file, symlink after "
     printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
     printf " From within an invocation of a script, locate the script's "
     printf "own file\n         (no matter where it has been linked or "
     printf "from where it is being called).\n\n"
    
    else pathfull "$@"
    fi
    
        9
  •  1
  •   Evgeny Karpov    7 年前

    Mac OS X的realpath

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

    具有相关路径的示例:

    realpath "../scripts/test.sh"
    

    主文件夹示例

    realpath "~/Test/../Test/scripts/test.sh"
    
        10
  •  -2
  •   occia Dennis Williamson    6 年前

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

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

    • arg只能是1个文件或目录,没有通配符
    • 目录或文件名中没有空格
    • 至少存在文件或目录的父目录
    • 请随意使用。…/东西,这些是安全的

        # 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
        }