
スクリプトによって生成された1 MBから6 GBのサイズの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:2番目のブロックの前の/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
私はできることがたくさんあると確信しています。