มีคำสั่งให้ดึงเส้นทางสัมบูรณ์ที่กำหนดเส้นทางสัมพัทธ์หรือไม่?
ตัวอย่างเช่นฉันต้องการ $ line มีเส้นทางที่แน่นอนของแต่ละไฟล์ใน dir ./etc/
find ./ -type f | while read line; do
echo $line
done
มีคำสั่งให้ดึงเส้นทางสัมบูรณ์ที่กำหนดเส้นทางสัมพัทธ์หรือไม่?
ตัวอย่างเช่นฉันต้องการ $ line มีเส้นทางที่แน่นอนของแต่ละไฟล์ใน dir ./etc/
find ./ -type f | while read line; do
echo $line
done
คำตอบ:
ใช้:
find "$(pwd)"/ -type f
เพื่อรับไฟล์ทั้งหมดหรือ
echo "$(pwd)/$line"
เพื่อแสดงเส้นทางแบบเต็ม (ถ้าเส้นทางสัมพันธ์สัมพันธ์กับ)
$(pwd)
ในสถานที่ของ$PWD
? (ใช่ฉันรู้ว่าpwd
เป็นตัว)
ลองrealpath
ดู
~ $ sudo apt-get install realpath # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc
เพื่อหลีกเลี่ยงการขยาย symlinks realpath -s
ใช้
คำตอบมาจาก " คำสั่ง bash / fish เพื่อพิมพ์พา ธ สัมบูรณ์ไปยังไฟล์ "
realpath
ดูเหมือนจะไม่มีใน Mac (OS X 10.11 "El Capitan") :-(
realpath
ดูเหมือนจะไม่สามารถใช้งานบน CentOS 6 ได้เช่นกัน
brew install coreutils
จะนำเข้าrealpath
realpath
มีอยู่แล้ว ฉันไม่ต้องติดตั้งแยกต่างหาก
realpath
มีใน Git สำหรับ Windows (สำหรับฉันอย่างน้อย)
หากคุณมีการติดตั้งแพ็คเกจ coreutils โดยทั่วไปคุณสามารถใช้readlink -f relative_file_name
เพื่อเรียกคืนหนึ่งแน่นอน (ด้วย symlink ทั้งหมดได้รับการแก้ไข)
brew install coreutils
homebrew อย่างไรก็ตามไฟล์ปฏิบัติการนั้นได้รับการเสริมโดย ag:greadlink -f relative_file_name
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
UPDคำอธิบายบางอย่าง
"$1"
dirname "$1"
cd "$(dirname "$1")
เข้าสู่ dir สัมพัทธ์นี้และรับเส้นทางสัมบูรณ์โดยเรียกใช้pwd
คำสั่งเชลล์$(basename "$1")
echo
มันrealpath
หรือ readlink -f
ตัวอย่างเช่นมันไม่ทำงานบนเส้นทางที่องค์ประกอบสุดท้ายคือ symlink
-P
ตัวเลือกในการpwd
บังคับบัญชา:echo "$(cd "$(dirname "$1")"; pwd -P)/$(basename "$1")"
สำหรับสิ่งที่คุ้มค่าฉันลงคะแนนให้กับคำตอบที่เลือก แต่ต้องการแบ่งปันวิธีแก้ปัญหา ข้อเสียคือเป็นลินุกซ์เท่านั้น - ฉันใช้เวลาประมาณ 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)"
อัตราผลตอบแทน
/etc/passwd
คุณปลอดภัยหากคุณกำหนดเป้าหมายไดเรกทอรีbasename
จะไม่คืนสิ่งใดเลยและคุณก็จะได้เครื่องหมายทับสองครั้งในผลลัพธ์สุดท้าย
dirname
, และreadlink
basename
นั่นช่วยให้ฉันได้เส้นทางที่สมบูรณ์ของลิงก์สัญลักษณ์ไม่ใช่เป้าหมาย
/home/GKFX
และฉันพิมพ์touch newfile
แล้วก่อนที่ฉันจะกด Enter คุณสามารถทำงานได้ว่าฉันหมายถึง "create / home / GKFX / newfile" ซึ่งเป็นเส้นทางที่แน่นอนไปยังไฟล์ที่ยังไม่มีอยู่
ฉันคิดว่านี่เป็นอุปกรณ์พกพาที่ดีที่สุด:
abspath() {
cd "$(dirname "$1")"
printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
cd "$OLDPWD"
}
มันจะล้มเหลวหากเส้นทางไม่มีอยู่
dirname
เป็น GNU core ยูทิลิตี้ไม่ใช่เรื่องธรรมดาสำหรับยูนิกซ์ทั้งหมดที่ฉันเชื่อ
dirname
เป็นยูทิลิตี้มาตรฐาน POSIX ดูที่นี่: pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html
${1##*/}
มาหนึ่งวันแล้วและตอนนี้ฉันแทนที่ถังขยะด้วยbasename "$1"
ดูเหมือนว่าในที่สุดจะจัดการเส้นทางที่ถูกต้องใน /
../..
realpath
น่าจะดีที่สุด
แต่ ...
คำถามเริ่มต้นสับสนมากเมื่อเริ่มต้นด้วยตัวอย่างที่เกี่ยวข้องกับคำถามตามที่ระบุไว้ไม่ดี
คำตอบที่เลือกจริงตอบตัวอย่างที่กำหนดและไม่ใช่คำถามในชื่อ คำสั่งแรกคือคำตอบนั้น (จริงหรือไม่ฉันสงสัย) และสามารถทำได้โดยไม่ต้องใช้ '/' และฉันไม่เห็นว่าคำสั่งที่สองกำลังทำอะไรอยู่
มีหลายประเด็นที่หลากหลาย:
การเปลี่ยนชื่อพา ธ ที่สัมพันธ์กันให้เป็นชื่อที่แน่นอนไม่ว่ามันจะเป็นอะไร ( โดยทั่วไปหากคุณออกคำสั่งเช่นtouch foo/bar
ชื่อพา ธfoo/bar
ต้องมีอยู่สำหรับคุณและอาจใช้ในการคำนวณก่อนที่ไฟล์จะถูกสร้างขึ้นจริง )
อาจมีชื่อพา ธ สัมบูรณ์หลายอันที่แสดงถึงไฟล์เดียวกัน (หรือไฟล์ที่อาจเป็นไปได้) โดยเฉพาะอย่างยิ่งเนื่องจากลิงก์สัญลักษณ์ (symlinks) บนพา ธ แต่อาจเป็นเพราะเหตุผลอื่น (อุปกรณ์อาจถูกเมาท์สองครั้งเป็นแบบอ่านอย่างเดียว) หนึ่งอาจหรืออาจไม่ต้องการแก้ไขข้อบกพร่องดังกล่าว symlink
ไปที่จุดสิ้นสุดของเชนของลิงก์สัญลักษณ์ไปยังไฟล์หรือชื่อที่ไม่ใช่ symlink สิ่งนี้อาจหรือไม่อาจให้ชื่อพา ธ สัมบูรณ์ขึ้นอยู่กับวิธีการทำ และหนึ่งอาจหรืออาจไม่ต้องการแก้ไขเป็นชื่อพา ธ สัมบูรณ์
คำสั่งที่readlink foo
ไม่มีตัวเลือกจะให้คำตอบก็ต่อเมื่ออาร์กิวเมนต์foo
นั้นเป็นลิงก์สัญลักษณ์และคำตอบนั้นคือค่าของ symlink นั้น ไม่มีลิงก์อื่นติดตาม คำตอบอาจเป็นเส้นทางสัมพัทธ์: สิ่งที่เป็นค่าของอาร์กิวเมนต์ symlink
อย่างไรก็ตามreadlink
มีตัวเลือก (-f -e หรือ -m) ที่จะใช้งานได้กับไฟล์ทั้งหมดและให้ชื่อพา ธ สัมบูรณ์หนึ่งรายการ (ชื่อที่ไม่มี symlink) ให้กับไฟล์ซึ่งแสดงโดยอาร์กิวเมนต์
วิธีนี้ใช้ได้ผลดีกับทุกอย่างที่ไม่ใช่ symlink แต่อาจต้องการใช้ชื่อพา ธ สัมบูรณ์โดยไม่ต้องแก้ไข symlink ขั้นกลางบนพา ธ สิ่งนี้ทำโดยคำสั่งrealpath -s foo
ในกรณีของอาร์กิวเมนต์ symlink readlink
ด้วยตัวเลือกจะแก้ไขsymlink ทั้งหมดบนพา ธ สัมบูรณ์ไปยังอาร์กิวเมนต์อีกครั้ง แต่จะรวมsymlink ทั้งหมดที่อาจพบโดยทำตามค่าอาร์กิวเมนต์ คุณอาจไม่ต้องการที่ถ้าคุณต้องการเส้นทางที่แน่นอนเพื่อโต้แย้ง symlink ตัวเองมากกว่าสิ่งที่มันอาจเชื่อมโยงไป อีกครั้งถ้าfoo
เป็น symlinkrealpath -s foo
จะได้รับเส้นทางที่แน่นอนโดยไม่ต้องแก้ไขรวมถึงการกำหนดเป็นอาร์กิวเมนต์
หากไม่มี-s
ตัวเลือกrealpath
ก็จะทำเช่นเดียวกัน
readlink
ยกเว้นเพียงอ่านค่าของลิงก์และสิ่งอื่น ๆ อีกมากมาย มันไม่ชัดเจนสำหรับฉันว่าทำไมreadlink
มีตัวเลือกของมันสร้างความซ้ำซ้อนที่ไม่พึงประสงค์ด้วย
realpath
มีตัวเลือกของการสร้างเห็นได้ชัดซ้ำซ้อนที่ไม่พึงประสงค์ด้วย
การสำรวจเว็บไม่ได้บอกอะไรมากไปกว่านั้นยกเว้นว่าอาจมีการเปลี่ยนแปลงในบางระบบ
สรุป: realpath
เป็นคำสั่งที่ดีที่สุดที่จะใช้อย่างยืดหยุ่นที่สุดอย่างน้อยสำหรับการใช้ที่ร้องขอที่นี่
ทางออกที่ฉันชอบคือหนึ่งโดย @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
}
คำตอบของ Eugen ไม่ได้ผลสำหรับฉัน แต่สิ่งนี้ทำได้:
absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"
หมายเหตุข้างไดเรกทอรีทำงานปัจจุบันของคุณไม่ได้รับผลกระทบ
ที่ดีที่สุดของการแก้ปัญหา IMHO เป็นหนึ่งที่โพสต์ที่นี่: https://stackoverflow.com/a/3373298/9724628
มันต้องใช้ไพ ธ อนในการทำงาน แต่ดูเหมือนว่าจะครอบคลุมเคสทั้งหมดหรือส่วนใหญ่และเป็นโซลูชันแบบพกพา
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
ในกรณีของfind
อาจเป็นวิธีที่ง่ายที่สุดที่จะให้เส้นทางที่แน่นอนสำหรับการค้นหาเช่น:
find /etc
find `pwd`/subdir_of_current_dir/ -type f
หากคุณกำลังใช้ bash บน Mac OS X ซึ่งไม่มีrealpathอยู่และreadlinkไม่สามารถพิมพ์พา ธ สัมบูรณ์คุณอาจมีทางเลือก แต่ให้เขียนโค้ดเวอร์ชันของคุณเองเพื่อพิมพ์ นี่คือการดำเนินการของฉัน:
(ทุบตีบริสุทธิ์)
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[*]}"
)
}
(ใช้เพ่งพิศ)
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
}
(บริสุทธิ์ bsd 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
สิ่งที่พวกเขาพูดยกเว้นfind $PWD
หรือ (ในทุบตี) find ~+
สะดวกกว่าเล็กน้อย
คล้ายกับคำตอบของ @ ernest-a แต่ไม่มีผลกระทบ$OLDPWD
หรือกำหนดฟังก์ชั่นใหม่ที่คุณสามารถยิง subshell(cd <path>; pwd)
$ pwd
/etc/apache2
$ cd ../cups
$ cd -
/etc/apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups
หากเส้นทางสัมพัทธ์เป็นเส้นทางไดเรกทอรีให้ลองของฉันควรเป็นเส้นทางที่ดีที่สุด:
absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)
echo $absPath
echo "mydir/doc/ mydir/usoe ./mydir/usm" | awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'
การปรับปรุงเป็นเวอร์ชันที่ดีของ @ ernest-a:
absolute_path() {
cd "$(dirname "$1")"
case $(basename $1) in
..) echo "$(dirname $(pwd))";;
.) echo "$(pwd)";;
*) echo "$(pwd)/$(basename $1)";;
esac
}
ข้อเสนอนี้อย่างถูกต้องกับกรณีที่องค์ประกอบสุดท้ายของเส้นทางคือ..
ในกรณีที่"$(pwd)/$(basename "$1")"
ในคำตอบ @ เออร์เนส-a accurate_sub_path/spurious_subdirectory/..
จะผ่านมาเป็น
หากคุณต้องการแปลงตัวแปรที่มีเส้นทางสัมพัทธ์เป็นสัมพัทธ์สัมบูรณ์งานนี้:
dir=`cd "$dir"`
"cd" echoes โดยไม่เปลี่ยนไดเรกทอรีการทำงานเพราะทำงานที่นี่ใน sub-shell
dir=`cd ".."` && echo $dir
นี่เป็นโซลูชันที่ถูกโยงโซ่จากผู้อื่นทั้งหมดตัวอย่างเช่นเมื่อrealpath
ล้มเหลวเนื่องจากไม่ได้ถูกติดตั้งหรือออกจากด้วยรหัสข้อผิดพลาดดังนั้นจึงพยายามแก้ไขปัญหาถัดไปจนกว่าจะได้รับเส้นทางที่ถูกต้อง
#!/bin/bash
function getabsolutepath() {
local target;
local changedir;
local basedir;
local firstattempt;
target="${1}";
if [ "$target" == "." ];
then
printf "%s" "$(pwd)";
elif [ "$target" == ".." ];
then
printf "%s" "$(dirname "$(pwd)")";
else
changedir="$(dirname "${target}")" && basedir="$(basename "${target}")" && firstattempt="$(cd "${changedir}" && pwd)" && printf "%s/%s" "${firstattempt}" "${basedir}" && return 0;
firstattempt="$(readlink -f "${target}")" && printf "%s" "${firstattempt}" && return 0;
firstattempt="$(realpath "${target}")" && printf "%s" "${firstattempt}" && return 0;
# If everything fails... TRHOW PYTHON ON IT!!!
local fullpath;
local pythoninterpreter;
local pythonexecutables;
local pythonlocations;
pythoninterpreter="python";
declare -a pythonlocations=("/usr/bin" "/bin");
declare -a pythonexecutables=("python" "python2" "python3");
for path in "${pythonlocations[@]}";
do
for executable in "${pythonexecutables[@]}";
do
fullpath="${path}/${executable}";
if [[ -f "${fullpath}" ]];
then
# printf "Found ${fullpath}\\n";
pythoninterpreter="${fullpath}";
break;
fi;
done;
if [[ "${pythoninterpreter}" != "python" ]];
then
# printf "Breaking... ${pythoninterpreter}\\n"
break;
fi;
done;
firstattempt="$(${pythoninterpreter} -c "import os, sys; print( os.path.abspath( sys.argv[1] ) );" "${target}")" && printf "%s" "${firstattempt}" && return 0;
# printf "Error: Could not determine the absolute path!\\n";
return 1;
fi
}
printf "\\nResults:\\n%s\\nExit: %s\\n" "$(getabsolutepath "./asdfasdf/ asdfasdf")" "${?}"
คุณสามารถใช้การแทนที่สตริง bash สำหรับเส้นทาง $ line ใด ๆ ที่เกี่ยวข้อง:
line=$(echo ${line/#..\//`cd ..; pwd`\/})
line=$(echo ${line/#.\//`pwd`\/})
echo $line
การแทนที่ front-of-string พื้นฐานตามด้วยสูตร
$ {string / # substring / replace}
ซึ่งมีการกล่าวถึงที่นี่: https://www.tldp.org/LDP/abs/html/string-manipulation.html
\
ตัวละครขัดแย้ง/
เมื่อเราต้องการให้เป็นส่วนหนึ่งของสตริงที่เราพบ / แทนที่
ฉันไม่สามารถหาวิธีแก้ปัญหาที่พกพาได้อย่างเรียบร้อยระหว่าง Mac OS Catalina, Ubuntu 16 และ Centos 7 ดังนั้นฉันจึงตัดสินใจทำด้วย python inline และทำงานได้ดีสำหรับสคริปต์ทุบตีของฉัน
to_abs_path() {
python -c "import os; print os.path.abspath('$1')"
}
to_abs_path "/some_path/../secrets"