
Я хочу извлечь подстроку имени файла из пути неизвестной длины. Я могу сделать эти две части по отдельности, но интересно, есть ли способ объединить их без временной переменной?
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, который обрезает самую длинную начальную строку, соответствующуюpattern
from$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]