比较数十万个文件并以bash创建输出结果文件的最快方法

时间:2019-07-10 18:29:51

标签: linux bash

我有以下内容:

-值文件,values.txt

-目录结构:./dataset/label/author/files.txt

-数以万计的文件.txt的

-名为target.txt的文件,其中包含每个files.txt的位置

targets.txt示例

./dataset/tallperson/Jabba/awesome.txt
./dataset/fatperson/Detox/toxic.txt

我有一个名为values.txt的文件,其中包含成千上万行值。这些值是诸如“ aef”,“; i”,“ jfk”等之类的。3个字符的随机行。

我还有成千上万个文件,每个文件也包含成百上千行。每行还包含3个字符的随机行。

使用每个files.txt的值创建了values.txt。因此,任何file.txt文件中都没有value.txt中不包含的值。 values.txt不包含重复值。

示例:

./ dataset / weirdperson / Crooked / file1.txt

LOL
hel
lo 
how
are
you
on 
thi
s f
ine
day

./ dataset / awesomeperson / Mild / file2.txt

I a
m v
ery
goo
d. 
Tha
nks
LOL

values.txt

are
you
on 
thi
s f
ine
day
goo
d. 
Tha
hel
lo 
how
I a
m v
ery
nks
LOL

以上仅是示例数据。每个文件将包含数百行。而且values.txt将包含成千上万的行。

我的目标是制作一个文件,其中每一行都是一个文件。每行将包含N个值,其中每个值对应于values.txt中的行。每个值都将用逗号分隔。每个值的计算方法很简单,即每个文件包含values.txt中每一行的值的次数。

结果应如下所示。第1行是file1.txt,第2行是file2.txt。

Result.txt

1,1,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,1,
0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,

现在。最后一件事是,在获得此结果之后,我想添加一个标签。该标签等效于文件中的第N个父目录。对于此示例,假设第二个父目录。因此,标签将为“高人”或“矮人”。结果,新的Results.txt文件将如下所示。

Results.txt

1,1,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,1,weirdperson
0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,awesomeperson

我想要一种方法来完成所有这些工作,但是当我使用超大规模数据集时,我需要它要快速。

这是我当前的代码,但是太慢了。瓶颈是第2行。

脚本。每个文件位于“ ./dataset/label/author/file.java”

1  while IFS= read file_name; do
2      cat values.txt | xargs -d '\n' -I {} grep -Fc -- "{}" "$file_name" | xargs printf "%d," >> Results.txt;
3      label=$(echo "$file_name" | cut -d '/' -f 3);
4      printf "$label\n" >> Results.txt;
5  done < targets.txt

-----------< em>-

要复制此问题。请执行以下操作:

mkdir -p dataset/{label1,label2}
touch file1.txt; chmod 777 file1.txt
touch file2.txt; chmod 777 file2.txt
echo "Enter anything here" > file1.txt
echo "Enter something here too" > file2.txt
mv file1.txt ./dataset/label1
mv file2.txt ./dataset/label2
find ./dataset/ -type f -name "*.txt" | while IFS= read file_name; do cat $file_name | sed -e "s/.\{3\}/&\n/g" | sort -u > $modified-file_name; done
find ./dataset/ -type f -name "modified-*.txt" | xargs -d '\n' -I {} echo {} >> targets.txt
xargs cat < targets.txt | sort -u > values.txt

使用上面的UNCHANGED,您应该获得一个values.txt,其中包含类似下面的内容。如果出于某些原因,任何行中的字符少于或少于3个,请删除该行。

any
e
Ent
er 
eth
he
her
ing
ng 
re 
som
thi
too

您应该获得一个target.txt文件

./dataset/label2/modified-file2.txt
./dataset/label1/modified-file1.txt

从这里开始。目的是检查targets.txt中的每个文件,并计算该文件在values.txt中包含多少个值。并将带有标签的结果输出到Results.txt

以下脚本可用于该示例,但我需要它可以更快地用于大规模操作。

