Eu tenho um zilhão de arquivos que precisam ser reduzidos em tamanho. Descobri que a maioria dos arquivos (não todos) tem uma seção final que pode ser cortada sem perder informações:
Data 1
Data 2
something_unimportant_here END DATA
Rubbish 1
Rubbish 2
Como posso editar um arquivo (terminar, portanto, todos) excluindo a linha incluindo "END DATA" e todos os seguintes, no local, alterando apenas os arquivos que contêm o padrão, minimizando assim o acesso de gravação ao disco (muitos, muitos arquivos e disco lento).
Se possível, gostaria de adicionar uma nova última linha ao arquivo (minha própria tag final) para que a sintaxe do arquivo permaneça correta - novamente, apenas nos arquivos que contêm o padrão.
Eu estava pensando em usar ed
, como
echo ',s/END DATA/ ???? '\\n'q'\\n'wq' | ed "$file"
mas não consigo gerenciar o ???? parte correta.
Saída esperada:
Data 1
Data 2
NEW END
Você deve ser capaz de fazer isso apenas truncando o arquivo no lugar sem ter que escrever uma nova cópia do arquivo como
sed -i
/perl -i
/ed
/gawk -i inplace
faria. Comperl
:Isso minimiza a E/S, pois
perl
para de ler assim que encontra uma correspondência eNEW END\n
é a única coisa que grava. Ele também grava no local, para que os metadados dos arquivos (propriedade, permissão, acls, esparsidade ...) sejam preservados e os links físicos não sejam quebrados.Com
-exec {} +
nós também minimizamos o número deperl
invocações.Parece que a sequência de comandos que você está procurando é
ou como uma linha
(Você pode substituir
wq
por,p
para teste.)Ex. dado
então
dá
Com
GNU grep
eGNU sed
onde
*.txt
assume que todos os seus arquivos estão no diretório atual terminando com.txt
extensão. Se você precisar pesquisar arquivos recursivamente,GNU grep
também oferece suporte a-r/-R
opções./END DATA/,$
gama de linhas para operar//i foo
aqui//
corresponderá ao regex usado anteriormente, ou seja,/END DATA/
oi
comando adicionará o novo marcador final conforme necessáriocomo o
i
comando deve ser separado por nova linha,-e
a opção é usada para separar od
comando para excluir todas as linhas correspondentes ao intervalocomo alternativa, você também pode usar isso, mas apenas um arquivo será passado por vez para
sed
:Mantenha-o simples e apenas use awk para a parte de manipulação de arquivos, por exemplo, com GNU find, awk, grep e xargs:
ou para imprimir sua própria tag final no final de cada arquivo:
Esta solução 3.8 é vagamente baseada na solução
python
in-loco de Stephane com algumas diferenças 1. O código não depende de utilitários externos para passagem de diretório 2. Os arquivos são mapeados na memória para facilitar a localização da stringtruncate
END DATA
Coloque o código em um
.py
arquivo e passe o nome do diretório como parâmetroCombinando a resposta de Sundeep e a resposta de Ed Morton , mas sem
xargs
:find
, é claro, seleciona arquivos. Por padrão, ele pesquisa o(s) diretório(s) especificado(s) recursivamente. Para pesquisar apenas no diretório atual, adicione-maxdepth 1
após o.
.grep -q
sai rapidamente com um status de saída “success” se um arquivo contiver o padrão que está sendo pesquisado (END DATA
) e “false” caso contrário.-a
significa “AND”, como&&
em uma linha de comando do shell. Significa “faça a seguinte coisa se (somente se) a coisa anterior for bem-sucedida”. Na verdade, é o operador de conjunção padrão entrefind
predicados (testes/ações), então você pode deixá-lo de fora. Eu incluí-lo apenas para maior clareza.sed
comando, que é copiado literalmente da resposta do Sundeep (masfoo
alterado paraNEW END
), é executado apenas em arquivos que contêm aEND DATA
string e satisfazem os outrosfind
testes.-exec … +
fazsed
com que seja invocado uma vez com vários arquivos, assim comoxargs
ele.Observe que não podemos usar
-exec … +
com ogrep
comando porque ele não permite testar o status de saída.Usando awk para encontrar o deslocamento do padrão e
dd
truncar o arquivo nesse ponto e anexar o novo trailer:Com uma implementação awk que suporta
nextfile
(gawk
,bwk
, algumas versões demawk
[1]), isso pode ser feito de forma mais eficiente passando lotes de arquivos para o awk:Em vez do icky
2>/dev/null
status=noxfer
pode ser usado comdd
implementações que o suportam.A passagem de variáveis de kludge e ambiente de citação é uma bagunça, poderia usar algumas melhorias.
[1]: de acordo com o manual GNU awk , deve ser suportado no mawk também. No entanto, a versão mais antiga do mawk do Debian 10 não o suporta.
Se esta for uma tarefa única, é conveniente usar
vi
para isso:Primeiro, localize a linha com o conteúdo necessário (usando search
/
ou?
)Para excluir todas as linhas que seguem a linha atual até o final do arquivo, pressione
d
G
.Para excluir todas as linhas desde o início do arquivo até a linha atual, pressione
d
gg
.Salve e saia
:wq