Existe alguma alternativa para o comando “sed -i” no Solaris?

10

Eu tenho um requisito em meu projeto para substituir algum texto existente em um arquivo como foo com 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: illegal option -- i

Eu encontrei no manual que tenho que usar:

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

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

Também encontrei alternativas em perl e awk , mas uma solução no Solaris sed seria muito apreciada.

Estou usando esta versão do bash:

GNU bash, version 3.2.57(1)-release (sparc-sun-solaris2.10)

    
por tpsaitwal 14.10.2016 / 09:14

5 respostas

15

Use ed . Está disponível na maioria das plataformas e pode editar seus arquivos no local.
Como sed é baseado em ed , a sintaxe para substituir padrões é semelhante:

ed -s infile <<\IN
,s/old/new/g
w
q
IN
    
por 14.10.2016 / 12:46
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 redirecionamento para enviar a saída do sed para um arquivo temporário. Se a sed for concluída com êxito, isso substitui abc.txt pelo arquivo temporário.

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

Se houver uma chance de que abc.tmp já exista, convém usar mktemp ou um utilitário semelhante para gerar o nome exclusivo para o temporário.

    
por 14.10.2016 / 09:21
3

Se você quer o equivalente a 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 quisermos 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, em seguida, 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 o stdin original ainda esteja disponível depois disso. É possível trocar as entradas e voltar sem um subshell, mas desta forma parece mais claro para mim.

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

    
por 14.10.2016 / 15:30
3

Primeiro, você deve saber que o comando i\ ao qual você está se referindo é para inserir uma linha de texto - não para salvar o texto editado de volta no arquivo. Não há nenhuma maneira especificada pelo POSIX para usar sed para isso.

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

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

O comando x é para "salvar e sair". Você também pode usar wq , mas x é menor.

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

Uma grande diferença entre ex e sed é que sed é um editor stream . Apenas opera sequencialmente, linha por linha. ex é muito mais flexível do que isso e, de fato, você pode fazer a edição interativa de arquivos de texto diretamente em ex . É o antecessor imediato de vi .

    
por 14.10.2016 / 22:29
1

Usando sed e nenhum arquivo temporário visível :

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

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

Explicação

Sistemas semelhantes a Unix não removem o conteúdo do arquivo do disco até que ele seja ambos desvinculado no sistema de arquivos, e não abertos em nenhum processo. Portanto, você pode fazer exec 3< para abrir o arquivo no shell para ler no descritor de arquivo 3, rm o arquivo (que o desvincula do sistema de arquivos) e, em seguida, chamar sed com o descritor de arquivo 3 usado como entrada .

Note 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 ambas as leituras, e para escrever com a opção de truncar o arquivo - como ainda é o mesmo arquivo, você perde o conteúdo. Mas se você abri-lo para leitura, então rm it, então abra o mesmo nome de caminho para escrita, você está criando um novo arquivo no mesmo nome de caminho (mas em um novo inode e local de disco, pois o original ainda está aberto ): então o conteúdo ainda está disponível.

Depois de terminar, feche o descritor de arquivo que você abriu anteriormente (é o que a sintaxe exec 3<&- especial), que libera o arquivo original para que o sistema operacional exclua (marque como não usado) seu espaço em disco .

Advertências

Há algumas coisas que você deve ter em mente sobre essa solução:

  1. Você só obtém um "go" através do conteúdo - não há portable maneira de um shell "procurar" de volta no descritor de arquivo - então, uma vez que um programa lê algum conteúdo , outros programas só verão o restante do arquivo. E sed lerá o arquivo inteiro.

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

por 15.10.2016 / 04:48