{"id":13537161,"url":"https://github.com/bashup/realpaths","last_synced_at":"2025-07-14T16:31:47.210Z","repository":{"id":51336212,"uuid":"105726082","full_name":"bashup/realpaths","owner":"bashup","description":"Fast, portable path manipulation and symlink resolution in 2K of pure bash","archived":false,"fork":false,"pushed_at":"2024-02-25T09:46:35.000Z","size":27,"stargazers_count":38,"open_issues_count":0,"forks_count":3,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-11-03T02:32:28.774Z","etag":null,"topics":["bash","realpath"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bashup.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-04T02:51:34.000Z","updated_at":"2024-10-11T21:06:07.000Z","dependencies_parsed_at":"2024-11-03T02:30:56.986Z","dependency_job_id":null,"html_url":"https://github.com/bashup/realpaths","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bashup%2Frealpaths","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bashup%2Frealpaths/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bashup%2Frealpaths/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bashup%2Frealpaths/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bashup","download_url":"https://codeload.github.com/bashup/realpaths/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225986603,"owners_count":17555661,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","realpath"],"created_at":"2024-08-01T09:00:55.759Z","updated_at":"2024-11-23T01:18:42.192Z","avatar_url":"https://github.com/bashup.png","language":"Shell","readme":"## Fast path manipulation and symlink resolution for bash\n\nSadly, it's hard to find a portable way to get the \"real path\" of a file, and shell-based emulations of `realpath` and `readlink -f` are usually quite slow compared to the real thing, and don't return *quite* the same results.  (Dealing with missing elements, symlink loops, and the like can also be a challenge, even with the \"real\" `readlink` or `realpath`!)\n\nSo, this module is a thorough implementation of symlink resolution and path canonicalization (for bash 3.2 and up) using plain, portable `readlink` with no options.  Its symlink resolution is about as fast as `readlink -f` when there is only one symlink to follow, and *enormously* faster when the target isn't a symlink.  (It's slower on symlink chains, though.)\n\nTo avoid the need for subshells, all of this module's functions output a single path result via the bash `REPLY` variable.  All functions return success, *always*, as they are designed to provide meaningful results even in the presence of missing, looping, or inaccessible files, symlinks, or directories.\n\nMost of this module's functions also return absolute, well-formed paths: that is, paths that begin with `/` and contain no `.`, `..` or empty components.  (The exceptions are `realpath.follow`, `realpath.dirname`, and `realpath.basename`, which can return relative paths.)\n\n(**Note:** unlike their operating system counterparts, these functions do *not* accept options, and always return exactly **one** path as a result.  Do not pass `--` to them or expect to get multiple results for multiple arguments!)\n\n**Contents**\n\n\u003c!-- toc --\u003e\n\n  * [Installation, Requirements And Use](#installation-requirements-and-use)\n  * [Resolving Symlinks](#resolving-symlinks)\n    + [realpath.location](#realpathlocation)\n    + [realpath.resolved](#realpathresolved)\n    + [realpath.follow](#realpathfollow)\n  * [Path String Manipulation](#path-string-manipulation)\n    + [realpath.absolute *[paths...]*](#realpathabsolute-paths)\n    + [realpath.relative *path [basedir]*](#realpathrelative-path-basedir)\n    + [realpath.dirname](#realpathdirname)\n    + [realpath.basename](#realpathbasename)\n  * [Determinining Canonical Paths](#determinining-canonical-paths)\n    + [realpath.canonical](#realpathcanonical)\n- [License](#license)\n\n\u003c!-- tocstop --\u003e\n\n### Installation, Requirements And Use\n\nCopy and paste the [code](realpaths) into your script, or place it on `PATH` and `source realpaths`.  (if you have [basher](https://github.com/basherpm/basher), you can `basher install bashup/realpaths` to get it installed on your `PATH`.)  The code is licensed CC0, so you are not required to add any attribution or copyright notices to your project.\n\nThe code's only extenal requirement is `readlink`, which is used only to resolve a single symlink level with no special options, and therefore can be a GNU, BSD, or OS X readlink implementation.\n\n### Resolving Symlinks\n\n#### realpath.location\n\nSets `REPLY` to the absolute, well-formed path of a physical directory that contains (or *would* contain) the supplied path.  (Equivalent to the `dirname` of  `realpath.resolved \"$1\"`.) Always succeeds.\n\n(Pass this function `\"$BASH_SOURCE\"` to get the directory of the currently-executing file, or `\"$0\"` to get the directory of the current main script.)\n\n#### realpath.resolved\n\nSets `REPLY` to the absolute, well-formed path of `$1`, with symlinks in the final path portion resolved.  (Equivalent to the `realpath.absolute` of `realpath.follow \"$1\"`.)  Always succeeds.  The result is not a symlink unless it is inaccessible or part of a symlink cycle.\n\n#### realpath.follow\n\nSets `REPLY` to the first non-symlink (or last accessible, non-looping symlink) in the symlink chain beginning at `$1`.  Replies with the unchanged `$1` if it is not a symlink.  Always succeeds.  The result is not a symlink unless it is inaccessible or part of a symlink cycle.  (Note: the result *can* be a relative path, if both `$1` and all the symlinks in the chain are relative.  Use `realpath.resolved` instead if you want a guaranteed-absolute path.)\n\n### Path String Manipulation\n\n#### realpath.absolute *[paths...]*\n\nSets `REPLY` to the absolute, well-formed combination of the supplied path(s). Always succeeds.\n\nEach path may be absolute or relative.  The resulting path is the combination of the *last* absolute path in the list supplied, combined with any relative paths that follow it.  If no absolute paths are given, the relative paths are processed relative to `$PWD` -- so passing zero arguments simply returns `$PWD`.\n\nRelative path parts are resolved *logically* rather than physically.  That is to say, `..` is processed by removing elements from the path string, rather than by inspecting the filesystem.  (So symlinks are not processed in any way, and the existence or accessibility of the files and directories is irrelevant: with the exception of defaulting to `$PWD`, the result is obtained solely via string manipulation of the supplied paths.)\n\nAs per POSIX, bash, Python, and other path handling libraries, absolute paths beginning with *exactly* two slashes are treated specially.  The string returned by `realpath.absolute` will begin with two slashes if the last absolute path argument began with exactly two slashes; otherwise the result will begin with only one slash, no matter how many slashes the last absolute path argument began with.\n\n#### realpath.relative *path [basedir]*\n\nSets `REPLY` to the shortest relative path from *basedir* to *path*.  *basedir* defaults to `$PWD` if not supplied.  Always succeeds.\n\nThe *path* and *basedir* are preprocessed with `realpath.absolute`, so they can be relative paths, or absolute ones containing relative components.  (The result is identical to calling Python's `os.path.relpath` function with the same arguments, but is much faster than even the fork to start Python would be.)\n\nThe main use case for this function is portably creating relative symlinks without needing `ln --relative` or `realpath --relative-to`.  Specifically, if you are doing `ln -s somepath somedir/link`, you can make it relative using `realpath.relative somepath somedir; ln -s \"$REPLY\" somedir/link`.\n\n#### realpath.dirname\n\nSets `REPLY` to the directory name of `$1`, always returning success.  Produces the *exact* same results as `REPLY=$(dirname -- \"$1\")` except much, *much* faster.  Always succeeds.\n\n#### realpath.basename\n\nSets `REPLY` to the basename of `$1`.  Produces the *exact* same results as `REPLY=$(basename -- \"$1\")` except much, *much* faster.  Always succeeds.\n\n### Determinining Canonical Paths\n\n#### realpath.canonical\n\nSets `REPLY` to the *fully canonicalized* form of `$1`, recursively resolving symlinks in every part of the path where that can be done, roughly equivalent to `realpath -m` or `readlink -m`.   Always succeeds, but potentially rather slow, depending on how many directories are symlinks.\n\nYou don't really need this function unless you are trying to determine whether divergent paths lead to the \"same\" file.  For use cases that don't involve comparing paths, `realpath.resolved` should be sufficient, or perhaps even `realpath.absolute`.  (Note, too, that using canonical paths can result in user confusion, since users then have to reconcile their inputs with your outputs!)\n\n## License\n\n\u003cp xmlns:dct=\"http://purl.org/dc/terms/\" xmlns:vcard=\"http://www.w3.org/2001/vcard-rdf/3.0#\"\u003e\n  \u003ca rel=\"license\" href=\"http://creativecommons.org/publicdomain/zero/1.0/\"\u003e\u003cimg src=\"https://licensebuttons.net/p/zero/1.0/80x15.png\" style=\"border-style: none;\" alt=\"CC0\" /\u003e\u003c/a\u003e\u003cbr /\u003e\n  To the extent possible under law, \u003ca rel=\"dct:publisher\" href=\"https://github.com/pjeby\"\u003e\u003cspan property=\"dct:title\"\u003ePJ Eby\u003c/span\u003e\u003c/a\u003e\n  has waived all copyright and related or neighboring rights to \u003cspan property=\"dct:title\"\u003ebashup/realpaths\u003c/span\u003e.\nThis work is published from: \u003cspan property=\"vcard:Country\" datatype=\"dct:ISO3166\" content=\"US\" about=\"https://github.com/bashup/realpaths\"\u003eUnited States\u003c/span\u003e.\n\u003c/p\u003e\n","funding_links":[],"categories":["Shell","Libraries"],"sub_categories":["Testing"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbashup%2Frealpaths","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbashup%2Frealpaths","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbashup%2Frealpaths/lists"}