Как удалить случайные строки из файла?

У меня есть файл, который содержит 10000 строк, и я хочу удалить из него 5 случайно определенных строк. Как я могу это сделать?

6
задан 15 April 2019 в 19:14

5 ответов

Вы можете использовать цикл для получения случайного числа и команду sed для удаления строки.

for i in {0..5};
 do sed -i "$((1 + RANDOM % 10000))d" filename; 
done
0
ответ дан 15 April 2019 в 19:14

Ответ на U & amp; L имеет это хорошее awk решение проблемы:

<file awk -v p=5 -v n=$(<file wc -l) '
  BEGIN {srand()}
  rand() * n-- < p {p--; next}
  {print}'

Объяснение

  • -v p=5 - множество переменная p, содержащая количество удаляемых строк
  • -v n=$(<file wc -l) - установить переменную n, содержащую количество строк в файле
  • BEGIN {srand()} - перед обработкой файла установить затравка для генерации случайных чисел, это обязательное условие для использования rand() для получения действительно ™ случайных чисел
  • rand() * n-- < p {…} - Условное выражение, запускающее деталь в фигурных скобках, если оно истинно. rand() создает случайное число между (включая) 0 и (исключая) 1, оно умножается на количество строк n, которое уменьшается на 1. Если результат меньше, чем p, выражение является истинным.
  • p--; next - уменьшить p на 1 и перейти к следующей строке, игнорируя последующие команды
  • print - напечатать текущую обработанную строку

Вторая и последняя строка сценария awk запускается для каждой строки входного файла, поэтому в каждой строке есть шанс p / n пропустить и не распечатать строку, в то время как действие по умолчанию - просто напечатать строку. 1135]

Пример выполнения

Я создал файл с буквами a – e, каждая в отдельной строке, с помощью

printf '%s\n' {a..e} >file

и установил p=1 для случайного удаления одной строки. Я изменил код, чтобы также печатать значения n и p для каждой строки, прежде чем уменьшится какое-либо из них.

$ <file awk -v n=$(<file wc -l) -v p=1 'BEGIN {srand()} {printf "n="n" p="p" "} rand() * n-- < p {p--; print ""; next} {print}'
n=5 p=1
n=4 p=0 b
n=3 p=0 c
n=2 p=0 d
n=1 p=0 e
$ <file awk -v n=$(<file wc -l) -v p=1 'BEGIN {srand()} {printf "n="n" p="p" "} rand() * n-- < p {p--; print ""; next} {print}'
n=5 p=1 a
n=4 p=1 b
n=3 p=1 
n=2 p=0 d
n=1 p=0 e
$ <file awk -v n=$(<file wc -l) -v p=1 'BEGIN {srand()} {printf "n="n" p="p" "} rand() * n-- < p {p--; print ""; next} {print}'
n=5 p=1 a
n=4 p=1 b
n=3 p=1 c
n=2 p=1 d
n=1 p=1 

Дополнительная литература

0
ответ дан 15 April 2019 в 19:14

Аналогичен ответу Шивадити, но без цикла, и удалит строки из всего файла, а не только первые 10 строк:

sed -i "$((1+RANDOM%10000))d;$((1+RANDOM%10000))d;$((1+RANDOM%10000))d;$((1+RANDOM%10000))d;$((1+RANDOM%10000))d" filename

Выберет пять случайных чисел от 1 до 10000 и удалит эти строки в одиночная операция.

0
ответ дан 15 April 2019 в 19:14

С помощью gawk поместите следующий код в файл (называемый, скажем, del_random)

function randint(n)
{
    return int(n * rand()) + 1
}

BEGINFILE {
  command = sprintf("wc -l <\"%s\"", FILENAME)
  command | getline total_lines
  srand()
  delete arr
  while (length(arr) < lines_to_del)
  {
    val = randint(total_lines)
    if (val in arr)
       continue
    arr[val] = 1
  }
}
!(FNR in arr)

, а затем выполните его как

gawk -i inplace -f del_random lines_to_del=5 file1 lines_to_del=20 file2

Любое количество файлов может быть передано (file1, file2, ...) и количество строк, подлежащих удалению, может быть указано для каждого файла отдельно с помощью параметра lines_to_del, как показано. -i inplace является gawk эквивалентом sed -i

С другой стороны, если нужно удалить одинаковое количество строк из каждого файла, вы можете установить lines_to_del один раз следующим образом:

gawk -i inplace -v lines_to_del=5 -f del_random file1 file2
0
ответ дан 15 April 2019 в 19:14

Вероятно, вы можете решить это более эффективно, чем с помощью цикла for, который должен обрабатывать весь файл один раз в строке для удаления.

filename="/PATH/TO/FILE"
number=5

line_count="$(wc -l < "$filename")"
line_nums_to_delete="$(shuf -i "1-$line_count" -n "$number")"
sed_script="$(printf '%dd;' $line_nums_to_delete)"

sed -i.bak -e "$sed_script" "$filename"

Или в одной строке (после определения переменных filename и number или их замены вручную):

sed -i.bak -e "$(printf '%dd;' $(shuf -i "1-$(wc -l < "$filename")" -n "$number"))" "$filename"

Переключатель -i.bak сообщает sed, что нужно редактировать / заменять входной файл немедленно, но сохраните резервную копию исходных данных, названную так же, как входной файл, но с добавленным к имени файла .bak Если вы не хотите, чтобы он делал копию, просто напишите -i.

Кстати, вам не нужно использовать переменные, как я. Вы также можете напрямую заменить "$number" и оба вхождения "$filename" на соответствующие значения. Я просто сделал это для ясности.


Чтобы разбить и объяснить оставшуюся часть команды:

sed -e "SCRIPT" "$filename"

запускает инструмент обработки текста sed для файла, указанного в переменной filename, применяя инструкции, заданные как SCRIPT Аргумент.

Наш SCRIPT динамически генерируется в строках над ним, которые запускают команды и присваивают свои выходы переменным. Здесь мы используем эти команды:

  • wc -l < "$filename" считывает файл, указанный в переменной filename, и выводит количество строк, содержащихся в этом файле.

    • В вашем случае это должно вернуть примерно 10000 в соответствии с размером, который вы указали в вопросе.
  • shuf -i "1-$line_count" -n "$number возвращает столько уникальных случайных чисел, сколько указано в переменной number в диапазоне от 1 до $line_count (включая обе границы).

    • Например, shuf -i 1-6 -n 2 будет подражать бросанию двух обычных шестигранных штампов.
  • printf '%dd;' ARGUMENTS возвращает форматированную строку, принимая все ARGUMENTS (на этот раз не в кавычках, чтобы рассматривать каждое случайное число как отдельный аргумент). Строка формата %dd; будет повторяться, пока остаются аргументы, а %d будет заменен аргументом, представленным в виде десятичного числа.

    • Следовательно, например, ввод 1 7 42 приведет к выводу 1d;7d;42d;.

Итоговый $sed_script, наконец, наш SCRIPT для sed. Простое число обрабатывается как адрес, то есть номер строки, к которой применяется действие, начиная с 1 для первой строки входного файла. d - это команда для удаления указанной строки, а ; разделяет несколько команд sed сценариев.

Все вместе, вся команда сначала проверяет ваш входной файл, как указано в переменной filename, и считает его строки. Затем он генерирует number множество уникальных случайных чисел в диапазоне от 1 до количества строк и из них создает скрипт sed для удаления каждой упомянутой случайной строки. Наконец, sed запускает этот скрипт в файле, модифицируя его.

0
ответ дан 15 April 2019 в 19:14

Другие вопросы по тегам:

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