Existe alguma alternativa ao comando "sed -i" no Solaris?

11

Eu tenho um requisito no meu projeto para substituir algum texto existente em um arquivo como foopor outro texto como fooofoo:

abc.txt
name
foo
foo1

Então eu tentei:

sed -i "s/foo/fooofoo/g" abc.txt

No entanto, recebo este erro:

sed: opção ilegal - i

Encontrei no manual que tenho que usar:

sed -i\ "s/foo/fooofoo/g" abc.txt

No entanto, isso também não está funcionando.

Encontrei alternativas perle awktambém uma solução no Solaris sedseria muito apreciada.

Estou usando esta versão do bash:

Lançamento do GNU bash, versão 3.2.57 (1) (sparc-sun-solaris2.10)

tpsaitwal
fonte
No Solaris 11 e posterior, basta usar /usr/gnu/bin/sedpara obter o suporte -i.
Alanc

Respostas:

15

Use ed. Está disponível na maioria das plataformas e pode editar seus arquivos no local.
Desde que sedé baseado na edsintaxe para substituir padrões é semelhante:

ed -s infile <<\IN
,s/old/new/g
w
q
IN
don_crissti
fonte
Por que, em edvez de ex, apenas curioso? (Eu sei que ambos são compatíveis POSIX e eu ligada com as especificações;.))
Wildcard
1
@Wildcard - eu poderia perguntar o contrário - por quê ex? Para responder à sua pergunta: como nunca uso, ex não estou familiarizado com isso. btw, eu vi setups, onde edestava presente, mas exnão foi (mas não o contrário) ... o mais notável é o meu laptop :)
don_crissti
Boa resposta. :) Minha resposta: Como uso o Vim o tempo todo, estou familiarizado com os excomandos. Todos os vicomandos que você pode digitar que começam com dois pontos são excomandos. É claro que existem algumas extensões específicas do Vim, mas apenas o conjunto principal de comandos é incrivelmente poderoso e flexível e é suficiente para edições com script.
Curinga
Meu sistema Manjaro tem exe ainda não ed.
meados de
8

Se você não pode instalar o GNU sed, use:

sed "s/foo/fooofoo/g" abc.txt >abc.tmp && mv abc.tmp abc.txt

Isso usa o redirecionamento para enviar a saída do sed para um arquivo temporário. Se sed for concluído com êxito, isso substituirá abc.txto arquivo temporário.

Como pode ser visto no código fonte do GNU sed , é exatamente sed -iisso que faz. Então, isso é tão eficiente quanto sed -i.

Se abc.tmpjá existe uma chance , convém usar mktempum utilitário semelhante para gerar o nome exclusivo para o temporário.

John1024
fonte
Obrigado pela ajuda. No entanto, existe alguma opção no solaris para atualizar o arquivo existente sem criar um arquivo temporário?
tpsaitwal
10
Odeio quebrá-lo para você, mas mesmo o sed cria um arquivo temporário. git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.c#n84
Jeff Schaller
1
Você pode fazer isso se não se importar com links físicos - veja meu exemplo. Ainda existe um arquivo temporário, mas está oculto (sem nome). Sem um temporário, você precisaria usar ed, pois ele lê o arquivo inteiro na memória.
perfil completo de Toby Speight
3

Se você quer o equivalente sed -i.bak, é bem simples.

Considere este script para o GNU sed:

#!/bin/sh

# Create an input file to demonstrate
trap 'rm -r "$dir"' EXIT
dir=$(mktemp -d)
grep -v '[[:upper:][:punct:]]' /usr/share/dict/words | head >"$dir/foo"

# sed program - removes 'aardvark' and 'aardvarks'
script='/aard/d'

##########
# What we want to do
sed -i.bak -e "$script" "$dir"
##########

# Prove that it worked
ls "$dir"
cat "$dir/foo"

Podemos simplesmente substituir a linha marcada por

cp "$dir/foo" "$dir/foo.bak" && sed -e "$script" "$dir/foo.bak" >"$dir/foo"

Isso move o arquivo existente para um backup e grava um novo arquivo.

Se queremos o equivalente a

sed -i -e "$script" "$dir"  # no backup

então é um pouco mais complexo. Podemos abrir o arquivo para leitura como entrada padrão e desvinculá-lo antes de direcionar a saída do sed para substituí-lo:

