Estou escrevendo scripts shell para o meu servidor, que é uma hospedagem compartilhada executando o FreeBSD. Também quero testá-los localmente, no meu PC executando o Linux. Por isso, estou tentando escrevê-los de maneira portátil, mas sed
não vejo como fazer isso.
Parte do meu site usa arquivos HTML estáticos gerados e essa linha sed insere DOCTYPE correto após cada regeneração:
sed -i '1s/^/<!DOCTYPE html> \n/' ${file_name.html}
Funciona com o GNU sed
no Linux, mas o FreeBSD sed
espera que o primeiro argumento após a -i
opção seja extensão para cópia de backup. É assim que ficaria:
sed -i '' '1s/^/<!DOCTYPE html> \n/' ${file_name.html}
No entanto, o GNU, sed
por sua vez, espera que a expressão siga imediatamente depois -i
. (Também requer correções no tratamento da nova linha, mas isso já é respondido aqui )
É claro que posso incluir essa alteração na minha cópia do servidor do script, mas isso prejudicaria, ou seja, meu uso do VCS para controle de versão. Existe uma maneira de conseguir isso com o sed de uma maneira totalmente portátil?
-i
Respostas:
O GNU sed aceita uma extensão opcional depois
-i
. A extensão deve estar no mesmo argumento, sem espaço intermediário. Essa sintaxe também funciona no BSD sed.Observe que no BSD,
-i
também muda o comportamento quando existem vários arquivos de entrada: eles são processados independentemente (portanto,$
corresponde à última linha de cada arquivo). Além disso, isso não funcionará no BusyBox.Se você não quiser usar arquivos de backup, poderá verificar qual versão do sed está disponível.
Ou, alternativamente, para evitar interferir nos parâmetros posicionais, defina uma função.
Se você não quer se preocupar, use Perl.
Se você quiser escrever um script portátil, não use
-i
- ele não está no POSIX. Faça manualmente o que o sed faz sob o capô - é apenas mais uma linha de código.fonte
sed -i
também implica-s
. E a maneira mais fácil de verificar um GNUsed
é com osed v
comando que é um noop válido para o GNU, mas falha em qualquer outro lugar.sed -i$(sed v < /dev/null 2> /dev/null || echo -n " ''") -e '...' "$file"
Se não é GNU sed, insere um espaço seguido por duas citações singe depois-i
para que ele trabalha em BSD. GNU sed recebe apenas-i
.v
comando para testar o GNU sed. E se o FreeBSD decidisse implementá-lo?sed v
já que esse é o seu objetivo de existir.Se você não encontrar um truque para tornar o
sed
jogo agradável, tente:Não use
-i
:Use Perl
fonte
ed
Você sempre pode usar
ed
para acrescentar uma linha a um arquivo existente.Detalhes
Os bits ao redor do
<!DOCTYPE html>
são comandos paraed
instruí-lo a adicionar essa linha ao arquivomy.html
.sed
Eu acredito que este comando
sed
também pode ser usado:fonte
Você também pode fazer manualmente o que
perl -i
faz sob o capô:Assim
perl -i
, não há backup e, como a maioria das soluções fornecidas aqui, cuidado com isso pode afetar as permissões, a propriedade do arquivo e transformar um link simbólico em um arquivo comum.Com:
sed
sobrescreveria o arquivo, não afetaria a propriedade e as permissões ou links simbólicos. Funciona com o GNUsed
porquesed
normalmente terá lido um buffer cheio de dados defile
(4k no meu caso) antes de sobrescrevê-lo com oi
comando Isso não funcionaria se o arquivo tivesse mais de 4k, exceto pelo fato desed
também armazenar em buffer sua saída.sed
Funciona basicamente em blocos de 4k para leitura e escrita. Se a linha a inserir for menor que 4k,sed
nunca substituirá um bloco que ainda não leu.Eu não contaria com isso.
fonte
echo '<!DOCTYPE html>'
ou escapou sem "" aspas.O FreeBSD sed , que também é usado no Mac OS X, precisa da
-e
opção após a-i
alternância para definir e reconhecer o seguinte comando (regex) corretamente e sem ambiguidade.Em outras palavras,
sed -i -e ...
deve funcionar com o FreeBSD e GNUsed
.De maneira geral, a omissão da extensão de backup após o FreeBSD
sed -i
requer algumased
opção ou opção explícita, a seguir a,-i
para evitar confusão por parte do FreeBSDsed
enquanto analisa seus argumentos de linha de comando.(Observe, no entanto, que
sed
edições in-loco de arquivos levam a alterações no inode de arquivo, consulte Edição "in-loco" de arquivos ).(Como uma dica geral, as versões recentes do FreeBSD
sed
têm a-r
opção de aumentar a compatibilidade com o GNUsed
).fonte
bsdsed -i -e 's/a/A/'
é uma edição no local, é editada ao salvar o original com o sufixo "-e" (testfile.txt-e
).Para emular
sed -i
para um único arquivo de maneira portável, evitando ao máximo as condições de corrida:A propósito, isso também lida com o possível problema que
sed -i
, dependendo das permissões de diretório e arquivo,sed -i
pode permitir que um usuário substitua um arquivo que esse usuário não tem permissão para editar.Você também pode fazer backups como:
fonte
Você pode usar o Vim no modo Ex:
1
selecione a primeira linhai
inserir texto e nova linhax
salvar e fecharOu ainda, como nos comentários, padrão
ex
antigo simples :fonte
ex
, exceto que as implementações não são necessárias para suportar vários-c
sinalizadores. Para portabilidade definitiva eu usariaprintf '%s\n' 1i '<!DOCTYPE html>' . x | ex file