Como dividir o nome do arquivo em variável?

11

Suponha que eu tenha uma lista de arquivos csv com o seguinte formato:

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

O INT_V1_ e ASG_B1_V1_ é fixo, o que significa que todos os arquivos csv começam com ele.
Como posso dividir os nomes dos arquivos em variáveis?
Por exemplo, eu queria capturar o Nome e atribuí-lo a uma variável $Name.

Juliet.Y
fonte
Por que a tag "bash", se você estiver usando o ksh no AIX 7.1?
Stéphane Chazelas
Desejo produzir um script bash. Só que eu queria experimentar primeiro no ksh, desculpe por causar problemas.
precisa saber é o seguinte

Respostas:

7

Com 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

Com a bashversão 4.3 ou mais recente, ksh93t ou mais recente ou zsh na emulação sh (no entanto zsh, você prefere simplesmente field=("${(@s:_:)field}")dividir do que usar o operador sem sentido split + glob sh), você pode dividir a string em _caracteres e referenciá-los a partir do final :

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

Ou (bash 3.2 ou mais recente):

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

(que pressupõe que $filecontenha texto válido no código de idioma atual, que não é garantido para nomes de arquivos, a menos que você fixe o código de idioma em C ou outro código de idioma com um byte único por conjunto de caracteres).

Como zshé *acima, o .*é ganancioso . Portanto, o primeiro comerá o maior número *_possível, de modo que o restante .*corresponderá apenas a _cadeias livres.

Com ksh93, você poderia fazer

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

Em um POSIX shscript, você poderia usar os ${var#pattern}, ${var%pattern}operadores de expansão de parâmetro padrão:

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##*_}

Ou use o operador split + glob novamente:

IFS=_
set -o noglob
set -- $file
shift "$(($# - 4))"
product=$1 id=$2 name=$3 date=${4%.*}
Stéphane Chazelas
fonte
Estou usando o bash no AIX7.1 e atualmente estou testando no ksh. De alguma forma eu encontrar um erro afirmando ksh: file: 0403-046 The specified subscript cannot be greater than 4095.para ${field[-1]}ou qualquer coisa na forma ${x[n]}.
precisa saber é
@ Juliet, ${field[-1]}era para bash-4.3+. Para ksh, use qualquer uma das soluções "POSIX". O suporte para subscrito negativo não foi adicionado antes do ksh93t (um recurso originário do zsh).
Stéphane Chazelas
Tudo bem, anotado. Muito obrigado, os scripts estão funcionando bem.
precisa saber é o seguinte
4

Você pode pegar os valores do seu campo <Name>com este comando:

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

(ou com awk):

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

E você pode colocá-los em uma variável como esta:

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

ou

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

Não está claro na pergunta se você deseja a mesma variável para todos os valores ou uma única variável para cada um deles.

Zumo de Vidrio
fonte
1
file='INT_V1_<Product>_<ID>_<Name>_<ddmmyy>.csv'
IFS=\_ read -r x x product id name date x <<< "$file"
date=${date%.*}

fonte
Observe que _não é especial e não precisa ser citado. Isso pressupõe que o nome do arquivo não contenha caracteres de nova linha. Você pode adicionar um -d ''.
Stéphane Chazelas