sed 스크립트를 더 빠르게 실행하려면 어떻게 해야 합니까?

sed 스크립트를 더 빠르게 실행하려면 어떻게 해야 합니까?

내 관련 질문에 대한 이 스크립트를 받았습니다.csv 시작 부분에 파일 이름과 제목을 삽입하는 방법

find . -name '*.csv' -printf "%f\n" |
sed 's/.csv$//' |
xargs -I{} sed -i '1s/^/customer|/ '$'\n'' 1!s/^/{}|/' {}.csv;

현재 대용량 파일의 경우 시간이 꽤 오래 걸립니다. 파일을 50,000개로 확장하여 이 결과를 얻었습니다.

real    1m41.251s
user    0m59.326s
sys     0m38.681s

100,000개의 파일에 대해 이것을 얻습니다.

real    3m18.466s
user    1m58.451s
sys     1m16.550s

du -sh100,000개의 파일은 485M입니다. 이 데이터를 10-20GB로 확장하고 싶습니다.

위 스크립트의 속도를 높일 수 있는 방법이 있는지 궁금합니다. 나는 작업 속도를 높이기 위해 어떤 도구든 사용할 의향이 있습니다.

도움이 된다면 Ubuntu 18.04.02 LTS, 16GB RAM을 사용하고 있습니다.


사용내 질문에 대한 Ed Morton의 답변

time awk -i inplace -v OFS='|' 'FNR==1{cust=FILENAME; sub(/\.csv$/,"",cust)} {print (FNR>1 ? cust : "customer"), $0}' *.csv

real    0m20.253s
user    0m3.336s
sys     0m14.854s

sed원본보다 훨씬 빠릅니다. :o. 이유를 모르겠습니다. 누군가가 그것을 설명할 수 있다면 그것은 매우 도움이 될 것입니다.


파일을 백만 개까지 확장하면 위 스크립트에 Argument list too long.

다음과 같이 시도했지만 매우 느렸습니다.

find . -name \*.csv -exec awk -i inplace -v OFS='|' 'FNR==1{cust=FILENAME; sub(/\.csv$/,"",cust)} {print (FNR>1 ? cust : "customer"), $0}' {} \;

일괄적으로 해도 10만개 파일까지는 느린 것 같습니다.

time find . -name "10*.csv" -exec awk -i inplace -v OFS='|' 'FNR==1{cust=FILENAME; sub(/\.csv$/,"",cust)} {print (FNR>1 ? cust : "customer"), $0}' {} \;

real    9m29.474s
user    2m3.336s
sys     6m37.822s

Ed의 답변을 사용하여 일반적인 for 루프를 시도했지만 생성된 원본 파일과 동일한 속도로 작동하는 것 같았습니다. 100만 개의 레코드를 생성하는 데 약 40분이 소요되었습니다.

for file in *.csv; do
    echo "$file"
    awk -i inplace -v OFS='|' 'FNR==1{cust=FILENAME; sub(/\.csv$/,"",cust)} {print (FNR>1 ? cust : "customer"), $0}' "$file"
done

나는 100,000개의 파일마다 일괄 처리를 시도했는데 lsEd xargs가 제공한 초기 솔루션 이후 합리적으로 보입니다.

time ls 11*.csv | xargs awk -i inplace -v OFS='|' 'FNR==1{cust=FILENAME; sub(/\.csv$/,"",cust)} {print (FNR>1 ? cust : "customer"), $0}'

real    0m23.619s
user    0m3.537s
sys     0m15.272s

time ls 12*.csv | xargs awk -i inplace -v OFS='|' 'FNR==1{cust=FILENAME; sub(/\.csv$/,"",cust)} {print (FNR>1 ? cust : "customer"), $0}'

real    0m25.044s
user    0m3.892s
sys     0m16.261s

time ls 13*.csv | xargs awk -i inplace -v OFS='|' 'FNR==1{cust=FILENAME; sub(/\.csv$/,"",cust)} {print (FNR>1 ? cust : "customer"), $0}'

real    0m24.997s
user    0m4.035s
sys     0m16.757s

현재 계획은 일괄 처리를 위해 for 루프를 사용하여 위의 솔루션을 사용하는 것입니다. 각 배치의 평균 시간을 25초로 가정하면 약 25*10 -> 4분 정도 소요됩니다. 수백만 건의 기록이 빠른 것 같아요.

