¿Cómo normalizar una ruta de archivo en Bash?
Quiero transformar /foo/bar/..
a /foo
¿Hay un comando bash que hace esto?
Editar: en mi caso práctico, el directorio existe.
21 answers
Si quieres chomp parte de un nombre de archivo de la ruta, "dirname" y "basename" son tus amigos, y "realpath" también es útil.
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
alternativas
Si realpath
no es compatible con su shell, puede probar
readlink -f /path/here/..
También
readlink -m /path/there/../../
Funciona igual que
realpath -s /path/here/../../
En que la ruta no necesita existir para ser normalizada.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-07 21:38:49
No se si hay una orden directa de bash para hacer esto, pero usualmente lo hago
normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"
Y funciona bien.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-18 04:33:04
Intenta realpath
. A continuación se muestra la fuente en su totalidad, donada al dominio público.
// 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);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-07 13:58:49
Utilice la utilidad readlink del paquete coreutils.
MY_PATH=$(readlink -f "$0")
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-26 08:32:31
Una solución portátil y confiable es usar python, que está preinstalado en casi todas partes (incluido Darwin). Tienes dos opciones:
-
abspath
devuelve una ruta absoluta pero no resuelve enlaces simbólicos:python -c "import os,sys; print os.path.abspath(sys.argv[1])" path/to/file
-
realpath
devuelve una ruta absoluta y al hacerlo resuelve enlaces simbólicos, generando una ruta canónica:python -c "import os,sys; print os.path.realpath(sys.argv[1])" path/to/file
En cada caso, path/to/file
puede ser una ruta relativa o absoluta.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-07 18:37:03
readlink
es el estándar bash para obtener la ruta absoluta. También tiene la ventaja de devolver cadenas vacías si las rutas o una ruta no existen (dadas las banderas para hacerlo).
Para obtener la ruta absoluta a un directorio que puede o no existir, pero cuyos padres existen, use:
abspath=$(readlink -f $path)
Para obtener la ruta absoluta a un directorio que debe existir junto con todos los padres:
abspath=$(readlink -e $path)
Para canonizar la ruta dada y seguir enlaces simbólicos si existen, pero de lo contrario ignore los directorios que faltan y simplemente devuelva la ruta de todos modos, es:
abspath=$(readlink -m $path)
El único inconveniente es que readlink seguirá los enlaces. Si no desea seguir los enlaces, puede utilizar esta convención alternativa:
abspath=$(cd ${path%/*} && echo $PWD/${path##*/})
Que chdir a la parte del directorio de path path e imprimir el directorio actual junto con la parte del archivo de path path. Si falla en chdir, se obtiene una cadena vacía y un error en stderr.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-02 22:06:58
Mi solución reciente fue:
pushd foo/bar/..
dir=`pwd`
popd
Basado en la respuesta de Tim Whitcomb.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-10 19:53:55
Antigua pregunta, pero hay una forma mucho más sencilla si se trata de nombres de ruta completos a nivel de shell:
abspath="$( cd "$path" && pwd )"
Como el cd sucede en una subcapa, no afecta al script principal.
Dos variaciones, suponiendo que los comandos integrados en el shell acepten-L y-P, son:
abspath="$( cd -P "$path" && pwd -P )" #physical path with resolved symlinks abspath="$( cd -L "$path" && pwd -L )" #logical path preserving symlinks
Personalmente, rara vez necesito este enfoque posterior a menos que esté fascinado con los enlaces simbólicos por alguna razón.
FYI: variación en la obtención del directorio de inicio de un script que funciona incluso si el script cambia su directorio actual más adelante.
name0="$(basename "$0")"; #base name of script dir0="$( cd "$( dirname "$0" )" && pwd )"; #absolute starting dir
El uso de CD asegura que siempre tenga el directorio absoluto, incluso si el script es ejecutado por comandos como ./script.sh que, sin el cd/pwd, a menudo da solo .. Inútil si el script hace un cd más tarde.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-07 18:00:05
Como Adam Liss señaló realpath
no se incluye en todas las distribuciones. Lo cual es una pena, porque es la mejor solución. El código fuente proporcionado es genial, y probablemente comenzaré a usarlo ahora. Esto es lo que he estado usando hasta ahora, que comparto aquí solo para completar:
get_abs_path() {
local PARENT_DIR=$(dirname "$1")
cd "$PARENT_DIR"
local ABS_PATH="$(pwd)"/"$(basename "$1")"
cd - >/dev/null
echo "$ABS_PATH"
}
Si desea que resuelva los enlaces simbólicos, simplemente reemplace pwd
por pwd -P
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-11 07:56:16
Hablador, y un poco tarde respuesta. Necesito escribir uno ya que estoy atascado en RHEL4/5 más viejo. Maneja enlaces absolutos y relativos, y simplifica //,/./ y somedir/../ entrada.
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`
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-10 14:33:47
No es exactamente una respuesta, pero tal vez una pregunta de seguimiento (la pregunta original no era explícita):
readlink
está bien si realmente quieres seguir enlaces simbólicos. Pero también hay un caso de uso para simplemente normalizar ./
y ../
y //
secuencias, que se pueden hacer puramente sintácticamente, sin canonizar enlaces simbólicos. readlink
no es bueno para esto, y tampoco lo es realpath
.
for f in $paths; do (cd $f; pwd); done
Funciona para rutas existentes, pero se rompe para otras.
Un script sed
sería parece ser una buena apuesta, excepto que no puedes reemplazar secuencias iterativamente(/foo/bar/baz/../..
-> /foo/bar/..
-> /foo
) sin usar algo como Perl, que no es seguro asumir en todos los sistemas, o usar algún bucle feo para comparar la salida de sed
con su entrada.
FWIW, un one-liner usando Java (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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-29 23:16:49
Llego tarde a la fiesta, pero esta es la solución que he elaborado después de leer un montón de hilos como este:
resolve_dir() {
(builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi)
}
Esto resolverá la ruta absoluta de $1, jugará bien con ~, mantendrá enlaces simbólicos en la ruta donde están, y no se meterá con su pila de directorios. Devuelve la ruta completa o nada si no existe. Espera que $1 sea un directorio y probablemente fallará si no lo es, pero esa es una comprobación fácil de hacer usted mismo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-24 16:49:31
Pruebe nuestro nuevo producto de la biblioteca Bash realpath-lib que hemos colocado en GitHub para un uso gratuito y sin trabas. Está completamente documentado y es una gran herramienta de aprendizaje.
Resuelve rutas locales, relativas y absolutas y no tiene ninguna dependencia excepto Bash 4+; por lo que debería funcionar casi en cualquier lugar. Es gratis, limpio, simple e instructivo.
Puedes hacer:
get_realpath <absolute|relative|symlink|local file path>
Esta función es el núcleo de la biblioteca:
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
}
También contiene funciones para get_dirname, get_filename, get_ stemname y validate_path. Pruébalo en todas las plataformas y ayuda a mejorarlo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-03 03:56:54
Basado en la respuesta de @Andre, podría tener una versión ligeramente mejor, en caso de que alguien esté buscando una solución completamente basada en manipulación de cadenas y sin bucles. También es útil para aquellos que no quieren desreferenciar ningún enlace simbólico, que es la desventaja de usar realpath
o readlink -f
.
Funciona en bash versiones 3.2.25 y superiores.
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-12 01:44:25
El problema con realpath
es que no está disponible en BSD (o OSX para el caso). Aquí hay una receta simple extraída de un artículo bastante antiguo (2009) de Linux Journal , que es bastante portátil:
function normpath() {
# Remove all /./ sequences.
local path=${1//\/.\//\/}
# Remove dir/.. sequences.
while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
path=${path/${BASH_REMATCH[0]}/}
done
echo $path
}
Observe que esta variante también hace no requiere que la ruta exista.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-25 13:10:23
Basado en el excelente fragmento de python de loveborg, escribí esto:
#!/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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-24 21:17:39
FILEPATH="file.txt"
echo $(realpath $(dirname $FILEPATH))/$(basename $FILEPATH)
Esto funciona incluso si el archivo no existe. Requiere que exista el directorio que contiene el archivo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-14 19:04:24
Necesitaba una solución que hiciera las tres cosas:
- Trabaja en un Mac de stock.
realpath
yreadlink -f
son complementos - Resolver enlaces simbólicos
- Tienen manejo de errores
Ninguna de las respuestas tenía tanto #1 como #2. Agregué el # 3 para salvar a otros más afeitándose yak.
#!/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")"
Aquí hay un breve caso de prueba con algunos espacios retorcidos en las rutas para ejercer plenamente la cita
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 "
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-16 01:53:30
Sé que esta es una pregunta antigua. Todavía estoy ofreciendo una alternativa. Recientemente me encontré con el mismo problema y no encontré ningún comando existente y portátil para hacer eso. Así que escribí el siguiente script de shell que incluye una función que puede hacer el truco.
#! /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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-18 10:03:23
Hoy descubrí que puedes usar el comando stat
para resolver rutas.
Así que para un directorio como "~/Documents":
Puedes ejecutar esto:
stat -f %N ~/Documents
Para obtener el camino completo:
/Users/me/Documents
Para los enlaces simbólicos, puede usar la opción de formato %Y:
stat -f %Y example_symlink
Que podría devolver un resultado como:
/usr/local/sbin/example_symlink
Las opciones de formato pueden ser diferentes en otras versiones de *NIX, pero funcionaron para mí en OSX.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-30 00:58:06
Una solución simple usando node.js
:
#!/usr/bin/env node
process.stdout.write(require('path').resolve(process.argv[2]));
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-12 22:23:43