sobre sed -e portátil… db ou! b?

12

Na presente edição Stéphane Chazelas POSIXifies (novamente) o meu sedformatação através da inserção de uma -epausa xpression e outra -edeclaração xpression. Agora, posso perguntar a ele por que, nos comentários, suponho, mas já é a revisão número 18 dessa resposta e quase todas as anteriores já eram graças a brindes semelhantes (se você puder ver comentários excluídos, saberá o que Quero dizer) . Além disso, acho que estou perto o suficiente para entender por que expressar isso de uma maneira que possa ser mais útil em geral. Então aqui está esperando ...

Geralmente, prefiro manter minhas sed -eimpressões totais em um, se eu puder, mas também tenho uma preferência maior por estar em conformidade com as especificações o mais próximo possível, especialmente quando a diferença chega a não mais que um <space>e um -e. Mas não posso fazer isso se não entendo por que deveria. Aqui está um breve resumo do estado atual do meu entendimento:

  • a ' -e 'interrupção pode substituir portabilidade uma quebra de linha de sedscript \nem uma sedinstrução de linha de comando ... Eu sou confuso sobre o porquê

  • a chave de fechamento em uma sed {função }deve ser precedida por uma \nquebra de linha de linha, conforme indicado aqui:

    • O <right-brace>deve ser precedido por a <newline>e pode ser precedido ou seguido por <blank>caracteres.
  • uma \npausa ewline é igualmente necessário seguir qualquer uso de ... a, b, c, i, r, t, w, ou :.

Mas não entendo claramente como a definição de {função }se relaciona com o !operador not. A única menção que encontro do operador de negação nos estados de especificação:

  • Uma função pode ser precedida por um ou mais !caracteres; nesse caso, a função será aplicada se os endereços não selecionarem o espaço do padrão.

Isso significa que o uso de um !implica {aparelho }? O que dizer dos $!comandos - eles também devem ser separados por ' -e 'intervalos? Foi isso que foi abordado quando Stéphane mais recentemente POSIXificou minha resposta?

Eu acho que é o !operador de negação ou a bdeclaração do rancho que ele aborda em sua edição - ou possivelmente é ao mesmo tempo -, mas eu não sei e gostaria de fazê-lo. Se for apenas a bdeclaração do rancho, acredito que um dfuncionaria em seu lugar e eliminaria a necessidade do ' -e 'intervalo, mas prefiro ter certeza antes de arriscar uma resposta três vezes POSIXificada . Você pode ajudar?

Afinal , arrisquei , mas não com muita certeza ...

mikeserv
fonte
Com b;n;:b, você está ramificando para o rótulo chamado ";n;:b"seds históricos e POSIX (e o GNU sed não é nesse sentido).
Stéphane Chazelas
@ StéphaneChazelas - eu entendi o :papel - você dirigiu isso para casa meses atrás. Mas não entendo completamente por que o segundo sedcomando foi similarmente POSIXificado .
mikeserv
1
De qualquer forma, a especificação POSIX para sedé muito incerta para mim. Solicitei esclarecimentos algumas vezes no passado, mas acho que não foi atualizado como resultado. Um bom teste é tentar usar o baú da ferramenta de herança (Solaris one, derivado do original e no qual a especificação POSIX se baseia amplamente).
Stéphane Chazelas
1
@ syntaxerror - eu não acredito que seja esse o caso. se você ler as especificações, descobrirá que s///as ubstitutions devem aceitar encadeamento com a ; . fica embaçado com os comandos que devem ser delimitados com uma nova linha e como -epode ficar nesse caso - pelo menos para mim. Ainda estou tropeçando em algo sedque não os interpreta de maneira bastante intercambiável.
mikeserv
1
@ syntaxerror - eu gosto, mas você deve saber que não precisa do ;antes de uma nova linha - uma nova linha é boa. Honestamente, você poderia ficar sem o -ee tudo inteiramente e apenas escrever um arquivo como #!/bin/sedcom cada comando em uma nova linha - ou aqueles que não requerem esses delimitadores, em vez disso, delimitados por ;. Os que fazer exigem novas linhas são geralmente os que levam entrada arbitrária - :nomes de rótulos e comandos que se referem a eles como bou tou fechar }curlies para funções ou read e writo que levam argumentos de nome de arquivo. Todos eles portàvelmente precisam ser seguidos por \n.
mikeserv

