Come dividere il nome del file in variabile?


11

Supponiamo di avere un elenco di file CSV con il seguente formato:

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

L'INT_V1_ & ASG_B1_V1_ è fisso, cioè tutti i file csv iniziano con esso.
Come posso dividere i nomi dei file in variabili?
Ad esempio, volevo acquisire il Nome e assegnarlo a una variabile $Name.


Perché il tag "bash", se stai usando ksh su AIX 7.1?
Stéphane Chazelas l'

Vorrei produrre uno script bash. Solo che volevo provarlo prima su ksh, scusami per averti causato problemi.
Juliet.Y

Risposte:


7

Con 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

Con bash4.3 o più recenti, ksh93t o più recenti o zsh in emulazione sh (sebbene in zsh, preferiresti semplicemente fare field=("${(@s:_:)field}")per dividere piuttosto che usare l'operatore non-sense split + glob di sh) potresti dividere la stringa su _caratteri e fare riferimento a loro dalla fine :

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

Oppure (bash 3.2 o più recente):

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

(ciò presuppone che $filecontenga un testo valido nella locale corrente che non è garantito per i nomi di file a meno che non si corregga la locale su C o altra locale con un set di caratteri a byte singolo per carattere).

Come zsh'il *sopra, il .*è avido . Quindi il primo ne mangerà il maggior numero *_possibile, quindi il rimanente abbinerà .*solo _stringhe libere.

Con ksh93, potresti fare

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

In un POSIX shscript, è possibile utilizzare i ${var#pattern}, ${var%pattern}operatori di espansione parametri 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##*_}

Oppure usa nuovamente l'operatore split + glob:

IFS=_
set -o noglob
set -- $file
shift "$(($# - 4))"
product=$1 id=$2 name=$3 date=${4%.*}

Sto usando bash su AIX7.1 e attualmente sto testando in ksh. In qualche modo ho incontrato un errore che indica ksh: file: 0403-046 The specified subscript cannot be greater than 4095.per ${field[-1]}o niente in forma ${x[n]}.
Juliet.Y

@Juliet, ${field[-1]}era per bash-4.3+. Per ksh, utilizzare una delle soluzioni "POSIX". Il supporto per il pedice negativo non è stato aggiunto prima di ksh93t (una funzione che ha origine in zsh).
Stéphane Chazelas l'

Ok notato. Grazie mille, gli script funzionano bene.
Juliet.

4

Puoi prendere i valori del tuo campo <Name>con questo comando:

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

(o con awk):

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

E puoi metterli in una variabile come questa:

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

o

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

Non è chiaro nella domanda se si desidera la stessa variabile per tutti i valori o una singola variabile per ognuno di essi.


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

Nota che _non è speciale e non ha bisogno di essere quotato. Ciò presuppone che il nome del file non contenga caratteri di nuova riga. Potresti voler aggiungere a -d ''.
Stéphane Chazelas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.