
Quiero extraer una subcadena de un nombre de archivo de una ruta de longitud desconocida. Puedo hacer estas dos partes por separado, pero me pregunto si hay alguna manera de combinar las dos sin una variable temporal.
INPUT_PATH=/path/to/subfolder/file_17.txt
# I would like to extract "17", the filname will always be 'file_XX.txt'
# The subfolder name is variable length
TMP=$(basename ${INPUT_PATH})
FILE_NUMBER=${TMP:5:2}
echo ${FILE_NUMBER} # This works as expected
Lo intenté ${$(basename $INPUT_PATH):5:2}
pero me da un error de sustitución grave. ¿Hay algún truco para hacer esto?
Respuesta1
Adoptar un enfoque diferente al problema y proporcionar una solución unificada utilizando únicamente bash
la funcionalidad:
$ cat demo.sh
#!/bin/bash
INPUT_PATH=/path/to/subfolder/file_17.txt
FILE_NUMBER=${INPUT_PATH:((${#INPUT_PATH} -6)):2}
echo ${FILE_NUMBER}
$
$./demo.sh
17
Un método más sencillo sería contar hacia atrás desde el final de la cadena, es decir
FILE_NUMBER=${INPUT_PATH: -6:2}
Obviamente, esta solución depende de la variable de cadena que termina en "##XXXX", donde "##" son los dos dígitos de interés y "XXXX" son los últimos 4 caracteres de la cadena.
Respuesta2
Como estás usando bash, puedes usar la coincidencia de expresiones regulares:
if [[ $input =~ ([[:digit:]]+)\.txt$ ]]; then
file_num=${BASH_REMATCH[1]}
fi
Respuesta3
La forma más sencilla es utilizar FILE_NUMBER en lugar de TMP:
FILE_NUMBER=$(basename ${INPUT_PATH})
FILE_NUMBER=${FILE_NUMBER:5:2}
Además, usar la expansión de parámetros es más rápido que llamar al nombre base:
FILE_NUMBER=${INPUT_PATH##*/}
FILE_NUMBER=${FILE_NUMBER:5:2}
Puedes usar sed para hacer todo en una sola línea, pero es más lento y menos legible:
FILE_NUMBER=$(sed 's|.*/||;s/.....\(..\).*/\1/' <<<"$INPUT_PATH")
Respuesta4
Para aquellos que usan zsh
en lugar de bash
:
6to a 7mo personajes delcola(parte devuelta por
basename
) de una ruta:num=${${a_path:t}[6,7]}
(
$var:t
decsh
para conseguir elt
todo)También puede
num=${"$(basename -- "$a_path")":5:2}
utilizar eseksh93
${var:offset:length}
operador agregado recientementezsh
para mayor compatibilidad. La sustitución del comando debe estar entre comillas para que no genere una matriz, de modo que seleccione:5:2
un rango de caracteres en lugar de un rango de elementos de la matriz. Sin embargo, usar la sustitución y ejecución de comandosbasename
terminaría siendo menos eficiente y menos confiable que usarzsh
el:t
operador incorporado.primera secuencia de dígitos en elcola:
num=${(MS)${a_path:t}##<->}
${var##pattern}
es el operador ksh que elimina la cadena inicial más larga que coincide conpattern
from$var
. Con laM
bandera, laM
parte adjunta se devuelve en lugar de eliminarse yS
buscaS
cadenas secundarias, no solo al principio. Y<->
coincide con cualquier secuencia de dígitos (<x-y>
con límites no especificados).secuencia de dígitos después de a
_
en elcola:num=${${a_path:t}/(#m)*_(<->)*/$match[1]}
(necesita
extendedglob
; tenga cuidado, devuelve la cola completa si no hay_digits
in$a_path
).O:
[[ $a_path:t =~ '_([[:digit:]]+)' ]] && num=$match[1]