파일은 특정 시간 창(동부 표준시 자정 0600) 내에 도착하고 Linux 호스트의 주문 디렉터리에 저장됩니다. 이러한 파일은 zip 형식으로 저장됩니다. 일반적으로 이 기간 동안 근무일당 약 30개의 파일을 받을 것으로 예상됩니다.
이 기간 동안 추가 파일도 나타나며 동일한 zip 파일 이름으로 동일한 주문 디렉터리에 저장됩니다. 따라서 가장 먼저 해야 할 일은 zip 파일의 압축을 풀고 관심 있는 파일(desc1234.NNN)이 이러한 zip 파일에 있는지 확인하는 것입니다.
다음 요구 사항은 파일의 특정 필드(특정 레코드 유형의 경우 파일의 필드 위치: 20-34 - 999)에 대한 시간 도착 창 이후에 이러한 30개 파일을 모두 구문 분석하는 것입니다. 파일에는 여러 레코드 유형이 있습니다(다른 레코드 유형 외에도).
이러한 파일(alt_ord_id)에서 구문 분석된 필드를 사용하여 ord_id
Oracle 데이터베이스의 주문 테이블에서 특정 열을 조회하고 ord_id가 alt_ord_id 및 파일 이름과 함께 표시됩니다.
Oracle Db 테이블 구조는 다음과 같습니다(관심 있는 2개 열만 표시됨).
sql>desc 명령어;
ord_id varchar2(15); alt_ord_id varchar2(15);
예상 최종 O/p:
파일 이름Alt_ord_id Ord_id
desc1234.001 123456789012345 ABCDEFGHIJKLMNO desc1234.002 234567890123456 BCDEFGHIJKLMNOP
이러한 일일 파일은 약 30개 정도이고 각 파일에는 약 10~15개의 레코드 유형이 포함되어 총 999개가 있을 것으로 예상됩니다.
- 목차:
orders
- 압축 파일 이름:
customer_orders_YYYYMMDDHHMISS.zip
- 파일 이름: ( 는 일련 번호
desc1234.NNN
입니다 )NNN
- 파일 형식: 고정 너비 텍스트
다음 명령을 사용하여 zip 파일에서 파일 이름을 읽어 파일이 포함되어 있는지 확인합니다.
desc1234.NNN
for zip_file in *.zip; do if [$(unzip -l $zip_file|grep -c "desc1234") -ne 0]; then filename="`zipinfo -1 customer_orders.zip`"; echo $filename; fi; done
산출:
desc1234.001
명령 #2
cat desc1234.123|grep "999"|cut -c 20-34|awk 'BEGIN {printf("(")} (for i=1; i<=NF;i++) { printf("'\''%s'\'',",$i} } '|awk '{gsub(/,$/,")"}; print}'> test2.txt
산출:
('123456789012345','234567890123456','345678901234567')
최종 문자열을 보내려면 위의 (1)과 (2)를 결합해야 할 것 같습니다.
('123456789012345','234567890123456','345678901234567')
Oracle 데이터베이스에서 테이블을 찾습니다.
오전 12시부터 오전 6시 사이에 처리된 파일을 찾을 때 (1)과 (2)의 조합을 얻으려면 어떻게 해야 합니까?
입력 파일:
001ORDERS20211117 72222 ORDERS CYCLE 001 202111170320
001 124 ABC XYZ
002 231 WASHING CYCLE
999 456 YUIHY 123456789012345
011 123 ABCD XYZ
012 786 MACHINE
999 654 234567890123456
답변1
시작점으로 아래와 같은 스크립트를 사용해 보세요.
awk
그런데, 어떤 유형의 UNIX를 실행하고 있는지, 어떤 셸을 사용하고 있는지, 어떤 버전을 사용하고 있는지는 밝히지 않으셨습니다 . 나는 당신이 Linux(또는 GNU 핵심 유틸리티가 설치된 다른 시스템), bash 및 GNU awk를 실행하고 있다고 가정합니다. 게다가정보 압축. unzip
이러한 가정이 잘못된 경우 시스템에 맞게 bash+awk 스크립트를 조정해야 합니다.
$ cat process-zip-files.sh
#!/bin/bash
# create a temporary directory
# mktemp is in GNU coreutils
td="$(mktemp -d)"
for zf in *.zip; do
# unzip options: -qq = very quiet, -o = don't prompt for overwrite,
# -d = directory to unzip files into.
unzip -qq -o -d "$td" "$zf" '*.[0-9][0-9][0-9]'
done
# Process each unzipped text file individually
# This awk script requires GNU awk. The ENDFILE pattern
# is a GNU extension to awk.
awk '/^999 / { data[i++] = substr($0,20,34) };
ENDFILE {
out="";
for (i in data) { out = out data[i] "," };
sub(/,$/,"",out);
print "(" out ")";
delete data;
}' "$td/"*
# delete the temporary directory and everything in it
rm -rf "$td/"
999
이 스크립트는 각 텍스트 파일에 최소한 하나의 레코드가 있다고 가정합니다. 그렇지 않은 경우 배열의 ENDFILE 블록에 요소가 하나 이상 있는지 확인해야 합니다. 그렇지 않으면 해당 텍스트 파일에 대해 한 줄만 출력됩니다 data
.()
이는 오류 확인이나 처리를 수행하지 않고 예외나 복잡한 상황을 처리하려고 시도하지 않는 최소한의 스크립트입니다.
예제 출력(예제 텍스트가 포함된 zip 파일을 생성한 후):
$ ./process-zip-files.sh
(123456789012345,234567890123456)
더 나은 스크립트는 를 사용하는 것입니다 perl
. 이는 Oracle 데이터베이스에 대한 연결을 설정합니다(open을 사용하여).데이터베이스 인터페이스그리고DBD::오라클.
그런 다음 오늘의 zip 파일 배치를 엽니다(다음을 사용).아카이브::우편번호모듈) 및 그 안에 있는 각 .NNN 텍스트 파일을 처리합니다. 텍스트 파일의 데이터를 사용하여 SQL 문을 구성하고 이를 Oracle DB로 보냅니다.
명령문은 검색, 삽입, 업데이트, 삭제 등 일반적으로 SQL을 사용하여 수행할 수 있는 모든 작업이 될 수 있습니다.
python
또 다른 좋은 구현 언어가 될 것입니다. Oracle과 같은 데이터베이스 및 zip 파일과 상호 작용하기 위한 라이브러리 모듈도 있습니다.
거기예Bash나 다른 쉘에서 직접 Oracle 및 기타 SQL 데이터베이스와 상호 작용할 수 있는 방법이 있습니다. 하지만 수행해야 하는 공백과 인용의 양이 너무 많아서 짜증나고 지루하며 작은 문제가 발생하기 쉬운 프로그래밍 작업입니다. 작업을 수행하는 데 필요한 최소한의 Perl(또는 Python) 하위 집합을 배우는 것이 훨씬 쉽습니다... 그리고 한 번 수행한 후에는 나중에 유사한 작업을 쉽게 수행할 수 있습니다.
거의 새벽 2시이고 자야 하기 때문에 이 시점에서는 그러한 스크립트의 기본 버전을 작성할 시간조차 없습니다. 어쨌든 귀하의 질문은 Oracle에서 데이터를 처리하는 방법에 대한 세부 정보를 실제로 제공하지 않습니다.
추신: 귀하의 질문에 게시한 두 개의 쉘 코드 예제를 보면 한 줄의 코드로 작업을 수행하는 것을 좋아하는 것 같습니다. Quip은 유용하지만 항상 문제에 대한 최선의 해결책은 아니며 일반적으로 나쁜 해결책입니다. awk 또는 Perl과 같은 언어로 스크립트를 작성하는 것을 두려워하지 마십시오. 독립형으로 사용하거나 쉘 스크립트의 일부로 스크립트를 사용하는 것이 Unix와 Linux가 사용되는 방식입니다.
많은 명령으로 구성된 길고 복잡한 파이프라인을 사용하여 셸에서 데이터 처리를 시도하는 것은 awk 또는 Perl로 사용자 정의 도구를 작성하는 것보다 확실히 더 어려울 것이며 셸 파이프라인은 더 취약할 가능성이 높습니다. 확실히 몇 배 더 느려집니다. 작은 데이터 파일과 간단한 처리 작업의 경우 성능이 중요하지 않을 수 있습니다. 대량의 데이터 및/또는 복잡한 처리의 경우 이는 실행 시간이 몇 초에서 몇 시간 사이의 차이를 의미할 수 있습니다.
Archive::Zip
다음은 Perl에서 DBI
동일한 작업을 수행하기 위해 and/또는 & 모듈을 사용하는 몇 가지 예 입니다 DBD
. 이러한 Perl 스크립트는 .zip 아카이브에서 직접 일치하는 파일을 읽기 때문에 .zip 파일을 추출할 임시 디렉토리가 필요하지 않습니다.
첫 번째 예는 bash + awk 스크립트의 기능을 복제합니다.
$ cat process-zip-files.pl
#!/usr/bin/perl
use strict;
use Archive::Zip;
# First arg is the source directory. defaults to ./
my $dir = shift // '.';
foreach my $zf (glob "$dir/*.zip") {
# open the zip file
my $zip = Archive::Zip->new($zf);
# get the list of files ending with a dot and at least one digit
my @txt = grep { /\.\d+$/ } $zip->memberNames();
# iterate over each matching filename
foreach my $f (@txt) {
my @data = ();
# Iterate over each line of the file ($f). This code is fine
# for smallish files, but it would be better to use the
# Archive::Zip::MemberRead module for large files to avoid
# reading the entire file into memory at once.
foreach (split /\n/, $zip->contents($f)) {
if (m/^999\s/) {
# perl substr offsets start at 0, not 1. So the
# next line grabs 15 chars, starting from char 20
# and adds the string to the @data array.
push @data, substr($_,19,15);
}
};
# Now do something with the data from this file
@data = map { "'$_'" } @data; # quote each element of @data
print "(", join(",",@data), ")\n";
} # end of current member file
} # end of current zipfile
$ ./process-zip-files.pl
('123456789012345','234567890123456')
단순히 데이터를 인쇄하는 대신 데이터베이스와 직접 상호 작용할 수 있습니다. 여기서는 모호하고 다소 쓸모없는 예만 제시할 수 있습니다. 왜냐하면 데이터베이스 테이블 구조가 어떤지, .NNN 파일에서 추출된 데이터로 실제로 무엇을 하려는지 모르기 때문입니다.
$ cat process-zip-files-sql.pl
#!/usr/bin/perl
use strict;
use Archive::Zip;
use Archive::Zip::MemberRead;
use DBI;
# First arg is the source directory. defaults to ./
my $dir = shift // '.';
# I don't have Oracle, and I couldn't be bothered setting up
# a database, table, and login account on mysql or postgres
# for this example, so I'll use SQLite. Other databases are
# just as easy to connect to, but the connect() call will
# require other details like hostname, port, login, password,
# etc.
#
# Set up a database handle ($dbh) to the sqlite db called
# "notoracle.sqlite3":
my $dbname='notoracle.sqlite3';
my $dbh = DBI->connect("dbi:SQLite:dbname=$dbname","","");
foreach my $zf (glob "$dir/*.zip") {
my $zip = Archive::Zip->new($zf);
my @txt = grep { /\.\d+$/ } $zip->memberNames();
foreach my $f (@txt) {
my @data = ();
# This example uses Archive::Zip::MemberRead, just to show
# how to use it.
my $fh = Archive::Zip::MemberRead->new($zip, $f);
while (defined(my $l = $fh->getline())) {
if ($l =~ m/^999\s/) {
push @data, substr($l,19,15);
}
};
$fh->close();
# Example 1: print matching records (each element needs to be
# quoted when using IN, can't use placeholders):
my @qdata = map { "'$_'" } @data; # quote each element of @data
my $values = join(",",@qdata);
my $sql = "select * from mytable where myfield in ($values)";
print join(",", $dbh->selectrow_array($sql)),"\n";
# Example 2 - using a placeholder ?, one element of @data
# at a time. There is no need to quote each element of
# the @data array because placeholders handle quoting
# automagically if and when required, depending on the data
# type of the database field.
my $sth = $dbh->prepare('select * from mytable where myfield = ?');
foreach my $d (@data) {
while (my @row = $sth->fetchrow_array($sql,undef,$d)) {
print join(",",@row), "\n";
}
}
} # end of current member file
} # end of current zipfile
이 예제는 기능적이지 않은 개념적 예제이므로 예제 출력이 없습니다. 같은 이유로 이 코드는 테스트되지 않았으며 사소한 버그가 포함될 수 있습니다. 잘 컴파일되지만 perl -w -c process-zip-files-sql.pl
실제로 작동하거나 유용한 작업을 수행한다는 보장은 없습니다.