Bagaimana cara membagi nama file menjadi variabel?

11

Misalkan saya memiliki daftar file csv dengan format berikut:

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

The INT_V1_ & ASG_B1_V1_ adalah tetap, yang berarti semua file csv mulai dengan itu.
Bagaimana saya bisa membagi nama file menjadi variabel?
Misalnya, saya ingin menangkap Nama & menetapkannya ke variabel $Name.

Juliet
sumber
Kenapa dengan tag "bash", jika Anda menggunakan ksh di AIX 7.1?
Stéphane Chazelas
Saya ingin membuat skrip bash. Hanya saja saya ingin mencobanya terlebih dahulu di ksh, maaf telah menyebabkan Anda kesulitan.
Juliet.Y

Jawaban:

7

Dengan 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

Dengan bashemulasi sh 4.3 atau lebih baru, ksh93t atau lebih baru atau zsh (meskipun dalam zsh, Anda lebih suka melakukan field=("${(@s:_:)field}")pemisahan daripada menggunakan operator split + glob non-sense sh) Anda dapat membagi string pada _karakter dan merujuknya dari akhir :

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

Atau (kurang lebih 3,2 atau lebih baru):

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

(yang mengasumsikan $fileberisi teks yang valid di lokal saat ini yang tidak dijamin untuk nama file kecuali jika Anda memperbaiki lokal ke C atau lokal lainnya dengan satu byte per karakter karakter).

Seperti zshdi *atas, .*itu serakah . Jadi yang pertama akan makan sebanyak *_mungkin, jadi sisanya .*hanya akan cocok dengan _string gratis.

Dengan ksh93, Anda bisa melakukannya

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

Dalam POSIX shskrip, Anda bisa menggunakan ${var#pattern}, ${var%pattern}operator ekspansi parameter standar:

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

Atau gunakan lagi operator split + glob:

IFS=_
set -o noglob
set -- $file
shift "$(($# - 4))"
product=$1 id=$2 name=$3 date=${4%.*}
Stéphane Chazelas
sumber
Saya menggunakan bash di AIX7.1 & Saya sedang menguji di ksh. Entah bagaimana saya menemukan kesalahan yang menyatakan ksh: file: 0403-046 The specified subscript cannot be greater than 4095.untuk ${field[-1]}atau apa pun dalam formulir ${x[n]}.
Juliet.Y
@ Juliet, ${field[-1]}adalah untuk bash-4.3+. Untuk ksh, gunakan salah satu dari solusi "POSIX". Dukungan untuk subskrip negatif tidak ditambahkan sebelum ksh93t (fitur yang berasal dari zsh).
Stéphane Chazelas
Oke tercatat. Terima kasih banyak, skrip bekerja dengan baik.
Juliet.Y
4

Anda bisa mengambil nilai bidang Anda <Name>dengan perintah ini:

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

(atau dengan awk):

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

Dan Anda dapat menempatkan mereka dalam variabel seperti ini:

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

atau

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

Tidak jelas dalam pertanyaan apakah Anda menginginkan variabel yang sama untuk semua nilai atau satu variabel tunggal untuk masing-masingnya.

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

sumber
Catatan yang _tidak spesial dan tidak perlu dikutip. Itu mengasumsikan nama file tidak mengandung karakter baris baru. Anda mungkin ingin menambahkan -d ''.
Stéphane Chazelas