Usando a saída da expressão no fatiamento de string bash

Usando a saída da expressão no fatiamento de string bash

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 bashfuncionalidade:

$ 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 zshem vez de bash:

  • 6º ao 7º caracteres docauda(parte retornada por basename) de um caminho:

    num=${${a_path:t}[6,7]}
    

    ( $var:tde cshpara pegar ttudo)

    Você também pode num=${"$(basename -- "$a_path")":5:2}usar esse ksh93 ${var:offset:length}operador adicionado recentemente zshpara compatibilidade. A substituição do comando deve ser colocada entre aspas para que não produza uma matriz para que selecione :5:2um intervalo de caracteres em vez de um intervalo de itens da matriz. No entanto, usar a substituição e a execução de comandos basenameacabaria sendo menos eficiente e menos confiável do que usar o operador zshinterno 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 a patternfrom $var. Com o Msinalizador, a Mparte anexada é retornada em vez de ser removida e Sprocura Ssubstrings, 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 _digitsin $a_path).

    Ou:

    [[ $a_path:t =~ '_([[:digit:]]+)' ]] && num=$match[1]
    

informação relacionada