ฉันกำลังพยายามหาเส้นทางที่แน่นอนไปยังสคริปต์ที่กำลังทำงานอยู่บน OS X
ฉันเห็นการตอบกลับreadlink -f $0
มากมาย อย่างไรก็ตามเนื่องจาก OS X readlink
เหมือนกับ BSD จึงไม่สามารถใช้งานได้ (ใช้ได้กับเวอร์ชันของ GNU)
มีวิธีแก้ปัญหาแบบสำเร็จรูปหรือไม่?
ฉันกำลังพยายามหาเส้นทางที่แน่นอนไปยังสคริปต์ที่กำลังทำงานอยู่บน OS X
ฉันเห็นการตอบกลับreadlink -f $0
มากมาย อย่างไรก็ตามเนื่องจาก OS X readlink
เหมือนกับ BSD จึงไม่สามารถใช้งานได้ (ใช้ได้กับเวอร์ชันของ GNU)
มีวิธีแก้ปัญหาแบบสำเร็จรูปหรือไม่?
$( cd "$(dirname "$0")" ; pwd -P )
คำตอบ:
มีrealpath()
ฟังก์ชัน C ที่ใช้งานได้ แต่ฉันไม่เห็นอะไรเลยในบรรทัดคำสั่ง นี่คือการเปลี่ยนที่รวดเร็วและสกปรก:
#!/bin/bash
realpath() {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
realpath "$0"
สิ่งนี้จะพิมพ์เส้นทางคำต่อคำหากขึ้นต้นด้วย/
. ถ้าไม่ใช่มันจะต้องเป็นพา ธ สัมพัทธ์มันจึง$PWD
นำหน้าไปข้างหน้า #./
ส่วนแถบออกจากด้านหน้าของ./
$1
realpath ../something
ผลตอบแทน$PWD/../something
command -v realpath >/dev/null 2>&1 || realpath() { ... }
สามขั้นตอนง่ายๆเหล่านี้จะแก้ปัญหานี้และปัญหาอื่น ๆ ของ OS X:
brew install coreutils
grealpath .
(3) อาจเปลี่ยนเป็นเพียงrealpath
ดู (2) เอาต์พุต
$( cd "$(dirname "$0")" ; pwd -P )
งานได้ดีสำหรับฉัน
XXX
และใครบางคนcd xxx
จากนั้นจะกลับมาpwd
.../xxx
ยกเว้นสำหรับคำตอบนี้ทั้งหมดของการแก้ปัญหาดังกล่าวข้างต้นส่งกลับเมื่อสิ่งที่คุณต้องการจริงๆคือxxx
XXX
ขอบคุณ!
realpath
แล้วสิ่งที่เกิดขึ้นเมื่อคุณเกือบจะแน่นอนจะต้องมีรายการอื่น ๆ จากcoreutils
? เขียนฟังก์ชันเหล่านั้นใหม่ใน bash ด้วยหรือไม่? : P
ฮึ. ฉันพบว่าคำตอบก่อนหน้านี้ค่อนข้างต้องการเหตุผลบางประการ: โดยเฉพาะอย่างยิ่งคำตอบเหล่านี้ไม่สามารถแก้ไขลิงก์สัญลักษณ์หลายระดับได้และคำตอบเหล่านี้เป็น "Bash-y" อย่างยิ่ง ขณะที่คำถามเดิมไม่ชัดเจนขอ "ทุบตีสคริปต์" ก็ยังทำให้การพูดถึง Mac OS X ของ BSD 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 "$@"
หวังว่าจะมีประโยชน์กับใครบางคน
local
สำหรับตัวแปรที่กำหนดไว้ภายในฟังก์ชันเท่านั้นเพื่อไม่ให้เนมสเปซทั่วโลกเป็นมลพิษ เช่นlocal OURPWD=...
. ทำงานอย่างน้อยสำหรับทุบตี
BASENAME=$(basename "$LINK")
ภายใน while และใช้สิ่งนั้นในตัวตั้งค่า LINK ตัวที่สองและตัวตั้งค่า REALPATH
..
อ้างอิงผู้ปกครองในแบบที่realpath
ต้องการ เมื่อcoreutils
ติดตั้งhomebrew ln -s /var/log /tmp/linkexample
แล้วลองrealpath /tmp/linkexample/../
; ภาพพิมพ์/private/var
นี้ แต่ฟังก์ชันของคุณสร้างขึ้น/tmp/linkexample/..
แทนเนื่องจาก..
ไม่ใช่ symlink
ตัวแปรที่เป็นมิตรกับบรรทัดคำสั่งเพิ่มเติมของโซลูชัน Python:
python -c "import os; print(os.path.realpath('$1'))"
python -c "import os; import sys; print(os.path.realpath(sys.argv[1]))"
ฉันกำลังมองหาวิธีแก้ปัญหาสำหรับใช้ในสคริปต์การจัดเตรียมระบบกล่าวคือเรียกใช้ก่อนที่จะติดตั้ง Homebrew ด้วยซ้ำ หากขาดวิธีแก้ปัญหาที่เหมาะสมฉันก็แค่ยกเลิกงานไปเป็นภาษาข้ามแพลตฟอร์มเช่น 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")
FULLPATH=$(perl -e "use Cwd 'abs_path'; print abs_path('$0')")
คุณอาจจะลดความซับซ้อนของรุ่นแรกที่จะ เหตุผลใด ๆ ที่ต่อต้าน?
''
ไม่สามารถกันกระสุนได้ จะแตกหาก$0
มีใบเสนอราคาเดียวตัวอย่างเช่น ตัวอย่างง่ายๆ: ลองใช้เวอร์ชันของคุณ/tmp/'/test.sh
และโทร/tmp/'/test.sh
ตามเส้นทางแบบเต็ม
/tmp/'.sh
นั้น
เนื่องจากมี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
gcc realpath.c -o /usr/local/bin/realpath
ไหม
/usr/local/bin
ใช้ Python เพื่อรับมัน:
#!/usr/bin/env python
import os
import sys
print(os.path.realpath(sys.argv[1]))
abs_path () {
echo "$(cd $(dirname "$1");pwd)/$(basename "$1")"
}
dirname
จะให้ชื่อไดเร็กทอรีของ/path/to/file
เช่น/path/to
.
cd /path/to; pwd
ทำให้แน่ใจว่าเส้นทางนั้นแน่นอน
basename
จะให้เพียงชื่อไฟล์ใน/path/to/file
เช่นfile
.
ดังที่คุณเห็นด้านบนฉันถ่ายภาพนี้เมื่อประมาณ 6 เดือนที่แล้ว ฉันลืมเรื่องนี้ไปโดยสิ้นเชิงจนกระทั่งพบว่าตัวเองต้องการสิ่งที่คล้ายกันอีกครั้ง ฉัน ตกใจมากที่เห็นว่ามันเป็นพื้นฐานแค่ไหน ฉันได้สอนตัวเองให้เขียนโค้ดอย่างเข้มข้นมาประมาณหนึ่งปีแล้ว แต่ฉันมักจะรู้สึกว่าบางทีฉันอาจจะไม่ได้เรียนรู้อะไรเลยเมื่อสิ่งต่างๆเลวร้ายที่สุด
ฉันจะลบ 'วิธีแก้ปัญหา' ด้านบน แต่ฉันชอบที่จะเป็นบันทึกว่าฉันได้เรียนรู้มากแค่ไหนในช่วงสองสามเดือนที่ผ่านมา
แต่ฉันพูดนอกเรื่อง ฉันนั่งลงและทำงานทั้งหมดเมื่อคืนนี้ คำอธิบายในความคิดเห็นควรเพียงพอ หากคุณต้องการติดตามสำเนาที่ฉันกำลังดำเนินการอยู่คุณสามารถทำตามส่วนสำคัญนี้ นี่อาจเป็นสิ่งที่คุณต้องการ
#!/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
..
การอ้างอิงระดับบนสุด เช่น/foo/link_to_other_directory/..
ได้รับการแก้ไขแล้ว/foo
แทนที่จะเป็นพาเรนต์ของเส้นทางใดก็ตามที่ symlink /foo/link_to_other_directory
ชี้ไป readlink -f
และrealpath
แก้ไขคอมโพเนนต์พา ธ แต่ละรายการโดยเริ่มต้นที่รูทและอัพเดตเป้าหมายลิงก์ที่เตรียมไว้ล่วงหน้าไปยังส่วนที่เหลือที่กำลังประมวลผล ฉันได้เพิ่มคำตอบสำหรับคำถามนี้โดยนำตรรกะนั้นมาใช้ใหม่
realpath สำหรับ Mac OS X
realpath() {
path=`eval echo "$1"`
folder=$(dirname "$path")
echo $(cd "$folder"; pwd)/$(basename "$path");
}
ตัวอย่างกับเส้นทางที่เกี่ยวข้อง:
realpath "../scripts/test.sh"
ตัวอย่างกับโฮมโฟลเดอร์
realpath "~/Test/../Test/scripts/test.sh"
..
ไม่ได้ให้คำตอบที่ถูกต้องดังนั้นฉันจึงเพิ่มการตรวจสอบว่าเส้นทางที่กำหนดเป็นไดเร็กทอรีหรือไม่: if test -d $path ; then echo $(cd "$path"; pwd) ; else [...]
"$(dirname $(dirname $(realpath $0)))"
ทำงานดังนั้นต้องการอย่างอื่น ...
echo
จะยังไม่ได้รับการปั่นหัว
..
การอ้างอิงผู้ปกครองก่อนที่จะแก้ไข symlinks ไม่ใช่หลังจาก; ลองนี้กับ homebrew coreutils
ติดตั้งสร้างการเชื่อมโยงกับการln -s /var/log /tmp/linkexample
ทำงานแล้วrealpath /tmp/linkexample/../
; ภาพพิมพ์/private/var
นี้ แต่ฟังก์ชันของคุณจะสร้างขึ้น/tmp/linkexample/..
แทนเนื่องจากpwd
ยังคงแสดงอยู่/tmp/linkexample
หลังซีดี
ใน macOS วิธีแก้ปัญหาเดียวที่ฉันพบสิ่งนี้ที่จัดการกับลิงก์สัญลักษณ์ได้อย่างน่าเชื่อถือคือการใช้realpath
ไฟล์. เนื่องจากสิ่งนี้ต้องการbrew install coreutils
ฉันจึงทำขั้นตอนนั้นโดยอัตโนมัติ การใช้งานของฉันมีลักษณะดังนี้:
#!/usr/bin/env bash
set -e
if ! which realpath >&/dev/null; then
if ! which brew >&/dev/null; then
msg="ERROR: This script requires brew. See https://brew.sh for installation instructions."
echo "$(tput setaf 1)$msg$(tput sgr0)" >&2
exit 1
fi
echo "Installing coreutils/realpath"
brew install coreutils >&/dev/null
fi
thisDir=$( dirname "`realpath "$0"`" )
echo "This script is run from \"$thisDir\""
ข้อผิดพลาดนี้หากไม่ได้brew
ติดตั้ง แต่คุณสามารถติดตั้งได้เช่นกัน ฉันไม่รู้สึกสบายใจที่จะทำบางสิ่งที่ม้วนงอรหัสทับทิมโดยพลการจากเน็ตโดยอัตโนมัติ
หมายเหตุว่านี้รูปแบบอัตโนมัติต่อ Oleg Mikheev ของคำตอบ
การทดสอบที่ดีอย่างหนึ่งของโซลูชันเหล่านี้คือ:
ln -s
) ไปยังไฟล์นั้นโซลูชันนั้นยกเลิกการอ้างอิง symlink และให้ไดเร็กทอรีดั้งเดิมแก่คุณหรือไม่? ถ้าเป็นเช่นนั้นก็ใช้ได้
ดูเหมือนว่าจะใช้ได้กับ OSX ไม่ต้องใช้ไบนารีใด ๆ และถูกดึงออกมาจากที่นี่
function normpath() {
# Remove all /./ sequences.
local path=${1//\/.\//\/}
# Remove dir/.. sequences.
while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
path=${path/${BASH_REMATCH[0]}/}
done
echo $path
}
ฉันชอบสิ่งนี้:
#!/usr/bin/env bash
function realpath() {
local _X="$PWD"
local _LNK=$1
cd "$(dirname "$_LNK")"
if [ -h "$_LNK" ]; then
_LNK="$(readlink "$_LNK")"
cd "$(dirname "$_LNK")"
fi
echo "$PWD/$(basename "$_LNK")"
cd "$_X"
}
ผมจำเป็นต้องมีrealpath
การเปลี่ยนใน OS X หนึ่งที่ดำเนินการอย่างถูกต้องในเส้นทางที่มี symlinks และการอ้างอิงผู้ปกครองเช่นเดียวreadlink -f
หากว่า ซึ่งรวมถึงการแก้ไขลิงก์สัญลักษณ์ในเส้นทางก่อนที่จะแก้ไขการอ้างอิงระดับบนสุด เช่นหากคุณติดตั้งcoreutils
ขวดhomebrew แล้วให้เรียกใช้:
$ ln -s /var/log/cups /tmp/linkeddir # symlink to another directory
$ greadlink -f /tmp/linkeddir/.. # canonical path of the link parent
/private/var/log
โปรดทราบว่าreadlink -f
ได้แก้ไขแล้ว/tmp/linkeddir
ก่อนที่จะแก้ไขการ..
อ้างอิง dir หลัก แน่นอนว่าไม่มีreadlink -f
บน Mac เช่นกัน
ดังนั้นในฐานะที่เป็นส่วนหนึ่งของการใช้งาน bash สำหรับrealpath
ฉันนำสิ่งที่canonicalize_filename_mode(path, CAN_ALL_BUT_LAST)
เรียกใช้ฟังก์ชัน GNUlib มาใช้ใหม่ใน Bash 3.2; นี่คือการเรียกใช้ฟังก์ชันที่ GNU readlink -f
ทำให้:
# shellcheck shell=bash
set -euo pipefail
_contains() {
# return true if first argument is present in the other arguments
local elem value
value="$1"
shift
for elem in "$@"; do
if [[ $elem == "$value" ]]; then
return 0
fi
done
return 1
}
_canonicalize_filename_mode() {
# resolve any symlink targets, GNU readlink -f style
# where every path component except the last should exist and is
# resolved if it is a symlink. This is essentially a re-implementation
# of canonicalize_filename_mode(path, CAN_ALL_BUT_LAST).
# takes the path to canonicalize as first argument
local path result component seen
seen=()
path="$1"
result="/"
if [[ $path != /* ]]; then # add in current working dir if relative
result="$PWD"
fi
while [[ -n $path ]]; do
component="${path%%/*}"
case "$component" in
'') # empty because it started with /
path="${path:1}" ;;
.) # ./ current directory, do nothing
path="${path:1}" ;;
..) # ../ parent directory
if [[ $result != "/" ]]; then # not at the root?
result="${result%/*}" # then remove one element from the path
fi
path="${path:2}" ;;
*)
# add this component to the result, remove from path
if [[ $result != */ ]]; then
result="$result/"
fi
result="$result$component"
path="${path:${#component}}"
# element must exist, unless this is the final component
if [[ $path =~ [^/] && ! -e $result ]]; then
echo "$1: No such file or directory" >&2
return 1
fi
# if the result is a link, prefix it to the path, to continue resolving
if [[ -L $result ]]; then
if _contains "$result" "${seen[@]+"${seen[@]}"}"; then
# we've seen this link before, abort
echo "$1: Too many levels of symbolic links" >&2
return 1
fi
seen+=("$result")
path="$(readlink "$result")$path"
if [[ $path = /* ]]; then
# if the link is absolute, restart the result from /
result="/"
elif [[ $result != "/" ]]; then
# otherwise remove the basename of the link from the result
result="${result%/*}"
fi
elif [[ $path =~ [^/] && ! -d $result ]]; then
# otherwise all but the last element must be a dir
echo "$1: Not a directory" >&2
return 1
fi
;;
esac
done
echo "$result"
}
ซึ่งรวมถึงการตรวจจับ symlink แบบวงกลมออกหากเห็นเส้นทางเดียวกัน (ตัวกลาง) สองครั้ง
หากสิ่งที่คุณต้องการคือreadlink -f
คุณสามารถใช้สิ่งต่อไปนี้:
readlink() {
if [[ $1 != -f ]]; then # poor-man's option parsing
# delegate to the standard readlink command
command readlink "$@"
return
fi
local path result seenerr
shift
seenerr=
for path in "$@"; do
# by default readlink suppresses error messages
if ! result=$(_canonicalize_filename_mode "$path" 2>/dev/null); then
seenerr=1
continue
fi
echo "$result"
done
if [[ $seenerr ]]; then
return 1;
fi
}
สำหรับrealpath
ฉันยังต้องการ--relative-to
และ--relative-base
การสนับสนุนซึ่งให้เส้นทางสัมพัทธ์แก่คุณหลังจากการกำหนดมาตรฐาน:
_realpath() {
# GNU realpath replacement for bash 3.2 (OS X)
# accepts --relative-to= and --relative-base options
# and produces canonical (relative or absolute) paths for each
# argument on stdout, errors on stderr, and returns 0 on success
# and 1 if at least 1 path triggered an error.
local relative_to relative_base seenerr path
relative_to=
relative_base=
seenerr=
while [[ $# -gt 0 ]]; do
case $1 in
"--relative-to="*)
relative_to=$(_canonicalize_filename_mode "${1#*=}")
shift 1;;
"--relative-base="*)
relative_base=$(_canonicalize_filename_mode "${1#*=}")
shift 1;;
*)
break;;
esac
done
if [[
-n $relative_to
&& -n $relative_base
&& ${relative_to#${relative_base}/} == "$relative_to"
]]; then
# relative_to is not a subdir of relative_base -> ignore both
relative_to=
relative_base=
elif [[ -z $relative_to && -n $relative_base ]]; then
# if relative_to has not been set but relative_base has, then
# set relative_to from relative_base, simplifies logic later on
relative_to="$relative_base"
fi
for path in "$@"; do
if ! real=$(_canonicalize_filename_mode "$path"); then
seenerr=1
continue
fi
# make path relative if so required
if [[
-n $relative_to
&& ( # path must not be outside relative_base to be made relative
-z $relative_base || ${real#${relative_base}/} != "$real"
)
]]; then
local common_part parentrefs
common_part="$relative_to"
parentrefs=
while [[ ${real#${common_part}/} == "$real" ]]; do
common_part="$(dirname "$common_part")"
parentrefs="..${parentrefs:+/$parentrefs}"
done
if [[ $common_part != "/" ]]; then
real="${parentrefs:+${parentrefs}/}${real#${common_part}/}"
fi
fi
echo "$real"
done
if [[ $seenerr ]]; then
return 1
fi
}
if ! command -v realpath > /dev/null 2>&1; then
# realpath is not available on OSX unless you install the `coreutils` brew
realpath() { _realpath "$@"; }
fi
ฉันรวมการทดสอบหน่วยไว้ในคำขอตรวจสอบรหัสของฉันสำหรับรหัสนี้
จากการสื่อสารกับผู้แสดงความคิดเห็นฉันยอมรับว่ามันยากมากและไม่มีทางที่จะใช้งาน realpath ได้โดยสิ้นเชิงกับ Ubuntu
แต่รุ่นต่อไปนี้สามารถจัดการกับเคสเข้ามุมได้คำตอบที่ดีที่สุดไม่สามารถตอบสนองความต้องการประจำวันของฉันบน macbook ได้ ใส่รหัสนี้ลงใน ~ / .bashrc ของคุณและจำไว้ว่า:
# 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
}
echo
ไฟล์. ก็pwd
เหมือนกับการecho $(pwd)
ไม่ต้องวางไข่สำเนาที่สองของเชลล์ นอกจากนี้การไม่อ้างถึงอาร์กิวเมนต์echo
เป็นจุดบกพร่อง (คุณจะสูญเสียช่องว่างที่นำหน้าหรือต่อท้ายอักขระช่องว่างภายในใด ๆ ที่อยู่ติดกันและมีการขยายสัญลักษณ์แทนเป็นต้น) ดูเพิ่มเติมstackoverflow.com/questions/10067266/…
realpath
ไดเร็กทอรีที่ไม่มีอยู่
dir=$(dirname "$1"); file=$(basename "$1")
แทนที่จะใช้ไวยากรณ์ backtick ที่ล้าสมัย นอกจากนี้โปรดสังเกตการอ้างถึงข้อโต้แย้งที่ถูกต้องอีกครั้ง