Devido a um erro no aplicativo ainda não diagnosticado, tenho várias centenas de servidores com um disco completo. Há um arquivo que foi preenchido com linhas duplicadas - não um arquivo de log, mas um arquivo de ambiente do usuário com definições de variáveis (portanto, não posso simplesmente excluir o arquivo).
Eu escrevi um sed
comando simples para verificar as linhas adicionadas erroneamente e excluí-las, e testei em uma cópia local do arquivo. Funcionou como pretendido.
No entanto, quando tentei no servidor com o disco completo, recebi aproximadamente o seguinte erro (é da memória, não copia e cola):
sed: couldn't flush /path/to/file/sed8923ABC: No space left on deviceServerHostname
Claro, eu sei que não resta espaço. É por isso que estou tentando excluir coisas! (O sed
comando que estou usando reduzirá um arquivo de mais de 4000 linhas para cerca de 90 linhas.)
Meu sed
comando é apenassed -i '/myregex/d' /path/to/file/filename
Existe uma maneira de aplicar este comando apesar do disco completo?
(Ele deve ser automatizado, pois preciso aplicá-lo a várias centenas de servidores como uma solução rápida.)
(Obviamente, o bug do aplicativo precisa ser diagnosticado, mas, enquanto isso, os servidores não estão funcionando corretamente ...)
Atualização: a situação que enfrentei foi resolvida excluindo outra coisa que descobri que poderia excluir, mas ainda assim gostaria de responder a essa pergunta, que seria útil no futuro e para outras pessoas.
/tmp
é um não-go; está no mesmo sistema de arquivos.
Antes de liberar espaço em disco, testei e descobri que era possível excluir as linhas vi
abrindo o arquivo e executando :g/myregex/d
e, em seguida, salve as alterações com êxito :wq
. Parece que deve ser possível automatizar isso, sem recorrer a um sistema de arquivos separado para armazenar um arquivo temporário .... (?)
fonte
sed -i
cria uma cópia temporária para operar. Eu suspeito queed
seria melhor para isso, embora eu não esteja familiarizado o suficiente para proibir uma solução real #ed
você correr:printf %s\\n g/myregex/d w q | ed -s infile
mas tenha em mente algumas implementações também utilizam arquivos temporários comosed
(você pode tentar busybox ed - afaik ele não cria um arquivo temporário)echo
. useprintf
. esed
adicione alguns caracteres que você soltar na última linha para evitar perder espaços em branco à direita. Além disso, seu shell precisa ser capaz de lidar com o arquivo inteiro em uma única linha de comando. esse é o seu risco - teste primeiro.bash
é especialmente ruim nisso (eu acho que é para fazer w / stack space?) e pode ficar doente com você a qualquer momento. os doissed
são recomendados pelo menos para usar o buffer de pipe do kernel entre eles, mas o método é bastante semelhante. sua subcomando de comando também truncaráfile
se o sed w / in é ou não bem-sucedido.sed '/regex/!H;$!d;x' <file|{ read v && cat >file;}
e, se funcionar, leia o resto da minha resposta. 'Respostas:
A
-i
opção realmente não substitui o arquivo original. Ele cria um novo arquivo com a saída e o renomeia para o nome do arquivo original. Como você não tem espaço no sistema de arquivos para esse novo arquivo, ele falha.Você precisará fazer isso sozinho em seu script, mas crie o novo arquivo em um sistema de arquivos diferente.
Além disso, se você estiver apenas excluindo linhas que correspondem a uma regexp, poderá usar em
grep
vez desed
.Em geral, raramente é possível que os programas usem o mesmo arquivo que a entrada e a saída - assim que começar a gravar no arquivo, a parte do programa que está lendo o arquivo não verá mais o conteúdo original. Portanto, ele precisa copiar o arquivo original em algum lugar primeiro ou gravar em um novo arquivo e renomeá-lo quando terminar.
Se você não quiser usar um arquivo temporário, tente armazenar em cache o conteúdo do arquivo na memória:
fonte
rsync -a --no-owner --no-group --remove-source-files "$backupfile" "$destination"
a partir daquised -i
isso preserva essas coisas?sed -i
não preserva nenhuma dessas coisas. Eu apenas tentei com um arquivo que não possuo, mas localizado em um diretório que possuo, e ele me permitiu substituir o arquivo. A substituição pertence a mim, não ao proprietário original.var=$(< FILE); echo "$FILE" | grep '^"' > FILE
v=$(<file)&& printf %s\\n "$v" >file
, mas você não precisa nem usar&&
. O solicitante está falando sobre executá-lo em um script - automatizando a substituição de um arquivo por uma parte dele. você deve pelo menos validar, pode abrir com êxito entrada e saída. Além disso, o shell pode explodir.É assim que
sed
funciona. Se usado com-i
(edição no local)sed
cria um arquivo temporário com o novo conteúdo do arquivo processado. Quando concluídosed
, substitui o arquivo de trabalho atual pelo temporário. O utilitário não edita o arquivo no local . Esse é exatamente o comportamento de todo editor.É como se você executasse a seguinte tarefa em um shell:
Neste ponto
sed
, tenta liberar os dados em buffer no arquivo mencionado na mensagem de erro com afflush()
chamada do sistema:Para o seu problema, vejo uma solução na montagem de um sistema de arquivos separado (por exemplo
tmpfs
, a , se você tiver memória suficiente ou um dispositivo de armazenamento externo) e mova alguns arquivos para lá, processe-os para lá e mova-os de volta.fonte
Desde a publicação desta pergunta, aprendi que
ex
é um programa compatível com POSIX. É quase universalmente vinculadovim
, mas de qualquer forma, o seguinte é (eu acho) um ponto-chaveex
em relação aos sistemas de arquivos (retirado da especificação POSIX):"... afetará qualquer arquivo ..." Acredito que colocar algo no sistema de arquivos (até mesmo um arquivo temporário) contaria como "afetando qualquer arquivo". Talvez?*
Um estudo cuidadoso das especificações POSIX para
ex
indicar algumas "dicas" sobre seu uso portátil pretendido, quando comparado aos usos comuns de scriptex
encontrados on-line (que estão repletos devim
comandos específicos).+cmd
é opcional de acordo com o POSIX.-c
opções também é opcional.:g
"come" tudo até a próxima nova linha não escapada (e, portanto, executa-o após cada correspondência encontrada para a regex em vez de uma vez no final). Portanto,-c 'g/regex/d | x'
apenas exclui uma instância e sai do arquivo.Portanto, de acordo com o que pesquisei, o método compatível com POSIX para editar no local um arquivo em um sistema de arquivos completo para excluir todas as linhas correspondentes a um regex específico é:
Isso deve funcionar, desde que você tenha memória suficiente para carregar o arquivo em um buffer.
* Se você encontrar algo que indique o contrário, mencione nos comentários.
fonte
ex +g/match/d -scx file
seja compatível com POSIX também?vi
funcionou em um sistema de arquivos completo, acredito que na maioria dos casos funcionariaex
também - embora talvez não seja para um arquivo gigantesco.sed -i
não funciona em um sistema de arquivos completo, independentemente do tamanho do arquivo.Use o cachimbo, Luke!
Ler arquivo | filtro | Escreva de volta
nesse caso
sed
, não cria um novo arquivo e apenas envia a saída canalizada para add
qual abre o mesmo arquivo . Claro que se pode usargrep
em casos particularesdepois trunque o restante.
fonte
sed
sempre usa arquivos temporários?grep
de qualquer maneira nãosponge
comando. Sim,sed
com-i
sempre cria arquivos lilke "seduyUdmw" com 000 direitos.Conforme observado em outras respostas,
sed -i
funciona copiando o arquivo para um novo arquivo no mesmo diretório , fazendo alterações no processo e movendo o novo arquivo sobre o original. É por isso que não funciona.ed
(o editor de linha original) funciona de maneira um pouco semelhante, mas, da última vez que verifiquei, ele é usado/tmp
para o arquivo inicial. Se você/tmp
estiver em um sistema de arquivos diferente do que está cheio,ed
pode fazer o trabalho por você.Tente isso (no prompt do shell interativo):
O
P
(que é um P maiúsculo ) não é estritamente necessário. Ativa a solicitação; sem ele, você está trabalhando no escuro e algumas pessoas acham isso desconcertante. Ow
eq
são w rito e q uit.Se o seu
/tmp
diretório estiver no sistema de arquivos cheio (ou se o sistema de arquivos estiver cheio também), tente encontrar algum espaço em algum lugar. o caos mencionado na montagem de um tmpfs ou de um dispositivo de armazenamento externo (por exemplo, uma unidade flash); mas, se você tiver vários sistemas de arquivos e eles não estiverem todos cheios, poderá simplesmente usar um dos outros existentes. o caos sugere copiar o (s) arquivo (s) para o outro sistema de arquivos, editando-o (comsed
) e copiando-o de volta. Nesse ponto, essa pode ser a solução mais simples. Mas uma alternativa seria criar um diretório gravável em um sistema de arquivos que tenha algum espaço livre, definir a variável de ambienteTMPDIR
para apontar para esse diretório e depois executared
. (Divulgação: não tenho certeza se isso vai funcionar, mas não pode doer.)Depois de
ed
trabalhar, você pode automatizar isso fazendoem um script. Ou , como sugerido por don_crissti.
printf '%s\n' 'g/myregex/d' w q | ed -s filename
fonte
ed
ou comex
), de modo que a memória seja usada em vez de um sistema de arquivos separado? Isso é o que eu estava realmente indo para (e a razão de eu não ter aceitado uma resposta.)ed
muitos anos atrás. Ainda existiam computadores de 16 bits, nos quais os processos eram limitados a um espaço de endereço de 64K (!); Portanto, a ideia de um editor que lê o arquivo inteiro na memória não é de partida. Desde então, é claro, a memória aumentou - mas também discos e arquivos. Como os discos são tão grandes, as pessoas não sentem necessidade de lidar com a contingência de/tmp
ficar sem espaço. Eu apenas dei uma olhada rápida no código fonte de uma versão recenteed
e ainda parece ... (continua)ed
(ex
ouvi
) ofereça uma opção para manter o buffer na memória. Por outro lado, Edição de Texto com ed e vi - Capítulo 11: Processamento de Texto - Parte II: Explorando os Segredos Profissionais do Red Hat Linux - Red Hat Linux 9 - Os sistemas Linux dizem queed
o buffer de edição reside na memória,… (continua )vi
(que é o mesmo programa queex
). Eu acredito que eles estão apenas usando palavras desleixadas e imprecisas - mas, se estiverem na Internet (ou impressas), deve ser verdade, certo? Você paga o seu dinheiro e faz a sua escolha.Você pode truncar o arquivo com bastante facilidade se conseguir obter a contagem de bytes para o deslocamento e suas linhas ocorrerem desde o ponto inicial até o final.
Ou então, se você
${TMPDIR:-/tmp}
estiver em outro sistema de arquivos, talvez:Porque (a maioria) os shells colocam seus documentos aqui em um arquivo temporário excluído. É perfeitamente seguro, desde que o
<<FILE
descritor seja mantido do início ao fim e${TMPDIR:-/tmp}
tenha o espaço necessário.Os shells que não usam arquivos temporários usam pipes e, portanto, não são seguros para uso dessa maneira. Estes escudos são tipicamente
ash
derivados comobusybox
,dash
, BSDsh
-zsh
,bash
,ksh
e o shell Bourne, no entanto, todos os arquivos de utilização de trabalho temporário.aparentemente, eu escrevi um pequeno programa de shell em julho passado para fazer algo muito parecido com isto
Se
/tmp
não for viável, desde que você possa ajustar o arquivo na memória, algo como ...... como um caso geral, pelo menos, garantiria que o arquivo fosse totalmente armazenado em buffer pelo primeiro
sed
processo antes de tentar truncar o arquivo de entrada / saída.Uma solução mais direcionada - e eficiente - pode ser:
... porque não incomodaria as linhas de buffer que você pretendia excluir de qualquer maneira.
Um teste do caso geral:
fonte
/tmp
que estão no mesmo sistema de arquivos. Eu gosto da suased
versão dupla . Eu acho que uma combinação de Barmar de e sua resposta provavelmente seria melhor, algo como:myvar="$(sed '/myregex/d' < file)" && [ -n "$myvar" ] && echo "$myvar" > file ; unset myvar
(Para este caso eu não me importo sobre a preservação de novas linhas à direita.)sed
|cat
O item acima nunca abre a saída, a menossed
que já tenha armazenado em buffer o arquivo inteiro e esteja pronto para começar a gravar tudo na saída. Se ele tentar armazenar em buffer o arquivo e falhar -read
não será bem-sucedido porque encontra o EOF no|
canal antes de ler sua primeira nova linha e, por isso,cat >out
nunca acontece até a hora de gravá-lo completamente da memória. um estouro ou algo assim simplesmente falha. também todo o pipeline retorna sempre sucesso ou fracasso. armazená-lo em um var é apenas mais arriscado.file=$(sed '/regex/!H;$!d;x' <file | read v && tee file) && cmp - file <<<"$file" || shite
assim, o arquivo de saída e o var seriam gravados simultaneamente, o que faria um backup eficaz ou um , ou seja, o único motivo pelo qual você deseja complicar as coisas além do necessário.read script
eread v
na sua resposta. Se você puder elaborar mais sobre isso, serei muito apreciado, obrigado!$script
é osed
script que você usaria para direcionar para qualquer parte do seu arquivo que você desejasse; é o script que fornece o resultado final que você deseja no fluxo.v
é apenas um espaço reservado para uma linha vazia. em umbash
shell, não é necessário porquebash
usará automaticamente a$REPLY
variável shell em seu lugar, se você não especificar um, mas POSIXly sempre deverá fazê-lo. Fico feliz que você ache útil, a propósito. Boa sorte com isso. im mikeserv @ gmail se você precisar de algo em profundidade. i deve ter um computador novo em alguns diasEsta resposta empresta idéias dessa outra resposta e dessa outra resposta, mas se baseia nelas, criando uma resposta mais aplicável em geral:
A primeira linha executa o
sed
comando com a saída gravada na saída padrão (e não em um arquivo); especificamente, a um tubowc
para contar os caracteres. A segunda linha também executa osed
comando com a saída gravada na saída padrão, que, nesse caso, é redirecionada para o arquivo de entrada no modo de substituição de leitura / gravação (sem truncamento), discutido aqui . Isso é algo perigoso de se fazer; é seguro somente quando o comando filter nunca aumenta a quantidade de dados (texto); ou seja, para cada n bytes que lê, ele grava n ou menos bytes. Isso é verdade, é claro, para osed '/myregex/d'
comando; para cada linha que lê, escreve exatamente a mesma linha, ou nada. (Outros exemplos:s/foo/fu/
ous/foo/bar/
seria seguro, mass/fu/foo/
es/foo/foobar/
não o faria.)Por exemplo:
porque esses 32 bytes de dados:
foi sobrescrito com esses 25 caracteres:
deixando os sete bytes
night.\n
restantes no final.Por fim, o
dd
comando procura o final dos novos dados limpos (byte 25 neste exemplo) e remove o restante do arquivo; isto é, trunca o arquivo nesse ponto.Se, por qualquer motivo, o
1<>
truque não funcionar, você poderáAlém disso, observe que, enquanto tudo o que você está fazendo é remover linhas, tudo o que você precisa é
grep -v myregex
(conforme indicado por Barmar ).fonte
sed -i 'd' / caminho / para / arquivo / nome do arquivo
fonte