Первое решение, которое я попробовал (ниже есть более быстрая альтернатива )похоже на то, что представил @steeldriver. Однако значения в файле2 должны иметь начальную точку, чтобы избежать совпадения строки типа a.bb.com
в cc.aa.bb.com
. Совпадения должны иметь точку в качестве разделителя. Делаем это в четыре шага:
n=100
echo "step1 ==============="
time head -n $n file2 | sort | tee file222 | sed 's/\./\\./g;s/^/\\./;s/$/$/' >file22
echo "step2 ==============="
time sed 's/^/./' file1 | head -n $n > file11
echo "step3 ==============="
time grep -oEf file22 file11 | sort -u | sed 's/^\.//' >file33
echo "step4 ==============="
time comm -13 file33 file222 > fileout
Но время увеличивается как квадрат n
, это довольно быстро для менее 1000
строк (обоих файлов ). Но он растет на порядок 475
дней (больше, чем за год )на 1 миллион строк. Явно не жизнеспособное решение.
Вариант б
Не очень интуитивный вариант — развернуть файл1 на все его составляющие.
Процесс, аналогичный расширению aa.ff.bb.com
до:
aa.ff.bb.com
ff.bb.com
bb.com
com
А затем, после удаления повторяющихся строк в этом файле, найти все строки, существующие только в (отсортированном )файле2.
Этап сортировки (и удаления повторов )занимает больше всего времени, но, поскольку он составляет порядка 8 секунд для файлов с 1 миллионом (уникальных )строк, это вполне разумно.
Весь процесс (, включая создание исходных файлов ), является:
#!/bin/bash
TIMEFORMAT='%R %U %S'
echo $'bb.com\na.com\n123.com' >file2
printf '%s\n' {a..z}{a..m}.{a..z}{a..m}.{com,net,dot} >>file2
echo $'aa.bb.com\naa.ff.bb.com\naa.bb.cc.com\na.com' >file1
printf '%s\n' {h..k}.{e..z}{a..m}.{e..z}{a..m}.{com,net,dot} >>file1
echo "file2 has $(wc -l <file2) lines"
echo "file1 has $(wc -l <file1) lines"
n=10000000
time sed -n 'p;:1;s/[^.]*\.//p;t1' file1 >file1b1
echo "file1b1 has $(wc -l <file1b1) lines"
time sort -u file1b1 | head -n $n >file1b2
echo "file1b2 has $(wc -l <file1b2) lines"
time sort -u file2 | head -n $n >file2b2
time comm -13 file1b2 file2b2 >fileout
Это печатает эти результаты:
file2 has 342735 lines
file1 has 981556 lines
4.353 4.248 0.096
file1b1 has 3926221 lines
8.649 15.024 0.488
file1b2 has 1227809 lines
0.618 0.908 0.112
1.011 0.968 0.032
Порядка 15 секунд.
for file in TLC*.csv; do
cut -d, -f2- "${file}" > "${file%.*}_prepped.csv"
done
${file%.*}
удаляет все после последней точки $file
. Если вы хотите удалить все после первой точки , используйте %%
.
Аналогично,${file#*.}
(${file##*.}
)удаляет все до первой (последней )точки.
Подробнее см. Расширение параметров, руководство Bash .
И помните, всегда цитируйте свои переменные . Вы можете использоватьshellcheck
для облегчения отладки ваших скриптов. Это предупредит вас о переменных без кавычек.
При циклическом переборе файлов с разными расширениями целевое расширение файла не может быть жестко закодировано, как это сделано выше, поэтому необходимо ${file##*.}
. В качестве минимального примера, который вы можете попробовать в пустом тестовом каталоге, это делает копию _prepped
для каждого файла с расширением:
touch A.file.txt B.file.dat C_file.csv D_file.ko E
for file in *.*; do
noext=${file%.*}
ext=${file##*.}
cp "$file" "${noext}_prepped.${ext}"
done
После казни,
$ ls
A.file_prepped.txt A.file.txt B.file.dat B.file_prepped.dat
C_file.csv C_file_prepped.csv D_file.ko D_file_prepped.ko E