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.