Respostas:

4

Portanto, já é hora de essa pergunta ter uma resposta e, embora eu tenha intuitivamente descoberto como fazer isso corretamente em praticamente todos os casos, há algum tempo atrás, só recentemente consegui concretizar esse entendimento com o texto do padrão. . Na verdade, é declarado lá de maneira bastante simples - eu simplesmente ignorei isso muitas vezes, eu acho.

As partes relevantes do texto são encontradas sob o título ...

  • Comandos de edição emsed :

    • O texto do argumento deve consistir em uma ou mais linhas. Cada linha \neletrônica incorporada no texto deve ser precedida por uma \barra invertida. Outras barras invertidas no texto serão removidas e o caractere a seguir será tratado literalmente.

    • Os verbos re wcomando e o wsinalizador para o scomando assumem um parâmetro opcional rfile (ou wfile ), separado da letra ou sinalizador do verbo do comando por um ou mais <blank>s; implementações podem permitir separação zero como uma extensão.

    • Comando verbos que não {, a, b, c, i, r, t, w, :, e #pode ser seguido por um ;ponto e vírgula, opcional <blank>s, e outro verbo de comando. No entanto, quando o sverbo de comando é usado com o wsinalizador, segui-lo com outro comando dessa maneira produz resultados indefinidos.

...dentro...

  • Opções: Múltiplas -ee -fopções podem ser especificadas. Todos os comandos devem ser adicionados ao script na ordem especificada, independentemente de sua origem.

    • -e script - Adicione os comandos de edição especificados pelo argumento da opção de script ao final do script dos comandos de edição. O argumento de opção de script deve ter as mesmas propriedades que o operando do script , descrito na seção OPERANDS .

    • -f script_file - adicione os comandos de edição no arquivo script_file ao final do script.

E por último em ...

  • Operandos:

    • script - Uma sequência a ser usada como o script dos comandos de edição. O aplicativo não deve apresentar um script que viole as restrições de um arquivo de texto, exceto que o caractere final não precise ser um \newline.

Portanto, quando você o toma em conjunto, faz sentido que qualquer comando que seja opcionalmente seguido por um parâmetro arbitrário sem um delimitador predefinido (em oposição a, s d sub d repl d flagpor exemplo) deva delimitar em uma linha de linha sem escape \n.

É possível argumentar que se ; trata de um delimitador predefinido, mas, nesse caso, o uso de ;para qualquer um dos [aic]comandos exigiria que um analisador separado fosse incluído na implementação especificamente para esses três comandos - separados, ou seja, do analisador usado para [:brw], por exemplo. Ou então a implementação teria que exigir que ; também houvesse uma barra invertida escapada dentro do parâmetro de texto e ela só se tornaria mais complicada a partir daí.

Se eu estivesse escrevendo um sedque desejasse ser compatível e eficiente, espero não escrever um analisador separado, exceto que talvez [aic]gere um erro de sintaxe se não for imediatamente seguido por um \newline. Mas esse é um problema simples de tokenização - o caso do delimitador final é geralmente o mais problemático. Eu escreveria assim:

sed -e w\ file\\ -e one -e '...;and more commands'

...e...

sed -e a\\ -e appended\\ -e text -e '...;and more commands'

... se comportaria de maneira muito semelhante, pois o primeiro criaria e gravaria em um arquivo chamado:

file
one

... e o segundo acrescentaria um bloco de texto à linha atual na saída, como ...

appended
text

... porque ambos compartilhariam o mesmo código de análise para o parâmetro

E em relação ao { ... }e $!edição - bem, eu estava longe lá. Um único comando precedido por um endereço não é uma função, mas apenas um comando endereçado. Quase todos os comandos - incluindo a { definição de função } são especificados para aceitar /one/ou /one/,/two/endereçar - com exceção da definição de #comentário e :rótulo . E um endereço pode ser um número de linha ou um expresso regular e pode ser negado !. Então todos ...

$!d
/address/s/ub/stitution/
5!y/d/c/

... pode ser seguido por um ;e mais comandos de acordo com o padrão, mas se mais comandos forem necessários para um único endereço, e esse endereço não deve ser reavaliado após a execução de cada comando, uma {função }deve ser usada como:

/address/{ s//replace addressed pattern/
           s/do other conditional/substitutions/
           s/in the same context/without/
           s/reevaluating/address/
}

... onde {não pode ser seguido na mesma linha por um fechamento }e que um fechamento }não pode ocorrer, exceto no início de uma linha. Porém, se um comando contido não \npuder ser seguido por uma linha de e-mail, ele também não precisará estar dentro da função. Portanto, todas as s///substituições acima - e até a }chave de fechamento , podem ser seguidas de maneira portável por ;ponto e vírgula e outros comandos.

