스크립트에 의해 생성된 1MB에서 6GB 크기의 CSV 파일이 여러 개 있고 inotify
이벤트 목록 형식은 다음과 같습니다
timestamp;fullpath;event;size
.
이러한 파일의 형식은 다음과 같습니다.
timestamp;fullpath;event;size
1521540649.02;/home/workdir/ScienceXMLIn/config.cfg;IN_OPEN;2324
1521540649.02;/home/workdir/ScienceXMLIn/config.cfg;IN_ACCESS;2324
1521540649.02;/home/workdir/ScienceXMLIn/config.cfg;IN_CLOSE_NOWRITE;2324
1521540649.02;/home/workdir/quad_list_14.json;IN_OPEN;2160
1521540649.03;/home/workdir/quad_list_14.json;IN_ACCESS;2160
1521540649.03;/home/workdir/quad_list_14.json;IN_CLOSE_NOWRITE;2160
1521540649.03;/home/workdir/ScienceXMLIn/masterbias_list.asc;IN_OPEN;70
1521540649.03;/home/workdir/ScienceXMLIn/masterbias_list.asc.1;IN_OPEN;80
1521540649.03;/home/workdir/ScienceXMLIn/masterbias_list.asc.2;IN_OPEN;70
1521540649.03;/home/workdir/otherfolder/quad_list_14.json;IN_OPEN;2160
1521540649.03;/home/workdir/otherfolder/quad_list_14.json;IN_CLOSE_NOWRITE;2160
내 목표는 다른 폴더에 나타나는 동일한 이름의 파일을 식별하는 것입니다.
이 예에서는 파일이 및 quad_list_14.json
모두에 나타납니다 ./home/workdir/otherfolder
/home/workdir/
내가 원하는 출력은 간단합니다. 여러 폴더에 나타나는 파일 목록입니다. 이 경우 다음과 같습니다.
quad_list_14.json
이를 위해 저는 다음과 같은 작은 코드를 작성했습니다.
#this line cut the file to only get unique filepath
PATHLIST=$(cut -d';' -f 2 ${1} | sort -u)
FILENAMELIST=""
#this loop build a list of basename from the list of filepath
for path in ${PATHLIST}
do
FILENAMELIST="$(basename "${path}")
${FILENAMELIST}"
done
#once the list is build, I simply find the duplicates with uniq -d as the list is already sorted
echo "${FILENAMELIST}" | sort | uniq -d
집에서는 이 코드를 사용하지 마세요. 안타깝습니다. 이 스크립트를 다음과 같은 온라인 사용자로 바꿔야 합니다.
#this get all file path, sort them and only keep unique entry then
#remove the path to get the basename of the file
#and finally sort and output duplicates entry.
cut -d';' -f 2 ${1} | sort -u | grep -o '[^/]*$' | sort | uniq -d
내 문제는 여전히 남아 있습니다. 파일 수가 많아 SSD에서는 최소 0.5초가 걸리지만 다른 폴더에서 중복된 파일 이름을 찾는 데는 최대 45초(제 프로덕션 디스크는 그리 빠르지 않습니다)가 걸립니다.
이 코드를 더 효율적으로 만들기 위해 개선해야 합니다. 나의 유일한 한계는 파일을 RAM에 완전히 로드할 수 없다는 것입니다.
답변1
다음 AWK 스크립트는 너무 많은 메모리를 사용하지 않고 이 문제를 해결해야 합니다.
#!/usr/bin/awk -f
BEGIN {
FS = ";"
}
{
idx = match($2, "/[^/]+$")
if (idx > 0) {
path = substr($2, 1, idx)
name = substr($2, idx + 1)
if (paths[name] && paths[name] != path && !output[name]) {
print name
output[name] = 1
}
paths[name] = path
}
}
각 파일의 경로와 이름을 추출하고 각 이름의 마지막 경로를 저장합니다. 이전에 다른 경로를 본 경우 이미 출력되지 않은 경우 해당 이름을 출력합니다.
답변2
코드의 주요 문제는 변수의 모든 경로 이름을 수집한 다음 이를 반복하여 호출한다는 것입니다 basename
. 이로 인해 속도가 느려집니다.
루프는 따옴표가 없는 변수 확장에서도 작동하는데 ${PATHLIST}
, 이는 경로 이름에 공백이나 쉘 와일드카드가 포함되어 있으면 현명하지 않습니다. (또는 이를 지원하는 다른 쉘) 에서는 bash
대신 배열을 사용합니다.
제안:
$ sed -e '1d' -e 's/^[^;]*;//' -e 's/;.*//' file.csv | sort -u | sed 's#.*/##' | sort | uniq -d
quad_list_14.json
첫 번째는 sed
경로 이름을 선택하고 헤더 행을 삭제합니다. 이는 awk -F';' 'NR > 1 { print $2 }' file.csv
, 또는 로 쓸 수도 있습니다 tail -n +2 file.csv | cut -d ';' -f 2
.
sort -u
고유한 경로명이 주어지며 기본 sed
이름은 아래와 같습니다. 끝에 sort
있는 기호는 uniq -d
어떤 기본 이름이 중복되었는지 알려줍니다.
sed 's#.*/##'
기본 이름을 제공하는 마지막 함수는 매개변수 확장을 ${pathname##*/}
연상 시킵니다. 이는 문자열에서 마지막 항목까지의 모든 항목을 $( basename "$pathname" )
제거합니다 ./
basename
코드와의 주요 차이점은 여러 호출에 루프를 사용하는 대신 sed
단일 루프를 사용하여 경로 이름 목록에서 기본 이름을 생성한다는 것입니다.
항목 만 보는 IN_OPEN
대신 :
sed -e '/;IN_OPEN;/!d' -e 's/^[^;]*;//' -e 's/;.*//' file.csv | sort -u | sed 's#.*/##' | sort | uniq -d
답변3
답변을 주신 두 분 모두에게 감사드리며, 의견을 주신 Isaac에게도 감사드립니다.
귀하의 모든 코드를 가져와서 스크립트에 넣은 후 stephen.awk
kusa.sh
다음 isaac.sh
과 같은 작은 벤치마크를 실행했습니다.
for i in $(ls *.csv)
do
script.sh $1
done
명령을 사용하여 time
비교했는데 결과는 다음과 같습니다.
stephen.awk
real 2m35,049s
user 2m26,278s
sys 0m8,495s
stephen.awk: 두 번째 블록 앞의 /IN_OPEN/으로 업데이트
real 0m35,749s
user 0m15,711s
sys 0m4,915s
kusa.sh
real 8m55,754s
user 8m48,924s
sys 0m21,307s
필터로 업데이트 IN_OPEN
:
real 0m37,463s
user 0m9,340s
sys 0m4,778s
참고 사항:
정확하지만 빈 줄을 많이 출력했지만 sed
귀하의 스크립트는 이와 같은 유일한 것입니다.
isaac.sh
grep -oP '^[^;]*;\K[^;]*' file.csv | sort -u | grep -oP '.*/\K.*' | sort | uniq -d
real 7m2,715s
user 6m56,009s
sys 0m18,385s
필터가 켜져 있을 때 IN_OPEN
:
real 0m32,785s
user 0m8,775s
sys 0m4,202s
내 스크립트
real 6m27,645s
user 6m13,742s
sys 0m20,570s
@Stephen 당신은 시간을 2.5배 단축하여 확실히 이겼습니다. 이는 인상적입니다.
하지만 잠시 생각한 끝에 또 다른 아이디어가 떠올랐습니다. OPEN 파일 이벤트만 보면 복잡성이 줄어들고 먼저 파일을 열지 않고는 파일에 액세스하거나 파일에 쓸 수 없어야 한다는 것이었습니다. 그래서 이렇게 했습니다. 완료:
#see I add grep "IN_OPEN" to reduce complexity
PATHLIST=$(grep "IN_OPEN" "${1}" | cut -d';' -f 2 | sort -u)
FILENAMELIST=""
for path in ${PATHLIST}
do
FILENAMELIST="$(basename "${path}")
${FILENAMELIST}"
done
echo "${FILENAMELIST}" | sort | uniq -d
이 수정만으로 동일한 결과를 얻을 수 있으며 결국 다음 time
값이 됩니다.
real 0m56,412s
user 0m27,439s
sys 0m9,928s
나는 내가 할 수 있는 다른 일들이 많이 있다고 확신한다.