( cp "$dir/foo" "$dir/foo.bak"; exec <"$dir/foo.bak"; rm "$dir/foo.bak"; exec sed -e "$script" >"$dir/foo" )

Fazemos isso em um sub-shell, para que nosso stdin original ainda esteja disponível depois disso. É possível alternar entradas e retornar sem um subshell, mas dessa maneira me parece mais claro.

Observe que temos o cuidado de copiar primeiro, em vez de criar um novo fooarquivo - isso é importante se o arquivo for conhecido por mais de um nome (por exemplo, possui links físicos) e você quiser ter certeza de que não os quebrará. .

Toby Speight
fonte
3

Primeiro, você deve saber que o i\comando a que se refere é para inserir uma linha de texto - não para salvar o texto editado de volta no arquivo. Não há nenhuma maneira POSIX-especificado de usar sedpara isso.

O que você pode fazer é usar ex, o que é especificado pelo POSIX :

printf '%s\n' '%s/find/replace/g' 'x' | ex file.txt

O xcomando é para "salvar e sair". Você também pode usar, wqmas xé mais curto.

O %sinal no início do comando substituto significa "Aplique este comando a todas as linhas do buffer". Você poderia usar 1,$s/find/replace/gtambém.


Uma grande diferença entre exe sedé que sedé um editor de fluxo . Só opera sequencialmente, linha por linha. exé muito mais flexível que isso e, de fato, você pode editar diretamente os arquivos de texto ex. É o antecessor imediato de vi.

Curinga
fonte
1

Usando sede nenhum arquivo temporário visível :

Você pode evitar a criação de um "arquivo temporário" visível separado :

exec 3<abc.txt
rm abc.txt
sed 's/foo/fooofoo/' <&3 >abc.txt
exec 3<&-

Explicação

Os sistemas tipo Unix não removem o conteúdo do arquivo do disco até que ele seja desvinculado no sistema de arquivos e não seja aberto em nenhum processo. Portanto, você pode exec 3<abrir o arquivo no shell para ler o descritor de arquivo 3, rmo arquivo (que o desassocia do sistema de arquivos) e depois chamar sedo descritor de arquivo 3 usado como entrada.

Observe que isso é muito diferente disso:

# Does not work.
sed 's/foo/fooofoo/' <abc.txt >abc.txt

A diferença é que, quando você faz isso em um comando, o shell apenas abre o mesmo arquivo para leitura e escrita, com a opção de truncar o arquivo - já que ainda é o mesmo arquivo, você perde o conteúdo. Mas se você o abre para leitura e, em seguida rm, abre o mesmo nome de caminho para escrever, na verdade você está criando um novo arquivo com o mesmo nome de caminho (mas em um novo local de inode e disco, pois o original ainda está aberto): o conteúdo ainda está disponível.

Depois que terminar, você pode fechar o descritor de arquivo que você abriu anteriormente (é o que a exec 3<&-sintaxe especial faz), que libera o arquivo original para que o sistema operacional possa excluir (marcar como não utilizado) seu espaço em disco.

Ressalvas

Há algumas coisas a ter em mente sobre esta solução:

  1. Você obtém apenas uma "análise" do conteúdo - não há uma maneira portátil de um shell "procurar" de volta no descritor de arquivo - portanto, uma vez que um programa lê parte do conteúdo, outros programas verão apenas o restante do arquivo. E sedvai ler o arquivo inteiro.

  2. Há uma pequena chance de o seu arquivo original ser perdido se o seu shell / script / sed for morto antes de terminar.

mtraceur
fonte
Para deixar claro, o @TobySpeight já postou um exemplo dessa solução no final de sua resposta, mas achei que poderia usar uma elaboração mais aprofundada.
Mtraceur 15/10/16
Para o voto negativo: Embora eu tenha certeza de que apenas deixar um voto negativo faz você se sentir bem consigo mesmo, agradeceria se você contribuísse mais fornecendo feedback sobre os problemas que você tem com esta resposta, como ela pode ser melhorada etc. #
18716 #
talvez você pode usar perl -i
c4f4t0r
@ c4f4t0r: Desculpe pela resposta tardia: Sim, perl -ié outra boa opção. A questão solicitado especificamente uma solução compatível com Solaris' sed, em contraste com soluções com perle awkque eles já mencionado encontrar. Além disso, minha resposta foi principalmente destinada a adicionar algo que achei útil saber e que faltava em qualquer uma das outras respostas.
Mtraceur 11/11/16