Ben dönüştürmek istediğiniz /foo/bar/..
için/foo
Bunu yapan bir bash komutu var mı?
Düzenleme: pratik durumumda, dizin var.
/foo/bar/..
için /foo
, ve bir kullanarak bash
komutu. Belirtilmeyen başka gereksinimler varsa, belki de olmalılar ...
Ben dönüştürmek istediğiniz /foo/bar/..
için/foo
Bunu yapan bir bash komutu var mı?
Düzenleme: pratik durumumda, dizin var.
/foo/bar/..
için /foo
, ve bir kullanarak bash
komutu. Belirtilmeyen başka gereksinimler varsa, belki de olmalılar ...
Yanıtlar:
bir dosya adının bir kısmını yoldan kesmek istiyorsanız, "dirname" ve "basename" arkadaşlarınızdır ve "realpath" de kullanışlıdır.
dirname /foo/bar/baz
# /foo/bar
basename /foo/bar/baz
# baz
dirname $( dirname /foo/bar/baz )
# /foo
realpath ../foo
# ../foo: No such file or directory
realpath /tmp/../tmp/../tmp
# /tmp
realpath
alternatifleri
Eğer realpath
sizin kabuk tarafından desteklenmemektedir, deneyebilirsin
readlink -f /path/here/..
Ayrıca
readlink -m /path/there/../../
İle aynı şekilde çalışır
realpath -s /path/here/../../
çünkü yolun normalleştirilmesi gerekmiyor.
realpath
ve readlink
GNU çekirdek yardımcı programlarından gelir, bu nedenle muhtemelen ikisini de alırsınız veya hiç almazsınız. Düzgün hatırlarsam, Mac'teki readlink sürümü GNU sürümünden biraz farklıdır: - \
Bunu yapmak için doğrudan bir bash komutu olup olmadığını bilmiyorum, ama genellikle
normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"
ve iyi çalışıyor.
rm -rf $normalDir
DirToNormalize yoksa bu çok tehlikeli olabilir ( )!
&&
@ DavidBlevins'in yorumuna göre kullanmak en iyisidir .
Deneyin realpath
. Aşağıda bütünüyle kaynak bulunmaktadır ve böylelikle kamu malı bağışlanmıştır.
// realpath.c: display the absolute path to a file or directory.
// Adam Liss, August, 2007
// This program is provided "as-is" to the public domain, without express or
// implied warranty, for any non-profit use, provided this notice is maintained.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <limits.h>
static char *s_pMyName;
void usage(void);
int main(int argc, char *argv[])
{
char
sPath[PATH_MAX];
s_pMyName = strdup(basename(argv[0]));
if (argc < 2)
usage();
printf("%s\n", realpath(argv[1], sPath));
return 0;
}
void usage(void)
{
fprintf(stderr, "usage: %s PATH\n", s_pMyName);
exit(1);
}
realpath
. Ayrıca benim favorim olur, ancak aracınızı kaynaktan çıkarmak yerine. Her şey ağır siklet ve çoğu zaman sadece istediğiniz readlink -f
... BTW, readlink
aynı zamanda bir bash yerleşik değil coreutils
, Ubuntu'nun bir parçası .
Taşınabilir ve güvenilir bir çözüm, hemen hemen her yere (Darwin dahil) önceden yüklenmiş olan python kullanmaktır. İki seçeneğiniz var:
abspath
mutlak bir yol döndürür ancak sembolik bağlantıları çözmez:
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
realpath
mutlak bir yol döndürür ve bunu yaparak sembolik bağlantıları çözer ve kurallı bir yol oluşturur:
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
Her durumda, path/to/file
göreceli veya mutlak bir yol olabilir.
Coreutils paketindeki readlink yardımcı programını kullanın.
MY_PATH=$(readlink -f "$0")
readlink
mutlak yolu elde etmek için temel standarttır. Ayrıca yollar veya bir yol yoksa boş dizeleri döndürme avantajına sahiptir (bunu yapmak için bayraklar göz önüne alındığında).
Var olan veya olmayan, ancak ebeveynleri olan bir dizinin mutlak yolunu almak için şunu kullanın:
abspath=$(readlink -f $path)
Tüm ebeveynlerle birlikte bulunması gereken bir dizinin mutlak yolunu bulmak için:
abspath=$(readlink -e $path)
Verilen yolu standartlaştırmak ve varsa bağlantıları takip etmek, ancak aksi halde eksik dizinleri yok saymak ve yolu yine de döndürmek için:
abspath=$(readlink -m $path)
Tek dezavantajı, readlink'in bağlantıları takip edeceğidir. Bağlantıları takip etmek istemiyorsanız, bu alternatif kuralı kullanabilirsiniz:
abspath=$(cd ${path%/*} && echo $PWD/${path##*/})
Bu, $ yolunun dizin kısmına yönlendirir ve geçerli dizini $ yolunun dosya kısmı ile birlikte yazdırır. Chdir'e başarısız olursa, boş bir dize ve stderr'de bir hata alırsınız.
readlink
varsa iyi bir seçenektir. OS X sürümü -e
veya -f
seçeneklerini desteklemez . İlk üç örnekte, $path
dosya adındaki boşlukları veya joker karakterleri işlemek için çift tırnak işareti kullanmalısınız. Parametre genişletme için +1, ancak bunun bir güvenlik açığı var. Eğer path
boştur, bu irade cd
ana dizininize. Çift tırnaklara ihtiyacınız var. abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
Eski soru, ancak kabuk düzeyinde tam yol adları ile uğraşıyorsanız çok daha basit bir yol var :
abspath = "$ (cd" $ yol "&& pwd)"
CD bir alt kabukta gerçekleştiğinden, ana komut dosyasını etkilemez.
Kabuk yerleşik komutlarınızın -L ve -P'yi kabul ettiğini varsayan iki varyasyon şunlardır:
abspath = "$ (cd -P" $ yol "&& pwd -P)" #çözünmüş sembollerle fiziksel yol abspath = "$ (cd -L" $ yol "&& pwd -L)" #lojik yol koruma simgeleri
Şahsen, bir nedenden dolayı sembolik bağlantılardan etkilenmedikçe nadiren bu yaklaşıma ihtiyacım var.
FYI: daha sonra geçerli dizini değiştirse bile çalışan bir komut dosyasının başlangıç dizinini elde etmedeki varyasyon.
name0 = "$ (taban adı" $ 0 ")"; #base komut dosyasının adı dir0 = "$ (cd" $ (dirname "$ 0") "&& pwd)"; #absolute başlangıç dizini
Komut dosyası, cd / pwd olmadan genellikle sadece ./script.sh gibi komutlar tarafından çalıştırılsa bile, her zaman mutlak dizine sahip olmanızı sağlar. Komut dosyası daha sonra bir cd yaparsa işe yaramaz.
Adam Liss'in belirttiği gibi realpath
, her dağıtımla birlikte verilmemiştir. Bu bir utanç, çünkü en iyi çözüm. Sağlanan kaynak kodu harika ve muhtemelen şimdi kullanmaya başlayacağım. Şimdiye kadar kullandığım şey, burada sadece tamlık için paylaşıyorum:
get_abs_path() {
local PARENT_DIR=$(dirname "$1")
cd "$PARENT_DIR"
local ABS_PATH="$(pwd)"/"$(basename "$1")"
cd - >/dev/null
echo "$ABS_PATH"
}
Sembolik bağlantıları çözmesini istiyorsanız, pwd
ile değiştirin pwd -P
.
pwd -P
Bu durum için bir seçenek var ... $(basename "$1")
Başka bir dizindeki bir dosyaya bir sembolik bağlantı olsaydı ne olacağını düşünün . pwd -P
Sadece basename kısmını yolunun dizin kısmında sembolik giderir, ama.
Son çözümüm şuydu:
pushd foo/bar/..
dir=`pwd`
popd
Tim Whitcomb'un cevabına dayanarak.
pushd $(dirname /usr/bin/java)
deneyebilirsiniz.
Tam olarak bir cevap değil, belki de bir takip sorusu (orijinal soru açık değildi):
readlink
aslında sembolik bağlantıları takip etmek istiyorsanız sorun yok. Ama sadece normalleştiren bir kullanım durumu da var ./
ve ../
ve //
tamamen sözdizimsel yapılabilir dizileri, olmadan sembolik standart hale. readlink
bunun için iyi değildir ve ikisi de değildir realpath
.
for f in $paths; do (cd $f; pwd); done
varolan yollar için çalışır, ancak diğerleri için keser.
Bir sed
(script iteratif dizilerin yerini olamayacağını hariç, iyi bir bahis olacak gibi görünüyor /foo/bar/baz/../..
-> /foo/bar/..
> - /foo
tüm sistemlerde üstlenme ya da bazı çirkin döngüsü kullanmak çıkışını karşılaştırmak güvenli değildir Perl, gibi bir şey kullanmadan) sed
için girdisi.
Java kullanan tek astarlı FWIW (JDK 6+):
jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths
realpath
Bir sahiptir -s
sembolik bağlantıları ve sadece çözmek başvuruları gidermezse seçeneği /./
, /../
ve ekstra kaldırmak /
karakterleri. -m
Seçenekle birleştirildiğinde , realpath
yalnızca dosya adında çalışır ve gerçek herhangi bir dosyaya dokunmaz. Bu sesler mükemmel bir çözüm gibi. Ancak ne yazık ki, realpath
birçok sistemde hala eksik.
..
Sembolik semboller söz konusu olduğunda bileşenleri kaldırmak sözdizimsel olarak yapılamaz. /one/two/../three
aynı şey değildir /one/three
eğer two
sembolik köprü ise /foo/bar
.
Partiye geç kaldım, ama böyle bir dizi parçayı okuduktan sonra yaptığım çözüm bu:
resolve_dir() {
(builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi)
}
Bu, 1 $ mutlak yolunu çözer, ~ ile iyi oynar, sembol bağlantılarını bulundukları yolda tutar ve dizin yığınınızla uğraşmaz. Tam yolu döndürür veya yoksa hiçbir şey döndürmez. 1 $ 'ın bir dizin olmasını bekler ve eğer değilse muhtemelen başarısız olur, ancak bu kendiniz yapmak için kolay bir kontrol.
Konuşkan ve biraz geç cevap. Daha eski RHEL4 / 5'e yapıştığım için bir tane yazmam gerekiyor. Mutlak ve göreli bağlantıları işlerim ve //, /./ ve somedir /../ girdilerini basitleştirir.
test -x /usr/bin/readlink || readlink () {
echo $(/bin/ls -l $1 | /bin/cut -d'>' -f 2)
}
test -x /usr/bin/realpath || realpath () {
local PATH=/bin:/usr/bin
local inputpath=$1
local changemade=1
while [ $changemade -ne 0 ]
do
changemade=0
local realpath=""
local token=
for token in ${inputpath//\// }
do
case $token in
""|".") # noop
;;
"..") # up one directory
changemade=1
realpath=$(dirname $realpath)
;;
*)
if [ -h $realpath/$token ]
then
changemade=1
target=`readlink $realpath/$token`
if [ "${target:0:1}" = '/' ]
then
realpath=$target
else
realpath="$realpath/$target"
fi
else
realpath="$realpath/$token"
fi
;;
esac
done
inputpath=$realpath
done
echo $realpath
}
mkdir -p /tmp/bar
(cd /tmp ; ln -s /tmp/bar foo; ln -s ../.././usr /tmp/bar/link2usr)
echo `realpath /tmp/foo`
GitHub'a ücretsiz ve ipoteksiz kullanım için yerleştirdiğimiz yeni Bash kütüphanesi ürün realpath-lib'imizi deneyin . Tamamen belgelenmiştir ve harika bir öğrenme aracıdır.
Yerel, göreli ve mutlak yolları çözer ve Bash 4+ dışında herhangi bir bağımlılığı yoktur; bu yüzden hemen hemen her yerde çalışmalıdır. Ücretsiz, temiz, basit ve öğretici.
Yapabilirsin:
get_realpath <absolute|relative|symlink|local file path>
Bu işlev kütüphanenin çekirdeğidir:
function get_realpath() {
if [[ -f "$1" ]]
then
# file *must* exist
if cd "$(echo "${1%/*}")" &>/dev/null
then
# file *may* not be local
# exception is ./file.ext
# try 'cd .; cd -;' *works!*
local tmppwd="$PWD"
cd - &>/dev/null
else
# file *must* be local
local tmppwd="$PWD"
fi
else
# file *cannot* exist
return 1 # failure
fi
# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success
}
Ayrıca get_dirname, get_filename, get_ stemname ve validate_path işlevlerini de içerir. Platformlar arasında deneyin ve geliştirilmesine yardımcı olun.
Sorun realpath
BSD'de (veya bu konuda OSX'te) bulunmamasıdır. Linux Journal'dan oldukça eski (2009) bir makaleden çıkarılan basit bir tarif :
function normpath() {
# Remove all /./ sequences.
local path=${1//\/.\//\/}
# Remove dir/.. sequences.
while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
path=${path/${BASH_REMATCH[0]}/}
done
echo $path
}
Bu varyant da gelmez dikkat değil var olma yolunu gerektirir.
@ Andre'nin cevabına dayanarak, birinin döngüsüz, tamamen dize manipülasyon tabanlı bir çözümden sonra olması durumunda biraz daha iyi bir sürüme sahip olabilirim. realpath
Veya kullanmanın dezavantajı olan herhangi bir sembolik bağlantıdan vazgeçmek istemeyenler için de yararlıdır readlink -f
.
3.2.25 ve üstü bash sürümlerinde çalışır.
shopt -s extglob
normalise_path() {
local path="$1"
# get rid of /../ example: /one/../two to /two
path="${path//\/*([!\/])\/\.\./}"
# get rid of /./ and //* example: /one/.///two to /one/two
path="${path//@(\/\.\/|\/+(\/))//}"
# remove the last '/.'
echo "${path%%/.}"
}
$ normalise_path /home/codemedic/../codemedic////.config
/home/codemedic/.config
hello/../world
(2) Dosya adındaki noktalar /hello/..world
(3) Çift eğik çizgi /hello//../world
(4) Çift eğik çizgi öncesi veya sonrası nokta /hello//./world
veya /hello/.//world
(5) Akım sonrası ebeveyn: /hello/./../world/
(6) Ebeveyn: Ebeveyn:, /hello/../../world
vb. sonra - Yolun değişmesi durana kadar düzeltmeler yapmak için bir döngü kullanılarak bunlardan bazıları düzeltilebilir. (Ayrıca kaldırın dir/../
, /dir/..
ancak dir/..
sonuna kadar kaldırın .)
Loveborg'un mükemmel python snippet'ine dayanarak şunu yazdım:
#!/bin/sh
# Version of readlink that follows links to the end; good for Mac OS X
for file in "$@"; do
while [ -h "$file" ]; do
l=`readlink $file`
case "$l" in
/*) file="$l";;
*) file=`dirname "$file"`/"$l"
esac
done
#echo $file
python -c "import os,sys; print os.path.abspath(sys.argv[1])" "$file"
done
FILEPATH="file.txt"
echo $(realpath $(dirname $FILEPATH))/$(basename $FILEPATH)
Bu, dosya olmasa bile çalışır. Dosyayı içeren dizinin var olmasını gerektirir.
realpath -e
Üçünü de yapacak bir çözüme ihtiyacım vardı:
realpath
ve readlink -f
eklentilerCevapların hiçbirinde # 1 ve # 2 yoktu. Başkalarını daha fazla yak tıraş etmek için # 3 ekledim.
#!/bin/bash
P="${1?Specify a file path}"
[ -e "$P" ] || { echo "File does not exist: $P"; exit 1; }
while [ -h "$P" ] ; do
ls="$(ls -ld "$P")"
link="$(expr "$ls" : '.*-> \(.*\)$')"
expr "$link" : '/.*' > /dev/null &&
P="$link" ||
P="$(dirname "$P")/$link"
done
echo "$(cd "$(dirname "$P")"; pwd)/$(basename "$P")"
İşte alıntıyı tam olarak uygulamak için yollarda bazı bükülmüş boşluklar içeren kısa bir test örneği
mkdir -p "/tmp/test/ first path "
mkdir -p "/tmp/test/ second path "
echo "hello" > "/tmp/test/ first path / red .txt "
ln -s "/tmp/test/ first path / red .txt " "/tmp/test/ second path / green .txt "
cd "/tmp/test/ second path "
fullpath " green .txt "
cat " green .txt "
Bunun eski bir soru olduğunu biliyorum. Hala bir alternatif öneriyorum. Son zamanlarda aynı sorunla karşılaştım ve bunu yapmak için mevcut ve taşınabilir bir komut bulamadım. Bu yüzden hile yapabilen bir işlev içeren aşağıdaki kabuk komut dosyasını yazdım.
#! /bin/sh
function normalize {
local rc=0
local ret
if [ $# -gt 0 ] ; then
# invalid
if [ "x`echo $1 | grep -E '^/\.\.'`" != "x" ] ; then
echo $1
return -1
fi
# convert to absolute path
if [ "x`echo $1 | grep -E '^\/'`" == "x" ] ; then
normalize "`pwd`/$1"
return $?
fi
ret=`echo $1 | sed 's;/\.\($\|/\);/;g' | sed 's;/[^/]*[^/.]\+[^/]*/\.\.\($\|/\);/;g'`
else
read line
normalize "$line"
return $?
fi
if [ "x`echo $ret | grep -E '/\.\.?(/|$)'`" != "x" ] ; then
ret=`normalize "$ret"`
rc=$?
fi
echo "$ret"
return $rc
}
https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c
Mümkün olan en yüksek performansa (eğlence için) odaklanarak bunu ele almak için yalnızca yerleşik bir işlev yaptım. Sembolik bağlantıları çözmez, bu nedenle temel olarak aynıdır realpath -sm
.
## A bash-only mimic of `realpath -sm`.
## Give it path[s] as argument[s] and it will convert them to clean absolute paths
abspath () {
${*+false} && { >&2 echo $FUNCNAME: missing operand; return 1; };
local c s p IFS='/'; ## path chunk, absolute path, input path, IFS for splitting paths into chunks
local -i r=0; ## return value
for p in "$@"; do
case "$p" in ## Check for leading backslashes, identify relative/absolute path
'') ((r|=1)); continue;;
//[!/]*) >&2 echo "paths =~ ^//[^/]* are impl-defined; not my problem"; ((r|=2)); continue;;
/*) ;;
*) p="$PWD/$p";; ## Prepend the current directory to form an absolute path
esac
s='';
for c in $p; do ## Let IFS split the path at '/'s
case $c in ### NOTE: IFS is '/'; so no quotes needed here
''|.) ;; ## Skip duplicate '/'s and '/./'s
..) s="${s%/*}";; ## Trim the previous addition to the absolute path string
*) s+=/$c;; ### NOTE: No quotes here intentionally. They make no difference, it seems
esac;
done;
echo "${s:-/}"; ## If xpg_echo is set, use `echo -E` or `printf $'%s\n'` instead
done
return $r;
}
Not: Bir yolun başlangıcında //
tam olarak iki çift eğik çizgi uygulama tanımlı davranış olduğundan, bu işlev ile başlayan yolları işlemez . Ancak /
,///
ve böylece gayet iyi.
Bu işlev tüm uç durumları düzgün bir şekilde ele alıyor gibi görünüyor, ancak hala ilgilenmediğim bazı şeyler olabilir.
Performans Notu: binlerce argümanla çağrıldığında, abspath
yaklaşık 10 kat daha yavaş çalışır realpath -sm
; tek bir argümanla çağrıldığında , çoğunlukla her seferinde yeni bir program yürütmesi gerekmediği için abspath
makinemden> 110 kat daha hızlı çalışır realpath -sm
.
Bugün stat
yolları çözmek için komutu kullanabileceğinizi keşfettim .
"~ / Documents" gibi bir dizin için:
Bunu çalıştırabilirsiniz:
stat -f %N ~/Documents
Tam yolu elde etmek için:
/Users/me/Documents
Sembolik bağlar için% Y biçimi seçeneğini kullanabilirsiniz:
stat -f %Y example_symlink
Hangi gibi bir sonuç döndürebilir:
/usr/local/sbin/example_symlink
Biçimlendirme seçenekleri * NIX'in diğer sürümlerinde farklı olabilir, ancak bunlar benim için OSX'te çalıştı.
stat -f %N ~/Documents
çizgi Kabuk değiştiriyor ... kırmızı ringa ~/Documents
ile /Users/me/Documents
ve stat
sadece kelimesi kelimesine onun argüman yazdırıyor.
/foo/bar
veya hatta/foo
var olup olmadığı önemli mi yoksa yalnızca yol adı kurallarına göre dize düzenleme yönüyle ilgileniyor musunuz?