Estoy buscando una línea elegante (p. Ej. awk
) Que acorte una cadena de una ruta Unix usando el primer carácter de cada nivel primario / intermedio, pero el nombre base completo. Más fácil de mostrar con ejemplos:
/path/to/file
→/p/t/file
/tmp
→/tmp
/foo/bar/.config/wizard_magic
→/f/b/./wizard_magic
/foo/bar/.config/wizard_magic
→/f/b/.c/wizard_magic
A la luz de los buenos puntos de @ MichaelKjörling y @ChrisH a continuación, este ejemplo muestra cómo podemos mostrar los dos primeros caracteres cuando el primer carácter es un punto.
/f/b/.c/wizard_magic
. El punto es a menudo tan común en un directorio particular que puede ser una pista muy pequeña de dónde debería estar buscando..
normalmente solo significa "directorio actual". Entonces/f/b/./wizard_magic
es lo mismo que/f/b/wizard_magic
porque el elemento de ruta se./
comprime en un elemento de ruta vacío.Respuestas:
Para este archivo de prueba:
Las abreviaturas se pueden generar con este código awk:
Edit1: uso de dos caracteres para nombres de puntos
Esta versión abrevia los nombres de directorio a un carácter, excepto los nombres que comienzan con los
.
cuales se abrevian a dos caracteres:Cómo funciona
-F/
Esto le dice a awk que use una barra como el separador de campo en la entrada.
for (i=1;i<NF;i++) $i=substr($i,1,1)
Esto recorre cada campo, excepto el último, y lo reemplaza solo con su primer carácter.
EDITAR1: en la versión revisada, hacemos la longitud de la subcadena 2 cuando el campo comienza con
.
.1
Esto le dice a awk que imprima la línea revisada.
OFS=/
Esto le dice a awk que use una barra como el separador de campo en la salida.
fuente
‥
separador:awk -F/ '{for (i=1;i<NF;i++) $i=substr($i,1,1+($i~/^[.]/))(i==1||length($i)<2?"":"‥")} 1' OFS=/ <<<$PWD
da:/foo/bar/.config/wizard_magic
→/f‥/b‥/.c‥/wizard_magic
Bastante fácil en sed (suponiendo que no haya nuevas líneas en los nombres de archivo):
Menos fácil en awk porque carece de referencias (excepto en Gawk, pero con una sintaxis torpe):
En zsh (con la ruta en
$full_path
):fuente
\1
en la cadena de reemplazo no significa una referencia a un grupo de captura en el patrón. Una referencia inversa es una referencia inversa sin importar dónde la use.puedes hacerlo como:
y aquí hay un
sed
:eso se acerca bastante a hacer las mismas cosas que la función hace a continuación. no se abrevia con tildes ni se inserta
$PWD
en la cabeza para una barra no inicial como lo hace la función (y, de hecho, nunca imprime la barra diagonal), pero eso podría manejarse después. procesa componentes de ruta nula y puntos únicos, y elimina los..
casos.dada la misma
man
ruta que lacd
anterior imprime:también imprimirá uno o dos puntos iniciales adicionales para cada componente de ruta que comience con tal y no sea solo uno o dos puntos.
usted preguntó por hacer más que el comienzo de caracteres para un trayecto de componente con una
.
. para hacerlo, pensé que cada componente necesitaría atención individual de todos modos, y como tenía curiosidad, intenté descifrar una ruta canónica sin el directorio de cambio. Después de un poco de prueba y error, finalmente decidí que la única forma de hacerlo bien era hacerlo dos veces, hacia atrás y hacia adelante:para que nunca cambie el directorio o intente confirmar la existencia de algún componente de ruta, pero exprime
/
delimitadores repetidos y descarta/./
por completo los componentes de un solo punto, y procesa/../
los componentes de doble punto de manera apropiada.cuando
$IFS
se establece en algún carácter que no sea un espacio en blanco , una secuencia de dos o más$IFS
caracteres dará como resultado uno o más campos nulos. por lo que varias barras inclinadas consecutivas resultan en argumentos de valor nulo. Lo mismo es cierto para un$IFS
personaje principal . y así, cuando seset -- $1
divide, si el resultado$1
es nulo, entonces comenzó con una barra, de lo contrario,${1:+$PWD}
si no es nulo, entonces lo inserto$PWD
. en otras palabras, si el primer argumento no comienza con una barra inclinada, se$PWD
antepondrá. eso es lo más cercano a la validación de ruta .de lo contrario, el primer
for
bucle invierte recursivamente el orden de los componentes de la ruta, como:... mientras lo hace, ignora cualquier componente de punto único o nulo, y para
..
ello ...... la segunda pasada invierte este efecto, y mientras lo hace, exprime cada componente a 2 puntos + char , o 1 punto + char , o char .
por lo que debería funcionar en un camino canónico independientemente de la existencia.
Agregué / resté un poco al segundo bucle. ahora
set
es menos frecuente (solo una vez para cada[!./]*
componente) , y lascase
evaluaciones de patrones de cortocircuitos la mayor parte del tiempo (gracias al patrón mencionado anteriormente) , e incluye una evaluación de coincidencia de llamada de cola contra~
. si todas o una parte inicial (como se divide en componentes completos) de la ruta canónica finalmente puede coincidir~
, el bit de coincidencia se eliminará y~
se sustituirá un literal . Para hacer esto, tuve que mantener una copia completa de la ruta junto a la abreviada (porque hacer coincidir la ruta abreviada con~
probablemente no sería muy útil) , y así se mantiene$3
. el últimowhile
La rama de bucle solo se ejecuta si~
coincide con un subconjunto de$3
.si lo ejecuta con el
set -x
rastreo habilitado, puede verlo funcionar.fuente
El tema Zsh "a pescado" de Oh My Zsh contiene un fragmento de Perl para hacer exactamente eso que tiene soporte Unicode:
fuente
¿Quieres tener un nombre corto o usarlo para tu línea de comando?
Para la línea de comandos, tengo las siguientes sugerencias:
¿No te ayuda completar el archivo en tu shell?
A veces tienes suerte y no tienes que hacer algo especial:
Cuando solo tiene algunos directorios que le interesan, puede usar alias:
O puede configurar variables para sus directorios favoritos
Creo que estas opciones tienen más sentido que intentar resolver esto con una función definida en .bashrc (o .profile) como
y llamando a esta función x con espacios entre sus letras:
fuente