Esiste un comando per recuperare il percorso assoluto dato il percorso relativo?
Ad esempio, voglio che $ line contenga il percorso assoluto di ciascun file in dir ./etc/
find ./ -type f | while read line; do
echo $line
done
Esiste un comando per recuperare il percorso assoluto dato il percorso relativo?
Ad esempio, voglio che $ line contenga il percorso assoluto di ciascun file in dir ./etc/
find ./ -type f | while read line; do
echo $line
done
Risposte:
uso:
find "$(pwd)"/ -type f
per ottenere tutti i file o
echo "$(pwd)/$line"
per visualizzare il percorso completo (se il percorso relativo è importante)
$(pwd)
al posto di $PWD
? (sì, lo so che pwd
è incorporato)
Prova realpath
.
~ $ sudo apt-get install realpath # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc
Per evitare di espandere i collegamenti simbolici, utilizzare realpath -s
.
La risposta arriva dal " comando bash / fish per stampare il percorso assoluto di un file ".
realpath
non sembra essere disponibile sul Mac (OS X 10.11 "El Capitan"). :-(
realpath
non sembra essere disponibile neanche su CentOS 6
brew install coreutils
porteràrealpath
realpath
è già presente. Non ho dovuto installarlo separatamente.
realpath
è disponibile su Git per Windows (almeno per me).
Se hai installato il pacchetto coreutils puoi generalmente usarlo readlink -f relative_file_name
per recuperare quello assoluto (con tutti i link simbolici risolti)
brew install coreutils
. Tuttavia l'eseguibile è anteposto da un ag:greadlink -f relative_file_name
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
UPD Alcune spiegazioni
"$1"
dirname "$1"
cd "$(dirname "$1")
in questa directory relativa e ne otteniamo il percorso assoluto eseguendo il pwd
comando shell$(basename "$1")
echo
sirealpath
o readlink -f
fare. Ad esempio, non funziona su percorsi in cui l'ultimo componente è un collegamento simbolico.
-P
un'opzione per pwd
comandare:echo "$(cd "$(dirname "$1")"; pwd -P)/$(basename "$1")"
Per quello che vale, ho votato per la risposta che è stata scelta, ma volevo condividere una soluzione. Il rovescio della medaglia è che è solo Linux - ho impiegato circa 5 minuti a cercare l'equivalente OSX prima di arrivare allo stack overflow. Sono sicuro che è là fuori però.
Su Linux puoi usare readlink -e
in tandem con dirname
.
$(dirname $(readlink -e ../../../../etc/passwd))
i rendimenti
/etc/
E poi usi dirname
la sorella, basename
solo per ottenere il nome del file
$(basename ../../../../../passwd)
i rendimenti
passwd
Metterli tutti insieme..
F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"
i rendimenti
/etc/passwd
Sei sicuro se stai prendendo di mira una directory, basename
non restituirà nulla e finirai con doppie barre nell'output finale.
dirname
, readlink
e basename
. Ciò mi ha aiutato a ottenere il percorso assoluto di un collegamento simbolico, non il suo obiettivo.
/home/GKFX
e scrivo touch newfile
, prima di premere Invio potresti capire che intendo "creare / home / GKFX / newfile", che è un percorso assoluto per un file che non esiste ancora.
Penso che questo sia il più portatile:
abspath() {
cd "$(dirname "$1")"
printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
cd "$OLDPWD"
}
Fallirà se il percorso non esiste però.
dirname
è un'utilità di base GNU, non comune a tutti gli unixen che credo.
dirname
è un'utilità POSIX standard, vedi qui: pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html
${1##*/}
da un giorno ormai, e ora che ho sostituito quel cestino con basename "$1"
esso sembra finalmente gestire correttamente i percorsi che finiscono in /.
../..
realpath
è probabilmente il migliore
Ma ...
Inizialmente la domanda iniziale era molto confusa, con un esempio scarsamente correlato alla domanda come affermato.
La risposta selezionata risponde effettivamente all'esempio fornito e per nulla alla domanda nel titolo. Il primo comando è quella risposta (è davvero? Ne dubito), e potrebbe fare altrettanto senza '/'. E non riesco a vedere cosa sta facendo il secondo comando.
Diverse questioni sono miste:
cambiando un nome di percorso relativo in uno assoluto, qualunque cosa denoti, forse nulla. ( In genere, se si emette un comando come touch foo/bar
, il percorso foo/bar
deve esistere per te ed eventualmente essere utilizzato nel calcolo, prima che il file venga effettivamente creato. )
potrebbero esserci diversi nomi di percorso assoluti che indicano lo stesso file (o potenziale file), in particolare a causa di collegamenti simbolici (collegamenti simbolici) sul percorso, ma probabilmente per altri motivi (un dispositivo può essere montato due volte in sola lettura). Si può o meno voler risolvere esplicitamente tali collegamenti simbolici.
arrivare alla fine di una catena di collegamenti simbolici a un file o nome non simbolico. Questo può o non può dare un nome di percorso assoluto, a seconda di come è fatto. E si può, o non si desidera, risolverlo in un percorso assoluto.
Il comando readlink foo
senza opzione fornisce una risposta solo se il suo argomento foo
è un collegamento simbolico e tale risposta è il valore di quel collegamento simbolico. Nessun altro collegamento è seguito. La risposta potrebbe essere un percorso relativo: qualunque fosse il valore dell'argomento symlink.
Tuttavia, readlink
ha opzioni (-f -e o -m) che funzioneranno per tutti i file e assegneranno un percorso assoluto (quello senza collegamenti simbolici) al file effettivamente indicato dall'argomento.
Questo funziona bene per tutto ciò che non è un collegamento simbolico, anche se si potrebbe desiderare di utilizzare un percorso assoluto senza risolvere i collegamenti simbolici intermedi sul percorso. Questo viene fatto dal comandorealpath -s foo
Nel caso di un argomento symlink, readlink
con le sue opzioni risolverà di nuovo tutti i symlink sul percorso assoluto dell'argomento, ma includerà anche tutti i link simbolici che possono essere incontrati seguendo il valore dell'argomento. Potresti non volerlo se desideri un percorso assoluto all'argomento symlink stesso, piuttosto che a qualunque cosa a cui possa collegarsi. Ancora una volta, se foo
è un collegamento simbolico, realpath -s foo
otterrà un percorso assoluto senza risolvere i collegamenti simbolici, incluso quello indicato come argomento.
Senza l' -s
opzione, realpath
fa praticamente lo stesso
readlink
, tranne per la semplice lettura del valore di un collegamento, così come molte altre cose. Non mi è chiaro perché readlink
abbia le sue opzioni, creando apparentemente una ridondanza indesiderata con
realpath
.
L'esplorazione del Web non dice molto di più, tranne per il fatto che potrebbero esserci alcune variazioni tra i sistemi.
Conclusione: realpath
è il comando migliore da usare, con la massima flessibilità, almeno per l'uso richiesto qui.
La mia soluzione preferita era quella di @EugenKonkov perché non implicava la presenza di altre utility (il pacchetto coreutils).
Ma non è riuscito per i relativi percorsi "." e "..", quindi ecco una versione leggermente migliorata che gestisce questi casi speciali.
cd
Tuttavia continua a fallire se l'utente non dispone dell'autorizzazione per accedere alla directory principale del percorso relativo.
#! /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
}
La risposta di Eugen non ha funzionato abbastanza per me, ma questo ha fatto:
absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"
Nota a margine, l'attuale directory di lavoro non è interessata.
La soluzione migliore, imho, è quella pubblicata qui: https://stackoverflow.com/a/3373298/9724628 .
Richiede Python per funzionare, ma sembra coprire tutti o la maggior parte dei casi limite ed essere una soluzione molto portatile.
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
Se si utilizza bash su Mac OS X, che nessuno dei due ha realpath esistita, né il suo readlink può stampare il percorso assoluto, potrebbe essere scelta, ma per il codice la propria versione per stamparlo. Ecco la mia implementazione:
(puro bash)
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[*]}"
)
}
(usa gawk)
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
}
(puro 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)
}
esempio:
$ abspath I/am/.//..//the/./god/../of///.././war
/Users/leon/I/the/war
Se il percorso relativo è un percorso di directory, quindi prova il mio, dovrebbe essere il migliore:
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") } }'
Un miglioramento alla versione piuttosto bella di @ ernest-a:
absolute_path() {
cd "$(dirname "$1")"
case $(basename $1) in
..) echo "$(dirname $(pwd))";;
.) echo "$(pwd)";;
*) echo "$(pwd)/$(basename $1)";;
esac
}
Questo si occupa correttamente del caso in cui si trova l'ultimo elemento del percorso ..
, nel qual caso la "$(pwd)/$(basename "$1")"
risposta di in @ ernest-a verrà visualizzata come accurate_sub_path/spurious_subdirectory/..
.
Se vuoi trasformare una variabile contenente un percorso relativo in un percorso assoluto, questo funziona:
dir=`cd "$dir"`
"cd" fa eco senza cambiare la directory di lavoro, perché eseguito qui in una sotto-shell.
dir=`cd ".."` && echo $dir
Questa è una soluzione concatenata di tutte le altre, ad esempio quando realpath
fallisce, o perché non è installata o perché esce con un codice di errore, quindi la soluzione successiva viene tentata fino a quando non trova il percorso giusto.
#!/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")" "${?}"
È possibile utilizzare la sostituzione della stringa bash per qualsiasi percorso relativo $ line:
line=$(echo ${line/#..\//`cd ..; pwd`\/})
line=$(echo ${line/#.\//`pwd`\/})
echo $line
La sostituzione di base della stringa iniziale segue la formula
$ {stringa / # sottostringa / sostituzione}
che è discussa bene qui: https://www.tldp.org/LDP/abs/html/string-manipulation.html
Il \
personaggio nega il /
momento in cui vogliamo che faccia parte della stringa che troviamo / sostituiamo.
Non sono riuscito a trovare una soluzione che fosse perfettamente portatile tra Mac OS Catalina, Ubuntu 16 e Centos 7, quindi ho deciso di farlo con Python in linea e ha funzionato bene per i miei script bash.
to_abs_path() {
python -c "import os; print os.path.abspath('$1')"
}
to_abs_path "/some_path/../secrets"