Caracteres nulos e de escape

6

Eu sei que um nome de arquivo no Linux não tem nenhuma restrição, exceto dois caracteres '/' e ''/'' . Eu sei que mkdir '$' é proibido porque é um separador de diretório, mas existe algum outro motivo ?

Também no meu terminal eu posso criar um arquivo ou diretório com o nome mkdir \$myfile . Então, eu me pergunto como escrever o caractere nulo corretamente porque obviamente ele não deve permitir que eu tenha um nome de arquivo com nulo nele

$myfile irá criar um diretório chamado mkdir \$myfile

Mais uma pergunta: Se eu quiser incluir mkdir '$'myfile no meu nome de arquivo, posso usar a barra invertida

mkdir "$"myfile criará um diretório chamado mkdir '$myfile'

No entanto, posso fazer o mesmo se eu cercar o cifrão com aspas simples e aspas duplas

mkdir "$myfile" é o mesmo que $ é o mesmo que %code% é o mesmo que %code% é o mesmo que %code%

Então minha pergunta é, As aspas simples e duplas são uma substituição para o caractere de barra invertida de escape?

Também o que outros personagens precisam escapar no bash além de %code% , (espaço) e barra invertida?

    
por alkabary 18.08.2015 / 01:12

4 respostas

5

Imprimindo o caractere nulo

Em muitos shells recentes você pode escrever caracteres nulos com formato de aspas simples em dólar $'\x00' , formato hexadecimal \u0000 , formato unicode \U00000000 ou 'echo' , ou apenas como você tentou com octal: -e . O ponto é que o comando tem que entender o que fazer com caracteres de escape com barra invertida. Por exemplo, no caso de printf , geralmente é necessário adicionar a opção %b e, no caso de echo -ne '' , isso seria printf '%b' .

Vamos verificar se funciona:

$ echo -ne '
$ printf '%b' '
$ printf '%b' a'
$ printf '%b' a'
$ printf '%b' a''b | wc -c
2
'b | wc -c 3
'b ab
' $
' $

Portanto, não produz nada, assim como echo -ne , semelhante

