¿Cómo dividir el nombre del archivo en variable?


11

Supongamos que tengo una lista de archivos csv con el siguiente formato:

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

El INT_V1_ y ASG_B1_V1_ es fijo, lo que significa que todos los archivos csv comienzan con él.
¿Cómo puedo dividir los nombres de archivo en variable?
Por ejemplo, quería capturar el Nombre y asignarlo a una variable $Name.


¿Por qué la etiqueta "bash", si está utilizando ksh en AIX 7.1?
Stéphane Chazelas

Deseo producir un script bash. Solo que quería probarlo primero en ksh, perdón por causarte problemas.
Juliet.Y

Respuestas:


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 más reciente, ksh93t o más nuevo o zsh en emulación sh (aunque en zsh, preferiría simplemente field=("${(@s:_:)field}")dividir en lugar de usar el operador sin sentido split + glob de sh) podría dividir la cadena en los _caracteres y hacer referencia a ellos desde el final :

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

O (bash 3.2 o más reciente):

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

(se supone que $filecontiene texto válido en la configuración regional actual que no está garantizado para los nombres de archivo a menos que arregle la configuración regional en C u otra configuración regional con un solo byte por juego de caracteres).

Como zshel de *arriba, .*es codicioso . Entonces, el primero comerá la mayor cantidad *_posible, por lo que el resto .*solo coincidirá con _cadenas libres.

Con ksh93, podrías hacer

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

En un POSIX shscript, podría utilizar los ${var#pattern}, ${var%pattern}operadores de expansión de parámetros estándar:

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

O use el operador split + glob nuevamente:

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

Estoy usando bash en AIX7.1 y actualmente estoy probando en ksh. De alguna manera me encuentro con un error que indica ksh: file: 0403-046 The specified subscript cannot be greater than 4095.para ${field[-1]}o cualquier cosa en forma ${x[n]}.
Juliet.Y

@Juliet, ${field[-1]}era para bash-4.3+. Para ksh, utilice cualquiera de las soluciones "POSIX". La compatibilidad con el subíndice negativo no se agregó antes de ksh93t (una característica que se origina en zsh).
Stéphane Chazelas

Ok, notado. Muchas gracias, los scripts están funcionando bien.
Juliet.Y

4

Puede tomar los valores de su campo <Name>con este comando:

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

(o con awk):

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

Y puedes ponerlos en una variable como esta:

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

o

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

No está claro en la pregunta si desea la misma variable para todos los valores o una sola variable para cada uno de ellos.


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

Tenga en cuenta que _no es especial y no necesita ser citado. Eso supone que el nombre del archivo no contiene caracteres de nueva línea. Es posible que desee agregar a -d ''.
Stéphane Chazelas
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.