Comment diviser un nom de fichier en variable?

11

Supposons que j'ai une liste de fichiers csv au format suivant:

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

Le INT_V1_ & ASG_B1_V1_ est fixe, ce qui signifie tous les fichiers csv commencent avec elle.
Comment puis-je diviser les noms de fichiers en variables?
Par exemple, je voulais capturer le nom et l'attribuer à une variable $Name.

Juliet.Y
la source
Pourquoi la balise "bash", si vous utilisez ksh sur AIX 7.1?
Stéphane Chazelas
Je souhaite produire un script bash. Juste que je voulais l'essayer d'abord sur ksh, désolé de vous avoir causé des ennuis.
Juliet.Y

Réponses:

7

Avec 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

Avec bash4.3 ou plus récent, ksh93t ou plus récent ou zsh dans l'émulation sh (bien que dans zsh, vous préférez tout simplement faire field=("${(@s:_:)field}")pour le fractionnement plutôt que d'utiliser l'opérateur de non-sens split + glob de sh), vous pouvez diviser la chaîne en _caractères et les référencer depuis la fin :

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 plus récent):

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

(cela suppose qu'il $filecontient du texte valide dans les paramètres régionaux actuels, ce qui n'est pas garanti pour les noms de fichiers, sauf si vous fixez les paramètres régionaux à C ou à d'autres paramètres régionaux avec un seul octet par jeu de caractères).

Comme zsh« est *ci - dessus, l' .*est avide . Ainsi, le premier en mangera autant *_que possible, de sorte que le reste .*ne correspondra qu'à des _chaînes libres.

Avec ksh93, tu pourrais faire

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

Dans un POSIX shscript, vous pouvez utiliser les ${var#pattern}, ${var%pattern}opérateurs d'extension de paramètres standard:

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 utilisez à nouveau l'opérateur split + glob:

IFS=_
set -o noglob
set -- $file
shift "$(($# - 4))"
product=$1 id=$2 name=$3 date=${4%.*}
Stéphane Chazelas
la source
J'utilise bash sur AIX7.1 et je teste actuellement dans ksh. D'une certaine manière je rencontre une erreur indiquant ksh: file: 0403-046 The specified subscript cannot be greater than 4095.pour ${field[-1]}ou quoi que ce soit sous forme ${x[n]}.
Juliet.Y
@Juliet, ${field[-1]}c'était pour bash-4.3+. Pour ksh, utilisez l'une des solutions "POSIX". La prise en charge de l'indice négatif n'a pas été ajoutée avant ksh93t (une fonctionnalité originaire de zsh).
Stéphane Chazelas
OK, c'est noté. Merci beaucoup, les scripts fonctionnent bien.
Juliet.Y
4

Vous pouvez prendre les valeurs de votre champ <Name>avec cette commande:

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

(ou avec awk):

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

Et vous pouvez les mettre dans une variable comme celle-ci:

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

ou

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

Il n'est pas clair dans la question si vous voulez la même variable pour toutes les valeurs ou une seule variable pour chacune d'elles.

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

la source
Notez que ce _n'est pas spécial et n'a pas besoin d'être cité. Cela suppose que le nom de fichier ne contient pas de caractères de nouvelle ligne. Vous voudrez peut-être ajouter un -d ''.
Stéphane Chazelas