$ printf '%b' a'
$ printf '%b' a'
$ touch $'
$ touch $(printf '%b' '
$ touch "$(printf '%b' a'
$ echo -ne '
$ printf '%b' '
$ printf '%b' a'
$ printf '%b' a'
$ printf '%b' a''b | wc -c
2
'b | wc -c 3
'b ab
' $
' $
'b)" $ ls a # in zsh ab # in bash
') touch: missing file operand Try 'touch --help' for more information. $ mkdir $(printf '%b' '
$ printf '%b' a'
$ printf '%b' a'
$ touch $'
$ touch $(printf '%b' '
$ touch "$(printf '%b' a'%pre%'b)"
$ ls
a   # in zsh
ab  # in bash
') touch: missing file operand Try 'touch --help' for more information. $ mkdir $(printf '%b' '%pre%') mkdir: missing operand Try 'mkdir --help' for more information.
' touch: cannot touch ‘’: No such file or directory $ mkdir $'%pre%' mkdir: cannot create directory ‘’: No such file or directory # let's try another approach - using printf in command substitution: $ touch "$(printf '%b' '%pre%')" touch: cannot touch ‘’: No such file or directory $ mkdir "$(printf '%b' '%pre%')" mkdir: cannot create directory ‘’: No such file or directory
'b | xargs -0 echo a b
'b | xargs echo xargs: Warning: a NUL character occurred in the input. It cannot be passed through in the argument list. Did you mean to use the --null option? a
') mkdir: missing operand Try 'mkdir --help' for more information.
' touch: cannot touch ‘’: No such file or directory $ mkdir $'%pre%' mkdir: cannot create directory ‘’: No such file or directory # let's try another approach - using printf in command substitution: $ touch "$(printf '%b' '%pre%')" touch: cannot touch ‘’: No such file or directory $ mkdir "$(printf '%b' '%pre%')" mkdir: cannot create directory ‘’: No such file or directory
'b | xargs -0 echo a b
'b | xargs echo xargs: Warning: a NUL character occurred in the input. It cannot be passed through in the argument list. Did you mean to use the --null option? a

Vamos adicionar alguns caracteres por aí (vou ficar com a''b a partir de agora como mais robusto, mas o efeito similar é com xargs ):

%pre%

Apenas dois caracteres foram impressos, onde foi null ?

%pre%

Vamos comparar com a :

%pre%

Por último mais verifique se realmente imprimimos caracter nulo antes de tentar criar o arquivo, vamos passar o valor impresso para o comando que vai lançar o erro, como xargs -0 :

%pre%

Observe como apenas touch '' foi impresso no final. É claro que touch funciona bem:

%pre%

Criando o arquivo com null?

Agora vamos tentar criar um arquivo com caractere nulo:

%pre%

O resultado é exatamente o mesmo que em mkdir , parece que null é ignorado todos juntos. E se pularmos aspas duplas em torno da substituição de comandos?

%pre%

Esta é a mesma situação que $'%code%' / %code% sem argumentos. Ainda outro resultado é se nós cercarmos null com texto:

%pre%

Também é possível tentar redirecionar a saída padrão para %code% , mas tudo o que se obtém é um tipo diferente de erro.

    
por 18.08.2015 / 03:20
4

Aspas simples / duplas vs. barra invertida: as aspas simples e as barras invertidas são equivalentes na potência de aspas. É muito mais conveniente usar aspas simples para citar uma string longa com espaços, tabulações, novas linhas, ()[]*$><?|{}~&;\"'^!# e provavelmente outros caracteres que estou esquecendo. Mas você poderia alcançar resultados exatamente equivalentes com apenas barras invertidas (cuidado com a sobrecarga de barras invertidas dentro dos backticks ( '...' ))

As aspas duplas são únicas, no entanto. $ expande aspas duplas, mas não é único. "$ foo" expande foo, mas protege o resultado expandido da expansão de globulação e expansão de palavras.

O

link pode ser um bom lugar para começar. O manual bash não gasta muito tempo em como usar todos os recursos que descreve, exatamente como eles funcionam individualmente.

Não é literalmente possível passar uma string contendo um zero-byte como um argumento de linha de comando, ou para uma chamada de sistema. A ABI (application binary interface) que especifica exatamente como os dados são passados entre os processos e o kernel usa strings C para tudo (exceto dados binários), incluindo argumentos de linha de comando e argumentos file / path para chamadas do sistema. Strings C são arrays de caracteres onde o fim da string é marcado por um byte zero. Não há como "escapar" de um byte zero para indicar que não é o fim de uma string.

Qualquer tentativa de fazer algo como touch $'footouchbar' resultaria em argv[1] = "fooexevce(2)bartouch" vendo sua lista de argumentos como

argv[0] = "/bin/touch";
argv[1] = "foo";

Mesmo sentado na memória, open(2) , o primeiro read(2) marca o final da string. Na verdade, "foo \ 0bar \ 0" não chegaria até o argv do novo processo. Ele não sairia da matriz argv na chamada do sistema write(2) que executou $'string' .

E mesmo que você tenha escrito um programa C ou perl com arrays / strings de caracteres contendo bytes nulos, passá-los para uma chamada de sistema como tr causaria a mesma interpretação da string pelo kernel. Chamadas de sistema que precisam manipular dados arbitrários, como printf '%b' 'foo%code%bar' e %code% , usam um argumento de comprimento e um ponteiro para o buffer.

Nem é possível fazer muita coisa com bytes nulos com o bash. Como jimmij aponta, a sintaxe bash para escrever um literal de string com o processamento de seqüência de escape é %code% , mas escrever um %code% em sua string literal atua como um terminador de string dentro do bash. Eu acho que isso significa que o bash armazena strings internamente como strings C, não com um tamanho explícito.

str=$'foo
argv[0] = "/bin/touch";
argv[1] = "foo";
bar' echo "${#str}" # 3, showing that bash isn't even storing it in a variable. echo "$str" | wc -c # 4. wouldn't work even if ${#str} did: echo's cmdline would eat it wc -c <<< $'foo
str=$'foo%pre%bar'
echo "${#str}"   # 3, showing that bash isn't even storing it in a variable.
echo "$str" | wc -c   # 4. wouldn't work even if ${#str} did: echo's cmdline would eat it
wc -c <<< $'foo%pre%bar'   # 4 (includes a newline)
bar' # 4 (includes a newline)

Portanto, não podemos usar essa sintaxe para enviar um byte nulo a qualquer lugar. Teríamos que usar %code% ou algo assim.

Como jimmij aponta, porém, podemos usar %code% para imprimir bytes nulos para stdout.

    
por 18.08.2015 / 04:45
3

Como você sabe, $var levará a interpretar a variável. A razão pela qual as diferentes opções funcionam varia:

  • o escape ( \$var ): NÃO interprete o caractere seguinte como caractere funcional do shell. MAS em alguns casos: dar um significado especial (por exemplo, \n para nova linha em alguns contextos)
  • as aspas simples ( '$var' ): tudo nas aspas simples será estritamente nada além da própria cadeia que elas contêm
  • a separação de $ ( "$"var ): um único $ não será interpretado, colocando-o entre aspas duplas é separado da parte var e nenhuma interpretação acontece
  • as aspas duplas ( "$var" ): permitirão, na verdade, interpretar a variável var : mkdir "$var" NÃO FUNCIONA E NÃO É O MESMO QUE OS OUTROS! Por favor cheque novamente! No entanto, qualquer coisa contida dentro das cotações será tratada como uma única picada. Especialmente útil quando você tem caracteres especiais em nomes de arquivos, por exemplo criando um arquivo com um espaço em seu nome: touch "a b" - > arquivo único a b criado / atualizado, touch a b - > dois arquivos a e b criados / atualizados.

Outros operadores especiais são: redirecionamentos e 'heres' > >> < << <<< , operadores de processo & | , operadores booleanos || && e separadores de comando ; e agrupamento com parênteses ( ) , às vezes - mas separados ou como primeiro caractere - o - para stdin ou opções de comandos. Também há o comando de teste [ e as citações que já usamos ' " , além de lembrar os comandos anteriores com um ponto de exclamação ! ou comentários com um hash # e os caracteres curinga asterisco * e pergunta marque ? para caracteres múltiplos e únicos. Observe também que o diretório atual e o diretório pai são . e .. , enquanto a página inicial é definida como ~/ . Ou seja os caracteres ; & | > < - [ \ ' " ( ) # * ! ? . ~ ^ { } , ' , nova linha, espaço, tabulação (e os outros caracteres em branco em localidades de byte único) devem ser examinados duas vezes, mas nem todos são "perigosos" no mesmo nível. Espero não ter esquecido nenhum, pois há muitos deles.

    
por 18.08.2015 / 01:36
2

Nos nomes dos arquivos, '/' é proibido porque é um separador de diretórios. Essa é a única razão. E se você editar manualmente um sistema de arquivos, você poderá até mesmo criar um arquivo com '/' no nome (não recomendado, pois você não poderá fazer muito com ele).

O caractere NUL não pode ser usado como parte do nome do arquivo, porque as chamadas de sistema relevantes usam convenções de passagem de string da linguagem C, e NUL é o terminador para essa string. Portanto, não pode ser interpretado como parte do nome.

Note que criar um arquivo chamado '\' não é o mesmo que criar um arquivo contendo um NUL - o primeiro é um nome de arquivo contendo os dois caracteres '0' e %code% .

    
por 18.08.2015 / 15:20