¿Cómo divido la variable $ 0 para encontrar directorios y rutas relativas en bash?

10

La variable $ 0 contiene la información de ruta del script.

  • ¿Cómo puedo cambiar la información de ruta a ruta absoluta? Me refiero a cómo procesar ~,., .. o similar?
  • ¿Cómo puedo dividir la información de la ruta en el directorio y el nombre del archivo?

Podría usar python / perl para esto, pero quiero usar bash si es posible.

prosseek
fuente
¿Qué estás intentando lograr? por cierto. llamar a un script con ~ / foo.sh se expandirá ~ en mi directorio de inicio por $ 0 con mi versión bash (GNU bash, versión 4.1.5 (2) -release- (x86_64-unknown-linux-gnu))
echox

Respuestas:

17

No necesita procesar cosas como ~, el shell lo hace por usted. Es por eso que puede pasar ~/filenamea cualquier script o programa y funciona: todos esos programas no se manejan ~solos, su shell convierte el argumento /home/username/filenamey lo pasa al programa en su lugar:

$ echo ~/filename
/home/mrozekma/filename

Si necesita un nombre de archivo canónico (uno que no incluya cosas como ..), use realpath(gracias Neil ):

$ realpath ~/../filename
/home/filename

En cuanto a dividir la ruta en el nombre del directorio y el nombre del archivo, use dirnamey basename:

$ dirname /foo/bar/baz
/foo/bar

$ basename /foo/bar/baz
baz
Michael Mrozek
fuente
Creo que te refieres a realpath, no a readlink. readlink no funcionará en la situación común, es decir, cuando el enlace usa una ruta relativa y usted no está en el mismo directorio que el enlace.
Neil Mayhew
@Neil realpathes mejor, pero readlinkparece funcionar cuando lo intento; ¿Conoces un ejemplo donde falla?
Michael Mrozek
1
cd /tmp; touch foo; ln -s foo bar; cd; readlink /tmp/bar. Esto vuelve fooy realpathvuelve /tmp/foo, que es lo que quieres.
Neil Mayhew
1
@Neil, @Michael: readlink -f(nota -f) es casi equivalente a realpath, y es más portátil: readlink -fen GNU coreutils y también existe en algunos otros sistemas; realpathse empaqueta por separado y es posible que no se instale (por ejemplo, solo 5 paquetes no tan comunes dependen de él en Ubuntu 10.04 o Debian lenny).
Gilles 'SO- deja de ser malvado'
@Gilles: buen punto. Gracias por el recordatorio sobre realpath -f.
Neil Mayhew
5

Usar dirname y basename como lo menciona Michael debería ser la forma más segura de obtener lo que desea.

De todos modos, si realmente quieres hacer esto con "bash only tools", puedes usar la sustitución de parámetros:

echo `basename $PWD`        # Basename of current working directory.
echo "${PWD##*/}"           # Basename of current working directory.
echo
echo `basename $0`          # Name of script.
echo $0                     # Name of script.
echo "${0##*/}"             # Name of script.
echo
filename=test.data
echo "${filename##*.}"      # data
                            # Extension of filename.

Este ejemplo está tomado directamente de la Guía avanzada de secuencias de comandos Bash que vale la pena echarle un vistazo.

La explicación es bastante simple:

$ {var # Pattern} Eliminar de $ var la parte más corta de $ Pattern que coincida con el extremo frontal de $ var. $ {var ## Pattern} Elimina de $ var la parte más larga de $ Pattern que coincide con la parte frontal de $ var.

Mire el patrón como una expresión regular y el #o ##como algún tipo de modificador codicioso / no codicioso.

Esto podría ser útil si tendrá que hacer algunas extracciones más complicadas de una parte de rutas.

echox
fuente
55
Tenga cuidado al usar ${...##...}y amigos en lugar de dirnamey basenameque no funcionan en todos los casos. Por ejemplo, ambos ${0##*/}y ${0%/*}expandir a la misma como $0si $0no contiene a /.
Gilles 'SO- deja de ser malvado'
@Gilles Entiendo por qué ${0%/*}no es una buena alternativa para dirname. Sin embargo, ¿qué tiene de malo usar en ${0##*/}lugar de basename?
toxalot
@toxalot Para este, el único problema es cuando $0es un nombre de directorio con un final /. Pero lamento mi consejo, porque dirnameno hay nada mejor.
Gilles 'SO- deja de ser malvado'
2

realpath es un comando que le indica la ruta real (elimina ... y enlaces simbólicos, etc.)

Es estándar con FreeBSD. Según esta discusión, también está disponible para Linux:

http://www.unix.com/shell-programming-scripting/89294-geting-real-path.html

Esa discusión también ofrece una solución bash:

$ bash -c "cd /foo/../bar/ ; pwd"

fuente
1
Exactamente, algo como esto generalmente funciona: MYDIR = "$ (cd $ (dirname" $ ​​0 "); pwd)"
Kevin Cantu