Как разделить имя файла на переменную?

11

Предположим, у меня есть список CSV-файлов в следующем формате:

INT_V1_<Product>_<ID>_<Name>_<ddmmyy>.csv
ASG_B1_V1_<Product>_<ID>_<Name>_<ddmmyy>.csv

INT_V1_ & ASG_B1_V1_ фиксировано, то есть все файлы CSV начать с ним.
Как я могу разделить имена файлов на переменные?
Например, я хотел захватить Имя и присвоить его переменной $Name.

Juliet.Y
источник
Почему тег "bash", если вы используете ksh в AIX 7.1?
Стефан
Я хочу создать скрипт bash. Просто я хотел попробовать это сначала на ksh, извините за то, что доставил вам неприятности.
Juliet.Y

Ответы:

7

С zsh:

file='INT_V1_<Product>_<ID>_<Name>_<ddmmyy>.csv'

setopt extendedglob
if [[ $file = (#b)*_(*)_(*)_(*)_(*).csv ]]; then
  product=$match[1] id=$match[2] name=$match[3] date=$match[4]
fi

С bash4.3 или новее, ksh93t или новее или zsh в эмуляции sh (хотя в zsh, вы бы скорее делали field=("${(@s:_:)field}")для расщепления, чем с использованием бессмысленного оператора split + glob sh) вы могли бы разбить строку на _символы и ссылаться на них с конца :

IFS=_
set -o noglob
field=($file) # split+glob  operator
date=${field[-1]%.*}
name=${field[-2]}
id=${field[-3]}
product=${field[-4]}

Или (Bash 3.2 или новее):

if [[ $file =~ .*_(.*)_(.*)_(.*)_(.*)\.csv$ ]]; then
  product=${BASH_REMATCH[1]}
  id=${BASH_REMATCH[2]}
  name=${BASH_REMATCH[3]}
  date=${BASH_REMATCH[4]}
fi

(при этом предполагается, что $fileв текущей локали содержится допустимый текст, который не гарантируется для имен файлов, если только вы не исправите локаль на C или другую локаль с однобайтовой кодировкой символов).

Как zsh«S *выше, .*является жадным . Таким образом, первый будет съедать столько, *_сколько возможно, а остальные .*будут соответствовать только _-безопасным строкам.

С ksh93, вы могли бы сделать

pattern='*_(*)_(*)_(*)_(*).csv'
product=${file//$pattern/\1}
id=${file//$pattern/\2}
name=${file//$pattern/\3}
date=${file//$pattern/\4}

В POSIX shсценарии, вы можете использовать ${var#pattern}, ${var%pattern}стандартные оператор расширения параметров:

rest=${file%.*} # remove .csv suffix
date=${rest##*_} # remove everything on the left up to the rightmost _
rest=${rest%_*} # remove one _* from the right
name=${rest##*_}
rest=${rest%_*}
id=${rest##*_}
rest=${rest%_*}
product=${rest##*_}

Или снова используйте оператор split + glob:

IFS=_
set -o noglob
set -- $file
shift "$(($# - 4))"
product=$1 id=$2 name=$3 date=${4%.*}
Стефан Шазелас
источник
Я использую bash на AIX7.1 и сейчас тестирую на ksh. Как - то я сталкиваюсь ошибка с указанием ksh: file: 0403-046 The specified subscript cannot be greater than 4095.на ${field[-1]}или что - либо в форме ${x[n]}.
Juliet.Y
@ Джульетта, ${field[-1]}была для bash-4.3+. Для ksh, используйте любое из решений "POSIX". Поддержка отрицательного индекса не была добавлена ​​до ksh93t (функция, созданная в zsh).
Стефан
Хорошо, отметил. Большое спасибо, скрипты работают нормально.
Juliet.Y
4

Вы можете взять значения вашего поля <Name>с этой командой:

cut -d'<' -f4 < csvlist | sed -e 's/>_//g'

(или с awk):

awk -F'<' '{print $4}' < csvlist | sed -e 's/>_//g'

И вы можете поместить их в переменную, как это:

variable_name=$(cut -d'<' -f4 < csvlist | sed -e 's/>_//g')

или

awk -F'<' '{print $4}' < csvlist | sed -e 's/>_//g'

Не ясно в вопросе, хотите ли вы одну и ту же переменную для всех значений или одну переменную для каждого из них.

Зумо де Видрио
источник
1
file='INT_V1_<Product>_<ID>_<Name>_<ddmmyy>.csv'
IFS=\_ read -r x x product id name date x <<< "$file"
date=${date%.*}

источник
Обратите внимание, что _это не особенное и не нуждается в кавычках. Это предполагает, что имя файла не содержит символов новой строки. Вы можете добавить -d ''.
Стефан