while IFS= read file_name; do
  cat values.txt | xargs -d '\n' -I {} grep -Fc -- "{}" $file_name | xargs printf "%d," >> Results.txt;
  label=$(echo "$file_name" | cut -d '/' -f 3);
  printf "$label\n" >> Results.txt;
done < targets.txt

这是另一个例子

示例2:

./ dataset / weirdperson / Crooked / file1.txt

LOL
LOL
HAHA

./ dataset / awesomeperson / Mild / file2.txt

LOL
LOL
LOL

values.txt

LOL
HAHA

Result.txt

2,1,weirdperson
3,0,awesomeperson

1 个答案:

答案 0 :(得分:0)

尝试一下:

<targets.txt xargs -n1 -P4 bash -c "
  awk 'NR==FNR{a[\$0];next} {if (\$0 in a) {printf \"1,\"} else {printf \"0,\"}}' \"\$1\" values.txt |
  sed $'s\x01$\x01'\"\$(<<<\"\$1\" cut -d/ -f3)\"'\n'$'\x01'
" --

通过-P4,您可以并行化targets.txt中的作业。简短的awk脚本对行进行标记并打印0和1,后跟逗号。然后使用sed将文件夹路径的第3部分附加到该行的末尾。 sed行看起来很奇怪,因为我使用了不可打印的字符$'\x01'作为s命令的分隔符。

经过测试:

mkdir -p ./dataset/weirdperson/Crooked
cat <<EOF >./dataset/weirdperson/Crooked/file1.txt
LOL
hel
lo 
how
are
you
on 
thi
s f
ine
day
EOF

mkdir -p ./dataset/awesomeperson/Mild/
cat <<EOF >./dataset/awesomeperson/Mild/file2.txt
I a
m v
ery
goo
d. 
Tha
nks
LOL
EOF

cat <<EOF >values.txt
are
you
on 
thi
s f
ine
day
goo
d. 
Tha
hel
lo 
how
I a
m v
ery
nks
LOL
EOF

cat <<EOF >targets.txt
./dataset/weirdperson/Crooked/file1.txt
./dataset/awesomeperson/Mild/file2.txt
EOF

measure_start() {
  declare -g ttic_start
  echo "==> Test $* <=="
  ttic_start=$(date +%s.%N)
}
measure_end() {
  local end
  end=$(date +%s.%N) 
  local start   
  start="$ttic_start"
  ttic_runtime=$(python -c "print(${end} - ${start})")
  echo "Runtime: $ttic_runtime"
  echo
}

measure_start original
while IFS= read file_name; do
  cat values.txt | xargs -d '\n' -I {} grep -Fc -- "{}" $file_name | xargs printf "%d,"
  label=$(echo "$file_name" | cut -d '/' -f 3);
  printf "$label\n"
done < targets.txt
measure_end

measure_start first try with bash
nl -w1 values.txt | sort -k2.2 > values_sorted.txt
< targets.txt xargs -n1 -P0 bash -c "
  sort -t$'\t' \"\$1\" |
  join -t$'\t' -12 -21 -eEMPTY -a1 -o1.1,2.1 values_sorted.txt - |
  sort -s -n -k1.1 |
  sed 's/.*\tEMPTY/0/;t;s/.*/1/' |
  tr '\n' ',' |
  sed $'s\x01$\x01'\"\$(<<<\"\$1\" cut -d/ -f3)\"'\n'$'\x01'
" --
measure_end

measure_start second try with awk
<targets.txt xargs -n1 -P0 bash -c "
  awk 'NR==FNR{a[\$0];next} {if (\$0 in a) {printf \"1,\"} else {printf \"0,\"}}' \"\$1\" values.txt |
  sed $'s\x01$\x01'\"\$(<<<\"\$1\" cut -d/ -f3)\"'\n'$'\x01'
" --
measure_end

输出:

==> Test original <==
1,1,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,1,weirdperson
0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,awesomeperson
Runtime: 0.133769512177

==> Test first try with bash <==
0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,awesomeperson
1,1,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,1,weirdperson
Runtime: 0.0322473049164

==> Test second try with awk <==
0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,awesomeperson
1,1,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,1,weirdperson
Runtime: 0.0180222988129