Добавление суффикса к имени файла во время цикла for в bash

Первое решение, которое я попробовал (ниже есть более быстрая альтернатива )похоже на то, что представил @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 секунд.

5
23.07.2020, 23:21
1 ответ
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
10
18.03.2021, 23:17

Теги

Похожие вопросы