Como excluo os mesmos números de linha em todos os arquivos em um diretório?

2

Eu quero excluir a linha 151-154 inclusive de todos os arquivos .fasta no diretório. Estou tentando

find . -type f -exec sed -i.fix '151,154d' '{}' '+' 

mas só será executado no primeiro arquivo, não nos outros 400.

    
por Gavin 03.03.2018 / 21:24

2 respostas

2

-i é uma opção sed não padrão. Vem de perl . GNU e FreeBSD sed adicionaram -i independentemente no final de 2001, início de 2002 para imitar o comportamento de perl , mas com interfaces diferentes. Ele entrou em mais algumas implementações, já que com mais variação (em especial, quanto dos metadados do arquivo original é preservado).

Em perl , você faria:

perl -ni.back -e 'print unless $. == 151..154' file

$. é o número da linha atual para o identificador do arquivo de entrada atual . Quando <> / -n é usado, esse é o identificador de arquivo ARGV que é aberto para cada um dos arquivos transmitidos como argumento, mas como ARGV não está fechado entre cada arquivo, $. não é redefinir entre cada arquivo. Para isso, você precisaria:

perl -ni.back -e '
  print unless $. == 151..154;
  close ARGV if eof' file1 file2

GNU sed , a primeira implementação (AFAIK) para adicionar uma opção -i à perl (adicionada em 2001-09-25 mas não liberada (em 3.95) até um ano depois), tinha a linha número redefinido entre cada arquivo ( -s , outra extensão do GNU implícita).

O FreeBSD fez diferente . As principais diferenças entre o GNU e a API original do FreeBSD eram que -i requer um argumento no FreeBSD, enquanto é opcional no GNU sed como em perl . E no FreeBSD, inicialmente, o número da linha não foi resetado entre cada arquivo.

Em 2007, o FreeBSD se alinhou com o GNU sed quando chegou ao segundo ponto . O número da linha foi redefinido entre cada arquivo e uma opção -I foi adicionada para obter o comportamento antigo de -i .

O

-i support foi adicionado muito mais tarde a algumas outras implementações de sed como o busybox, o NetBSD e o OpenBSD, mas todos eles estão alinhados com o GNU sed em ambos os pontos.

O macOS sed é baseado em uma versão antiga do FreeBSD, então provavelmente é a única implementação que se comporta da maneira antiga do FreeBSD atualmente, e provavelmente a que você está usando.

Então, aqui, você precisaria usar perl com:

find . -type f ! -name '*.back' -exec perl -ni.back -ne '
  print unless $. == 151..154;
  close ARGV if eof' {} +

Ou ligue para um sed por arquivo:

find . -type f ! -name '*.back' -exec sed -i.back 151,154d {} \;

Ou instale e use o GNU em sed ou o GNU awk (com -i inplace e corresponde a FNR , não NR ) ou ed /% abordagens baseadas emex .

    
por 04.03.2018 / 13:13
2

Esta resposta usa ex em vez de sed -i porque sed -i não é uma edição verdadeira no local. Ele grava em um arquivo temporário e renomeia para substituir o original, o que faz com que o número do inode seja alterado. Isso quebra todos os links e snapshots em sistemas de arquivos COW (como btrfs ou zfs) e qualquer outra coisa dependente do número de inode de um arquivo. O mesmo acontece com a maioria dos outros comandos comuns que implementam a opção -i / --in-place (incluindo a opção perl ' -i ).

find . -type f -exec sh -c 'for f in "$@" ; do
    cp -a "$f" "$f.fix"
    printf "%s\n" 151,154d x | ex -s -- "$f" 
  done' sh {} +

Isso bifurca sh para processar a lista de arquivos encontrados por find .

O processo sh primeiro faz uma cópia de backup do arquivo original (usando a opção cp do GNU -a para preservar todos os atributos como registro de data e hora e permissões) e invoca ex para editar o arquivo.

O printf ... | ex ... acima canaliza dois ex de comandos (*) para ex , cada comando separado por uma nova linha. O primeiro comando exclui as linhas 151-154. O segundo ( x ) informa ao ex para gravar as alterações no arquivo (por padrão, ex será encerrado sem salvar a menos que você diga para salvar). Se nenhuma alteração foi feita, então x fecha sem salvar, mesmo que o comando :x em vi .

Nota: se qualquer arquivo de entrada individual tiver menos de 154 linhas, as alterações no serão feitas nesse arquivo.

A opção -s para ex diz para ficar em silêncio e não imprime nenhuma mensagem de diagnóstico.

O -- impede que quaisquer argumentos de nome de arquivo sejam interpretados como opções por ex (no caso de qualquer um dos nomes de arquivos começar com - ). Isso não é estritamente necessário aqui, porque todos os nomes de arquivos gerados pelo comando find terão ./ pré-pagos - mas é inofensivo e um bom hábito de adquirir o processamento de nomes de arquivos desconhecidos.

Os comandos

(*) ex são praticamente os mesmos que os comandos sed . Originalmente, há muito tempo, ed era o editor de arquivos e sed era o editor de fluxo com base em ed . Posteriormente, vi foi escrito como uma versão visual de ed . vi adicionou um modo de edição visual, mas reteve os comandos ed em seu modo de comando : . vi também pode ser executado como ex para edição no modo de comando sem o modo visual, que é o que está sendo usado aqui. ex ainda é basicamente um clone de ed , mas ganhou algumas pequenas melhorias (como o comando x ) sobre o ed original nas décadas desde que foi escrito pela primeira vez.

    
por 04.03.2018 / 10:08