누구든지 더 나은 해결책이 있으면 알려주십시오. 위에 작성된 코드 중 잘못된 것/잘못된 코드가 있으면 알려주세요. 아직 초보자라 복사나 이해가 제대로 되지 않을 수도 있습니다.

답변1

$ awk -v OFS=',' 'FNR==1{cust=FILENAME; sub(/\.csv$/,"",cust)} {print (FNR>1 ? cust : "customer"), $0}' 10000000.csv
customer,first_name,middle_name,last_name,gender,email,phone_number,address,city,state,country,date_order_start,date_order_complete,invoice_number,invoice_date,item,item_price,quantity,cost,job_name,job_price,total_cost
10000000,Chae,Jesusa,Cummings,Female,[email protected],555-555-8750,911 Hauser Pike,Moline,Georgia,Cameroon,2016-06-29,2016-07-16,36298,2016-07-17,Acer,493.86,14,354.77,Broken,123.68,898.13

따라서 awk에 대해 다음을 수행할 수 있습니다.

for file in *.csv; do
    awk 'script' "$file" > tmp && mv tmp "$file"
done

또는 "내부" 편집을 위해 GNU awk를 사용하십시오.

$ tail -n +1 10000000.csv 10000001.csv
==> 10000000.csv <==
first_name,middle_name,last_name,gender,email,phone_number,address,city,state,country,date_order_start,date_order_complete,invoice_number,invoice_date,item,item_price,quantity,cost,job_name,job_price,total_cost
Chae,Jesusa,Cummings,Female,[email protected],555-555-8750,911 Hauser Pike,Moline,Georgia,Cameroon,2016-06-29,2016-07-16,36298,2016-07-17,Acer,493.86,14,354.77,Broken,123.68,898.13

==> 10000001.csv <==
first_name,middle_name,last_name,gender,email,phone_number,address,city,state,country,date_order_start,date_order_complete,invoice_number,invoice_date,item,item_price,quantity,cost,job_name,job_price,total_cost
Fleta,Rosette,Hurley,Other,[email protected],1-555-555-1210,35 Freelon Arcade,Beaverton,Rhode Island,Cayman Islands,2009-06-08,2009-06-29,39684,2009-07-01,NVIDIA GeForce GTX 980,474.31,16,395.79,Broken,157.53,1088.04
Bennett,Dennis,George,Male,[email protected],(555) 555-4131,505 Robert C Levy Arcade,Wellington,Louisiana,Mexico,2019-05-09,2019-05-19,37938,2019-05-21,8GB,187.67,16,205.77,Service,170.21,1007.85
Tommye,Pamula,Diaz,Other,[email protected],555.555.4445,1001 Canby Boulevard,Edinburg,Massachusetts,Gambia,2004-05-02,2004-05-24,31364,2004-05-26,Lenovo,137.21,13,193.63,Replacement,246.43,934.31
Albert,Jerrold,Cohen,Other,[email protected],+1-(555)-555-8491,1181 Baden Avenue,Menomonee Falls,Texas,Tajikistan,2019-08-03,2019-08-12,37768,2019-08-15,Intel® Iris™ Graphics 6100,396.46,17,223.02,Service,118.53,960.27
Louetta,Collene,Best,Fluid,[email protected],1-555-555-7050,923 Barry Viaduct,Laurel,Illinois,St. Barthélemy,2009-03-02,2009-03-06,39557,2009-03-07,AMD Radeon R9 M395X,133.9,11,198.49,Fix,178.54,1055.32
Kandace,Wesley,Diaz,Female,[email protected],+1-(555)-555-5414,341 Garlington Run,Santa Maria,New Jersey,Mexico,2005-10-09,2005-10-10,30543,2005-10-14,Samsung,590.29,5,354.85,Service,292.56,1032.22

.

$ awk -i inplace -v OFS=',' 'FNR==1{cust=FILENAME; sub(/\.csv$/,"",cust)} {print (FNR>1 ? cust : "customer"), $0}' 10000000.csv 10000001.csv

.

