Como usar o caractere NUL como um separador nos comandos de substituição e exclusão de sed?

2

Foi o que tentei quando pretendo substituir /path/to/a por /path/to/b usando NUL como o separador / delimitador:

$ cat pathsList| sed -r -e 's
$ sed --version
sed (GNU sed) 4.4
/path/to/a
$ cat pathsList| sed -r -e 's
$ sed --version
sed (GNU sed) 4.4
/path/to/a%pre%/path/to/b%pre%g' sed: -e expression #1, char 27: number option to 's' command may not be zero
/path/to/b%pre%g' sed: -e expression #1, char 27: number option to 's' command may not be zero

Meu desejo por NUL : NUL e / são os únicos caracteres não permitidos em ext4fs , e / já é muito usado como o separador de nome de caminho . Além disso, quero evitar cotar e desquotar meus dados apenas para poder usar sed .

Se NUL não puder ser usado como um delimitador (digamos), vou ficar bem com qualquer solução alternativa melhor do que citando e desmarcando meus dados.

%pre%     
por Harry 31.07.2018 / 05:31

4 respostas

3

Infelizmente, parece que não é possível usar o NUL como um separador para o comando s/// em sed.

Se você quiser criar uma string com um caractere NUL, você pode usar o formulário $'...' que o bash e outros shells reconhecem, então você pode pensar que isso funcionaria:

sed -r -e $'s
$ cat -v script.sed 
s^@o^@x^@g
o
$ echo "/path/to/a/folder" | sed -r -f script.sed 
sed: file script.sed line 1: delimiter character is not a single-byte character
x
case 0: /* Special case of mbrtowc(3): the NUL character */
  /* TODO: test this */
  return 1;
g'

Mas a maneira como os argumentos são passados no Linux (e no Unix em geral) faz com que não seja realmente possível passar strings com NULs incorporadas, já que tudo que você obtém é um argc (number of arguments) e argv que é um array de char * , em seguida, seqüências terminadas em NUL (C strings) é a única maneira possível de levar os argumentos. Em outras palavras, todo o sed (ou qualquer programa) verá se passou $'s"s"o^@xsg' é simplesmente unterminated 's' command (e o NUL, que eles devem tomar como o final da string.)

Eu achei que talvez passar isso como um arquivo externo para o sed poderia funcionar, já que nesse caso o sed pode saber que os NULs estão embutidos e potencialmente rastrear a string completa pelo seu comprimento, então eu tentei isso:

/*
 * Return zero in all other cases:
 *   CH is a valid single-byte character (e.g. 0x01-0x7F in UTF-8 locales);
 *   CH is an invalid byte in a multibyte sequence for the currentl locale,
 *   CH is the NUL byte.
 */

Os sed s são os bytes NUL. Eu os inseri no vim usando Ctrl v 0 0 0 (tres zeros) qual é o pressionamento de tecla vim para inserir um caractere pelo seu valor ASCII.

Mas isso também não parece funcionar:

sed -r -e $'s
$ cat -v script.sed 
s^@o^@x^@g
o
$ echo "/path/to/a/folder" | sed -r -f script.sed 
sed: file script.sed line 1: delimiter character is not a single-byte character
x
case 0: /* Special case of mbrtowc(3): the NUL character */
  /* TODO: test this */
  return 1;
g'

Curiosamente, isso é diferente de quando há apenas um único is_mb_char() no arquivo de script, caso em que o sed reclama de return 1 ... Portanto, parece estar acompanhando a string pelo seu comprimento, mas ainda assim não parece feliz em usar o NUL como seu caractere separador.

Olhando o código-fonte de return 0 , não está claro se isso foi planejado ou se foi um bug. Na função mbrtowc(3) , que tenta detectar se o byte é parte de um caractere de múltiplos bytes, manipular para NUL funciona assim :

/*
 * Return zero in all other cases:
 *   CH is a valid single-byte character (e.g. 0x01-0x7F in UTF-8 locales);
 *   CH is an invalid byte in a multibyte sequence for the currentl locale,
 *   CH is the NUL byte.
 */

Nesse caso, L'%code%' significa "sim, é um caractere de vários bytes", o que não é realmente o caso.

Um comentário que algumas linhas acima dizem :

%pre%

Então, talvez %code% fosse pretendido?

A confirmação que introduziu este código não t tem muito mais contexto aqui ...

A página do manual %code% menciona %code% , que eu suponho ser algum tipo de multi-byte NUL, então talvez seja por isso que eles decidiram lidar dessa maneira?

Espero que esta informação ainda seja útil!

    
por 31.07.2018 / 06:52
2

Se você quiser substituir caracteres únicos (bytes) por caracteres simples (bytes), use tr :

$ echo "/path/to/a/folder" | tr ao xy
/pxth/ty/x/fylder

Para strings arbitrárias, você pode usar o Perl:

$ echo "/path/to/a/folder" | patt=o repl=xx perl -pe 's/$ENV{patt}/$ENV{repl}/g'
/path/txx/a/fxxlder

(Eu passei patt e repl pelo ambiente, pois perl -p implica em usar os argumentos da linha de comando como nomes dos arquivos a serem processados.)

Aqui, é claro, patt é tomado como uma expressão regular, com tudo o que isso implica:

$ echo "/path/to/a/folder" | patt='a.' repl=x perl -pe 's/$ENV{patt}/$ENV{repl}/g'
/pxh/to/xfolder

Você precisará fugir dos pontos ( \. ) e outros caracteres especiais ou usar \Q$ENV{patt} :

$ echo "/path/to/a/folder.txt" | patt=. repl=, perl -pe 's/\Q$ENV{patt}/$ENV{repl}/g'
/path/to/a/folder,txt

Em ambos os casos acima (argumentos de linha de comando e variáveis de ambiente), a interface entre o SO e o utilitário passa as strings como strings terminadas em NUL, conforme usado pela biblioteca padrão C. Essa interface torna impossível injetar bytes NUL literais nos argumentos, e sed -e 's\a\x\g' usa sed a barra invertida literal como um separador para o comando s .

    
por 31.07.2018 / 10:52
1

Embora o NUL não possa ser encontrado em um nome de arquivo (pela mesma razão que não pode ser encontrado em um argumento de comando), . (muito comum), ^ , * , [ , $ , \ all pode e também teria que ser escapado de qualquer forma, pois são operadores de expressões regulares entendidos pelo comando sed ' s .

Você sempre pode fazer isso escapando de forma automatizada .

Note que ao lado de NUL, newline e todos os caracteres multi-byte não podem ser usados no GNU sed . Outras implementações podem ter diferentes limitações. O POSIX também proíbe a barra invertida (embora funcione para o GNU sed ), então eu recomendaria ficar com caracteres gráficos diferentes da barra invertida do conjunto de caracteres portátil.

    
por 02.08.2018 / 13:52
-2

Você pode tentar se isso funcionar:

$ echo "/path/to/a/folder" | sed -r -e 's/
$ echo "/path/to/a/folder" | sed -r -e 's/%pre%o/%pre%x/g'
o/%pre%x/g'
    
por 31.07.2018 / 06:12

Tags