Использование вывода выражения в нарезке строк bash

Использование вывода выражения в нарезке строк bash

Я хочу извлечь подстроку имени файла из пути неизвестной длины. Я могу сделать эти две части по отдельности, но интересно, есть ли способ объединить их без временной переменной?

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

Я пробовал, ${$(basename $INPUT_PATH):5:2}но это дает ошибку подстановки. Есть ли какой-то трюк, чтобы сделать это?

решение1

Другой подход к проблеме и предоставление однострочного решения с использованием только bashфункциональности:

$ 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

Более простой подход — отсчитывать от конца строки, т.е.

FILE_NUMBER=${INPUT_PATH: -6:2}

Очевидно, это решение зависит от строковой переменной, заканчивающейся на «##XXXX», где «##» — две интересующие нас цифры, а «XXXX» — последние 4 символа строки.

решение2

Поскольку вы используете bash, вы можете использовать сопоставление регулярных выражений:

if [[ $input =~ ([[:digit:]]+)\.txt$ ]]; then
    file_num=${BASH_REMATCH[1]}
fi

решение3

Самый простой способ — использовать FILE_NUMBER вместо TMP:

FILE_NUMBER=$(basename ${INPUT_PATH})
FILE_NUMBER=${FILE_NUMBER:5:2}

Кроме того, использование расширения параметров быстрее, чем вызов basename:

FILE_NUMBER=${INPUT_PATH##*/}
FILE_NUMBER=${FILE_NUMBER:5:2}

Вы можете использовать sed, чтобы сделать все в одной строке, но это медленнее и менее читабельно:

FILE_NUMBER=$(sed 's|.*/||;s/.....\(..\).*/\1/' <<<"$INPUT_PATH")

решение4

Для тех, кто использует zshвместо bash:

  • 6-й и 7-й символыхвост(часть, возвращаемая функцией basename) пути:

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

    ( $var:tот cshполучить tаил)

    Вы также можете num=${"$(basename -- "$a_path")":5:2}использовать этот ksh93 ${var:offset:length}недавно добавленный оператор zshдля совместимости. Подстановка команды должна быть заключена в кавычки, чтобы она не выдавала массив, так что выбирается :5:2диапазон символов вместо диапазона элементов массива. Однако использование подстановки команды и ее выполнение basenameв конечном итоге будет менее эффективным и менее надежным, чем использование zshвстроенного :tоператора .

  • первая последовательность цифр вхвост:

    num=${(MS)${a_path:t}##<->}
    

    ${var##pattern}это оператор ksh, который обрезает самую длинную начальную строку, соответствующую patternfrom $var. С этим Mфлагом Mвозвращается присоединенная часть, а не обрезается, и Sищет Sподстроки, а не только в начале. И <->сопоставляет любую последовательность цифр ( <x-y>с неуказанными границами).

  • последовательность цифр после_ вхвост:

    num=${${a_path:t}/(#m)*_(<->)*/$match[1]}
    

    (нужно extendedglob; будьте осторожны, он возвращает полный хвост, если _digitsв нем нет $a_path).

    Или:

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

Связанный контент