$ tail -n +1 10000000.csv 10000001.csv
==> 10000000.csv <==
customer,first_name,middle_name,last_name,gender,email,phone_number,address,city,state,country,date_order_start,date_order_complete,invoice_number,invoice_date,item,item_price,quantity,cost,job_name,job_price,total_cost
10000000,Chae,Jesusa,Cummings,Female,[email protected],555-555-8750,911 Hauser Pike,Moline,Georgia,Cameroon,2016-06-29,2016-07-16,36298,2016-07-17,Acer,493.86,14,354.77,Broken,123.68,898.13

==> 10000001.csv <==
customer,first_name,middle_name,last_name,gender,email,phone_number,address,city,state,country,date_order_start,date_order_complete,invoice_number,invoice_date,item,item_price,quantity,cost,job_name,job_price,total_cost
10000001,Fleta,Rosette,Hurley,Other,[email protected],1-555-555-1210,35 Freelon Arcade,Beaverton,Rhode Island,Cayman Islands,2009-06-08,2009-06-29,39684,2009-07-01,NVIDIA GeForce GTX 980,474.31,16,395.79,Broken,157.53,1088.04
10000001,Bennett,Dennis,George,Male,[email protected],(555) 555-4131,505 Robert C Levy Arcade,Wellington,Louisiana,Mexico,2019-05-09,2019-05-19,37938,2019-05-21,8GB,187.67,16,205.77,Service,170.21,1007.85
10000001,Tommye,Pamula,Diaz,Other,[email protected],555.555.4445,1001 Canby Boulevard,Edinburg,Massachusetts,Gambia,2004-05-02,2004-05-24,31364,2004-05-26,Lenovo,137.21,13,193.63,Replacement,246.43,934.31
10000001,Albert,Jerrold,Cohen,Other,[email protected],+1-(555)-555-8491,1181 Baden Avenue,Menomonee Falls,Texas,Tajikistan,2019-08-03,2019-08-12,37768,2019-08-15,Intel® Iris™ Graphics 6100,396.46,17,223.02,Service,118.53,960.27
10000001,Louetta,Collene,Best,Fluid,[email protected],1-555-555-7050,923 Barry Viaduct,Laurel,Illinois,St. Barthélemy,2009-03-02,2009-03-06,39557,2009-03-07,AMD Radeon R9 M395X,133.9,11,198.49,Fix,178.54,1055.32
10000001,Kandace,Wesley,Diaz,Female,[email protected],+1-(555)-555-5414,341 Garlington Run,Santa Maria,New Jersey,Mexico,2005-10-09,2005-10-10,30543,2005-10-14,Samsung,590.29,5,354.85,Service,292.56,1032.22

명령줄에 전달할 파일이 너무 많고 xargs를 통해 실행하는 것이 너무 느린 경우 다른 옵션이 있습니다.

awk -i inplace ... '
    BEGIN {
        while ( (getline line < ARGV[1]) > 0 ) {
            if ( line ~ /\.csv$/ ) {
                ARGV[ARGC] = line
                ARGC++
            }
        }
        ARGV[1] = ""
    }
    { the "real" script }
' <(ls)

위의 코드는 ls인수 대신 입력 파일로 의 출력을 읽고 로 끝나는 파일 이름으로 인수 배열을 채운 .csv다음 명령줄에서 인수로 전달된 것처럼 파일에 대해 작동합니다.

답변2

다음 두 가지 방법을 시도해 볼 수 있습니다.

$ find . -name \*.csv -type f ! -empty -exec \
  perl -spe 's/^/,/;
   $F //= $ARGV =~ s/\.csv$//r;
   s/^/$. == 1 ? "\n$C" : $F/e;
   undef $F, close ARGV if eof;
' -- -C="Customer" {} +

두 번째는 Gnu sed 기능을 활용합니다. 특히 F 명령을 사용하여 파일 이름을 가져오고 -s 옵션을 사용하여 여러 파일을 단일 스트림이 아닌 개별적으로 처리합니다.

$ find . -name \*.csv -type f ! -empty -exec \
  sed -se 'F;1s/^/CUSTOMER,/' {} + |
  sed -E \
    -e 'N;s/.*\.csv(\nCUSTOMER,)/\1/;t' \
    -e 's/\.csv\n/,/;s/..//' \
;

관련 정보