Como posso excluir várias linhas aleatórias de um arquivo de texto usando sed?

3

Eu quero excluir 10 linhas aleatórias de um arquivo de texto que tenha 90 linhas e, em seguida, imprima isso em um novo arquivo. Eu tenho tentado fazer isso usando sed, mas tenho dois problemas. Estou usando:

sed -i $((1 + RANDOM & 90))d input.txt > output.txt

e, em seguida, executando o comando 10 vezes (suponho que haja uma maneira melhor de fazer isso!)

O primeiro problema que tenho é que recebo o erro:

sed: -e expressão # 1, char 2: uso inválido do endereço de linha 0

Suponho que isso tenha algo a ver com o fato de já ter excluído a linha 1 e estar tentando novamente.

O segundo problema é que, às vezes, nada é gravado no arquivo de saída, mesmo que tenha funcionado antes de usar o mesmo comando.

    
por Amanda 20.11.2015 / 15:52

4 respostas

6

Você provavelmente queria usar RANDOM % 90 em vez de & . É daí que vêm os zeros (a exclusão da linha 1 é OK, na próxima execução, as linhas serão numeradas 1 .. 89).

Há um problema, no entanto: A fórmula pode gerar o mesmo número várias vezes. Para evitar isso, use uma abordagem diferente: embaralhe os números e escolha os dez primeiros:

shuf -i1-90 -n10 | sed 's/$/d/' | sed -f- input > output

Se você não gostar de sed gerando um script sed , também poderá usar printf :

sed -f <( printf %dd\;  $(shuf -i1-90 -n10) ) input > output
    
por 20.11.2015 / 16:25
3

Se você não tem o GNU shuf , portably, você poderia fazer:

awk -v n=90 -v p=10 '
  BEGIN {srand()}
  rand() * n-- < p {p--; next}
  {print}' < file

Ela também será mais eficiente que a abordagem shuf + sed com valores altos de p , já que está em o (n), enquanto shuf + sed está em o (n * p). Com n = 1000000, o ponto de ruptura no meu sistema é em torno de p = 35 com GNU sed vs GNU awk e com p = 1 com GNU sed vs mawk (como em mawk é sempre mais rápido).

    
por 20.11.2015 / 19:29
0

se não houver problema com o desempenho, use isto:

cat PATH_OF_SOURCE_FILE | \
grep -n ^ | \
grep -E "^($(seq 1 90 | shuf | head -n 80 | paste -s -d '|')):" | \
sed 's/[0-9]*:\(.*\)$//' > PATH_TO_TARGET_FILE

primeiro grep linhas de índice; o segundo grep seleciona 80 linhas aleatórias, e sed remove o número da linha adicionado pelo primeiro grep .

Nota: canalize a última saída para shuf se você não precisar da ordem de saída

    
por 20.11.2015 / 23:02
-1

Acho que o desafio aqui é excluir uma das 90 linhas, depois uma das 89 linhas restantes, etc. - não podemos excluir a linha 90 quando apenas 89 permanecem.

eval $(for i in {90..81}; do CMD="$CMD | sed $(( (RANDOM % $i)+1 ))d"; done; echo cat infile $CMD) > outfile

O loop for acumulou uma série de strings formando um pipeline na forma | sed NNd , em que NN é um número aleatório do intervalo de redução começando em 1 a 90 e terminando em 1 a 81, resultando em | sed 88d | sed 12d | sed 36d...

Após o comando CMD ser formado, prefixamos cat infile no CMD do pipeline (observando que o CMD é iniciado com | do loop for). O CMD agora parece com cat infile | sed 88d | sed 12d...

Finalmente, nós eval da string CMD para executar o comando e colocar o resultado em outfile

    
por 20.11.2015 / 16:55

Tags