sed na inserção OSX em uma determinada linha

24

Então, eu uso o 'sed' no linux há um tempo, mas tive um pouco de dificuldade ao tentar usá-lo no OSX, pois o 'POSIX sed' e o 'GNU sed' têm muitas pequenas diferenças. Atualmente, estou lutando para inserir uma linha de texto após um determinado número de linha. (neste caso, linha 4)

No linux, eu faria algo assim:

sed --in-place "4 a\  mode '0755'" file.txt

Então, no OSX, tentei o seguinte:

sed -i "" "4 a\ mode '0755'" file.txt

No entanto, isso continua me dando um erro de 'caracteres extras após \ no final de um comando'. Alguma idéia do que há de errado aqui? Eu tenho um erro de digitação? Ou não estou entendendo outra diferença entre as versões do sed?

CRThaze
fonte
2
Pode confirmar que isso não está funcionando no MacOS 10.6.8. Funciona bem no debian 6.0.3.
tink

Respostas:

24

Estritamente falando, a especificação POSIX parased requer uma nova linha após a\:

[1addr]a\
text

Escreva texto na saída padrão como descrito anteriormente.

Isso faz com que escrever one-liners um pouco de dor, que é provavelmente a razão para o seguinte extensão GNU ao a, ie ccomandos:

Como uma extensão GNU, se entre a ae a nova linha existir outra \sequência que não seja um espaço em branco , o texto dessa linha, iniciando no primeiro caractere que não seja um espaço em branco após o a, será considerado como a primeira linha do bloco de texto . (Isso simplifica a criação de scripts para adicionar uma linha.) Essa extensão também funciona com os comandos ie c.

Portanto, para ser portátil com sua sedsintaxe, você precisará incluir uma nova linha após o a\procedimento. A maneira mais fácil é apenas inserir uma nova linha citada:

$ sed -e 'a\
> text'

(onde $e >são prompts de shell). Se você achar que é uma dor, bash[1] tem a $' 'sintaxe de aspas para inserir escapes no estilo C, então use

sed -e 'a\'$'\n''text'

[1] e mksh (r39b +) e algumas conchas de bourne que não sejam do tipo bash (por exemplo, / bin / sh no FreeBSD 9+)

jw013
fonte
Resposta muito informativa. Obrigado pela dica sobre a sintaxe de cotação do bash.
cjackson
14

Conforme descrito nesta resposta , é útil ao usar comandos sed como ie ausar várias -e "..."cláusulas. Cada uma dessas cláusulas será entendida como separada por novas linhas. O ie acomandos são difíceis de usar em linha sed scripts de outra forma (eles são projetados para uso em um arquivo de script várias linhas sed solicitado usando sed -f file ...). Parece que você não pode usar a nova linha implícita introduzida no final de uma -ecláusula para separar a\a linha de texto a ser anexada. Mas você pode usá-lo para finalizar a linha de texto que será anexada.

Nesse caso específico, o que você está tentando fazer pode ser realizado com uma única -e ...cláusula. Você apenas precisa usar o acomando corretamente. Pelo padrão POSIX, aprecisa ser seguido por a e \, em seguida, por uma nova linha; o restante da próxima linha será inserido (até que uma nova linha ou o final da -ecláusula seja encontrada). Então você poderia fazer:

sed -i "" -e $'4 a\\\n'"mode '0755'" file.txt
dubiousjim
fonte
2
Se você quiser descobrir em quais recursos os Gnu-isms são confiáveis , esta página pode ajudar: wiki.alpinelinux.org/wiki/Regex . Porém, é limitado apenas aos recursos de regex; não os outros comandos sed.
dubiousjim
5

O GNU sed parece ser muito mais usado do que o POSIX sed, com base em exemplos / tutoriais que já usei.

Eu descobri que é muito mais fácil instalar o GNU sed em qualquer máquina OSX que eu use. Se você estiver interessado, a melhor maneira de fazer isso é instalá-lo através do Homebrew .

Você pode apenas $ brew install gnu-sed, ou obter todos os utilitários comuns do GNU $ brew install coreutils.

Então, quando você se deparar com um problema de sintaxe com dateou em algum outro programa, você pode simplesmente usar a versão GNU. Eventualmente, eu apenas decidi que era mais fácil sempre usar as versões do GNU e colocá-las antes das versões do sistema no meu PATH.

reitor
fonte
5

O Perl não sofre dessas idiossincrasias GNU versus não GNU vs. "proprietárias" dependentes dessa plataforma. Você poderia fazer:

perl -ni.old -e 'print;if ($.==4) {print "mode 0755\n"}' file

O -n' option creates a loop that reads every line of the input file. Unlike its cousin-p (not used here) it doesn't automatically print every line read. The-i invokes in-place replacement. The argument to-i (viz. ".old") can be dropped or changed. It leaves a backup of the unmodified file. The -esinaliza o início do script.

A $.indica a linha-número, começando com a linha-1. Assim, a linha de comando lê 'arquivo' e quando o número da linha é igual a 4, ela imprime essa linha seguida pelo que você deseja injetar.

Gostaria de acrescentar que o Perl deve suas raízes a sedAWK e C, portanto a sintaxe para substituições simples etc. não é uma curva de aprendizado muito acentuada.

JRFerguson
fonte
Boa resposta! Mas você pode simplificar um pouco. Tente isto: perl -wlpi.old -e 'print "mode 0755" if $. == 5' file.txt. O -pswitch funciona bem aqui (já que queremos imprimir todas as linhas); basta alterar a lógica de "impressão após a linha 4" para "impressão antes da linha 5". E a -lopção faz com que "\ n" seja impresso automaticamente a cada print, para que você não precise imprimi-lo.
JL