Eu continuo falando sobre \ndelimitadores de linha eletrônica, mas a pergunta é sobre -edeclarações xpression, eu sei. Mas os dois são realmente o mesmo, e a principal relação é que um script pode ser um argumento literal da linha de comando ou um arquivo com um dos dois -[ef], e que ambos são interpretados como arquivos de texto (que são especificados para terminar em um \nlinha), mas nenhuma delas precisa terminar em uma \nlinha. Com isso, posso razoavelmente (espero) inferir que um \0NULargumento delimitado implica uma linha final de \new e, como todos os argumentos de invocação têm pelo menos) um \0NULdelimitador de qualquer maneira, então eles devem funcionar bem.

De fato, na prática, em todos os casos, exceto aquele em que o padrão especifica que uma \nova linha com escape de barra invertida deve ser necessária, eu encontrei portably ...

sed -e ... -e '...\' -e '...'

... para trabalhar tão bem. E em todos os casos - novamente, na prática - onde um \newline não escapado deve ser necessário ...

sed -e '...' -e '...'

... funcionou para mim também. A única exceção que mencionei acima é ...

sed -e 's/.../...\' -e '.../'

... que não funciona para nenhuma implementação em nenhum dos meus testes. Tenho certeza de que isso remete ao requisito do arquivo de texto e ao fato de que s/// vem com um delimitador e, portanto, não há razão para que uma única instrução deva abranger \0NULargumentos delimitados.

Portanto, concluindo, aqui está um breve resumo de maneiras portáteis de escrever vários tipos de sedcomandos:

Para qualquer um de [aic]:

...commands;[aic]\
text embedded newline\
delimiting newline
...more;commands...

...ou...

sed -e '...commands;[aic]\' -e 'text embedded newline\' -e 'delimiting newline' -e '.;.;.'

Para qualquer um dos [:rwtb]lugares em que o parâmetro é opcional (para todos, exceto :), mas a linha de \nchamada delimitadora não é . Observe que eu nunca tive um motivo para tentar vários parâmetros de rótulo de linha , como seria usado com [:tb], mas esse wregistro / rleitura de várias linhas nos parâmetros do arquivo [rw] geralmente é aceito sem perguntas por seds que testei desde que o \newline incorporado é escapado com uma \barra invertida. Ainda assim, o padrão não especifica diretamente que os parâmetros label e [rw] devem ser analisados ​​de forma idêntica ao textoparâmetros e não faz menção de \nlinhas eletrônicas em relação aos dois primeiros, exceto quando os delimita.

...commands;[:trwb] parameter
...more;commands...

...ou...

sed -e '[:trwb] parameter' -e '...'

... onde o <space>acima é opcional para [:tb].

E por ultimo...

...;address[!]{ ...function;commands...
};...more;commands....

...ou...

sed -e '...;address[!]{ ...function;commands...' -e '};...more;commands...'

... onde qualquer um dos comandos mencionados acima (exceto :) também aceita pelo menos um endereço e que pode ser um número /regular /ou de linha e pode ser negado !, mas se mais de um comando for necessário para uma única avaliação do endereço , chaves de delimitação de {contexto de função }devem ser usadas. Uma função pode conter até vários \ncomandos delimitados por linha de linha, mas cada um deve ser delimitado dentro dos chavetas, como seria caso contrário.

E é assim que se escreve sedscripts portáteis .

mikeserv
fonte
2
Por que você não aceita sua própria resposta?
Philippos