
Quero extrair uma substring de um nome de arquivo de um caminho de comprimento desconhecido. Posso fazer essas duas partes separadamente, mas gostaria de saber se existe uma maneira de combinar as duas sem uma variável temporária?
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
Eu tentei, ${$(basename $INPUT_PATH):5:2}
mas isso deu um erro de substituição ruim. Existe algum truque para fazer isso?
Responder1
Adotando uma abordagem diferente para o problema e fornecendo uma solução unifilar usando apenas bash
funcionalidade:
$ 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
Uma abordagem mais simples seria contar regressivamente a partir do final da string, ou seja
FILE_NUMBER=${INPUT_PATH: -6:2}
Obviamente, esta solução depende da variável string que termina com "##XXXX" onde "##" são os dois dígitos de interesse e "XXXX" são os últimos 4 caracteres da string.
Responder2
Como você está usando o bash, você pode usar a correspondência de regex:
if [[ $input =~ ([[:digit:]]+)\.txt$ ]]; then
file_num=${BASH_REMATCH[1]}
fi
Responder3
A maneira mais fácil é usar FILE_NUMBER em vez de TMP:
FILE_NUMBER=$(basename ${INPUT_PATH})
FILE_NUMBER=${FILE_NUMBER:5:2}
Além disso, usar a expansão de parâmetros é mais rápido do que chamar basename:
FILE_NUMBER=${INPUT_PATH##*/}
FILE_NUMBER=${FILE_NUMBER:5:2}
Você pode usar sed para fazer tudo em uma única linha, mas é mais lento e menos legível:
FILE_NUMBER=$(sed 's|.*/||;s/.....\(..\).*/\1/' <<<"$INPUT_PATH")
Responder4
Para aqueles que usam zsh
em vez de bash
:
6º ao 7º caracteres docauda(parte retornada por
basename
) de um caminho:num=${${a_path:t}[6,7]}
(
$var:t
decsh
para pegart
tudo)Você também pode
num=${"$(basename -- "$a_path")":5:2}
usar esseksh93
${var:offset:length}
operador adicionado recentementezsh
para compatibilidade. A substituição do comando deve ser colocada entre aspas para que não produza uma matriz para que selecione:5:2
um intervalo de caracteres em vez de um intervalo de itens da matriz. No entanto, usar a substituição e a execução de comandosbasename
acabaria sendo menos eficiente e menos confiável do que usar o operadorzsh
interno do:t
.primeira sequência de dígitos docauda:
num=${(MS)${a_path:t}##<->}
${var##pattern}
é o operador ksh que remove a string inicial mais longa que corresponde apattern
from$var
. Com oM
sinalizador, aM
parte anexada é retornada em vez de ser removida eS
procuraS
substrings, não apenas no início. E<->
corresponde a qualquer sequência de dígitos (<x-y>
com limites não especificados).sequência de dígitos após a
_
nocauda:num=${${a_path:t}/(#m)*_(<->)*/$match[1]}
(precisa de
extendedglob
; cuidado, pois retorna a cauda completa se não houver_digits
in$a_path
).Ou:
[[ $a_path:t =~ '_([[:digit:]]+)' ]] && num=$match[1]