Estoy tratando de establecer una variable en un script sh para los últimos 3 caracteres del nombre base de un archivo (por nombre base quiero decir sin la ruta y sin el sufijo). He logrado hacer esto pero, por pura curiosidad, me pregunto si hay un comando único más corto que pueda usar. Originalmente tenía una sola línea awk
, pero fue bastante larga. Actualmente tengo este script de dos líneas (suponiendo que haya un nombre de archivo completo $1
):
filebase=`basename "$1"`
lastpart=`echo -n ${filebase%.*} | tail -c3`
Entonces, por ejemplo, "/path/to/somefile.txt" termina con "ile" en $lastpart
.
¿Puedo combinar de alguna manera basename
y el bit para pelar el sufijo en un solo comando, y hay alguna forma de enviarlo tail
(o algo más que pueda usar) sin usar una tubería? El sufijo es desconocido, por lo que no puedo basarlo como un parámetro basename
.
El objetivo principal no es en realidad ser lo más corto posible, sino ser lo más legible posible de un vistazo. El contexto real de todo esto es esta pregunta en Superusuario , donde estoy tratando de encontrar una respuesta razonablemente simple.
fuente
file.one.two.three
? ¿Quieresile
otwo
?two
funcionaría; la extensión en eso sería.three
, supongo.Respuestas:
Ese es un trabajo típico para
expr
:Si sabe que sus nombres de archivo tienen el formato esperado (contiene uno y solo un punto y al menos 3 caracteres antes del punto), eso puede simplificarse para:
Tenga en cuenta que el estado de salida será distinto de cero si no hay coincidencia, pero también si la parte coincidente es un número que se resuelve en 0. (como para
a000.txt
oa-00.txt
)Con
zsh
:(
:t
para cola (nombre base),:r
para descanso (con extensión eliminada)).fuente
expr
es otro con el que necesito familiarizarme. Realmente me gustan laszsh
soluciones en general (estaba leyendo sobre su soporte para sustituciones anidadas en el lado izquierdo de un${}
ayer también y deseandosh
tener lo mismo), es un fastidio que no siempre esté presente por defecto.expr
era POSIX. Ciertamente lo es. Sin embargo, rara vez está incorporado.Eso primero elimina los últimos tres caracteres y
$var
luego elimina$var
los resultados de esa eliminación, que devuelve los últimos tres caracteres de$var
. Aquí hay algunos ejemplos más específicamente dirigidos a demostrar cómo puede hacer tal cosa:No tiene que difundir todo esto a través de tantos comandos. Puedes compactar esto:
La combinación
$IFS
conset
los parámetros de shell ting también puede ser un medio muy eficaz de analizar y perforar a través de variables de shell:Eso hará que sólo los tres caracteres inmediatamente anterior al primer período siguiente a la última
/
en$path
. Si desea recuperar solo los primeros tres caracteres inmediatamente anteriores al último.
en$path
(por ejemplo, si existe la posibilidad de más de uno.
en el nombre del archivo) :En ambos casos puedes hacer:
Y...
... imprimirá lo que sigue al
.
Si no le importa usar un programa externo, puede hacer lo siguiente:
Si existe la posibilidad de un
\n
carácter ewline en el nombre del archivo (no aplicable para las soluciones de shell nativas, todos manejan eso de todos modos) :fuente
$base
ahí, lo mejor que pude hacer fue el de tres líneasname=${var##*/} ; base=${name%%.*} ; lastpart=${base#${base%???}}
. En el lado positivo, es puro golpe, pero sigue siendo 3 líneas. (En su ejemplo de "/tmp/file.txt" necesitaría "ile" en lugar de "archivo"). Aprendí mucho sobre la sustitución de parámetros; No tenía idea de que podría hacer eso ... bastante útil. Lo encuentro muy legible, también, personalmente.%
lugar de%%
eliminar el sufijo, y en realidad no necesito quitar el camino, por lo que puedo obtener una mejor línea de dos líneasnoextn=${var%.*} ; lastpart=${noextn#${noextn%???}}
.$IFS
en${noextn}
y no citar la expansión. Entonces, esto es más seguro:lastpart=${noextn#"${noextn%???}"}
Si puedes usar
perl
:fuente
perl -e 'shift =~ /(.{3})\.[^.]*$/ && print $1' $filename
. Sebasename
necesitaría un adicional si el nombre de archivo puede no contener sufijo pero algún directorio en la ruta sí.perl -e 'shift =~ m#(.{3})(?:\.[^./]*)?$# && print $1' $filename
sed
funciona para esto:O
Si
sed
no es compatible-r
, simplemente reemplace las instancias de()
con\(
y\)
, y luego-r
no es necesario.fuente
Si perl está disponible, creo que puede ser más legible que otras soluciones, específicamente porque su lenguaje regex es más expresivo y tiene el
/x
modificador, que permite escribir expresiones regulares más claras:Esto no imprime nada si no existe dicha coincidencia (si el nombre base no tiene extensión o si la raíz antes de la extensión es demasiado corta). Dependiendo de sus requisitos, puede ajustar la expresión regular. Esta expresión regular impone las restricciones:
El uso de esto en una sustitución de comando tiene los problemas normales con la eliminación de demasiadas nuevas líneas finales, un problema que también afecta la respuesta de Stéphane. Se puede tratar en ambos casos, pero es un poco más fácil aquí:
fuente
Python2.7
fuente
Creo que esta función bash, pathStr (), hará lo que estás buscando.
No requiere awk, sed, grep, perl o expr. Utiliza solo bash builtins, por lo que es bastante rápido.
También he incluido las funciones dependientes argsNumber e isOption pero sus funcionalidades podrían incorporarse fácilmente en pathStr.
La función dependiente ifHelpShow no está incluida, ya que tiene numerosas subdependencias para generar el texto de ayuda en la línea de comandos del terminal o en un cuadro de diálogo de la GUI a través de YAD . El texto de ayuda que se le pasa se incluye para documentación. Indique si desea ifHelpShow y sus dependientes.
RECURSOS
fuente
bash
ismos, aparentemente más simple que esto. Además, ¿qué es${#@}
?$#
es la sintaxis para el número de argumentos, y también se usa en otro lugar aquí.${#@}
en la mayoría de los casos, no es equivalente: la especificación POSIX indica los resultados de cualquier expansión de parámetros en cualquiera de los dos,$@
o$*
desafortunadamente. Puede funcionar,bash
pero esa no es una característica confiable, supongo que es lo que estoy tratando de decir.,