Null et caractères d'échappement

Je sais qu'un nom de file sous Linux n'a aucune ressortingction, sauf pour deux caractères '/' et '\0' . Je sais que '/' est interdit car il s'agit d'un séparateur de directorys mais existe-t-il une autre raison ?

Aussi sur mon terminal je peux créer un file ou un directory avec le nom \0 . Je me request donc comment écrire correctement le caractère nul car il ne faut évidemment pas me permettre d'avoir un nom de file avec null

mkdir '\0' va créer un directory nommé \0

Une autre question, Si je veux inclure $ dans mon nom de file, je peux utiliser la barre oblique inverse

mkdir \$myfile va créer un directory nommé $myfile

Cependant, je peux faire la même chose si j'entoure le signe dollar avec des guillemets simples et des guillemets

mkdir \$myfile est le même que mkdir '$'myfile est le même que mkdir "$"myfile est le même que mkdir '$myfile' est le même que mkdir "$myfile"

Ma question est donc la suivante: les guillemets simples et doubles sont-ils une substitution pour le caractère antislash d'échappement?

Aussi ce que les autres personnages ont besoin d'échapper à bash en plus de $ , (espace) et barre oblique inverse?

Comme vous le savez, $var va conduire à l'interprétation de la variable. La raison pour laquelle les différentes options fonctionnent varie:

  • l'échappement ( \$var ): n'interprète PAS le caractère suivant comme caractère fonctionnel du shell. MAIS dans certains cas: donnez une signification particulière (par exemple \n pour la nouvelle ligne dans certains contexts)
  • les guillemets simples ( '$var' ): tout ce qui se trouve entre guillemets ne sera ssortingctement rien d'autre que la string qu'ils contiennent
  • la séparation de $ ( "$"var ): un seul $ ne sera pas interprété, en le plaçant entre guillemets, il est séparé de la partie var et aucune interprétation ne se produit
  • les guillemets doubles ( "$var" ): permettent réellement d'interpréter la variable var : mkdir "$var" NE FONCTIONNE PAS ET N'EST PAS LE MEME QUE LES AUTRES! S'il vous plaît vérifier à nouveau! Cependant, tout contenu contenu dans les guillemets sera traité comme une piqûre unique. Particulièrement utile lorsque vous avez des caractères spéciaux dans les noms de files, par exemple en créant un file avec un espace dans son nom: touch "ab" -> file unique ab créé / mis à jour, touch ab -> deux files a et b créés / mis à jour.

Les autres opérateurs spéciaux sont: redirections et 'heres' > >> < << <<< , opérateurs de process & | , opérateurs booleans || && || && , et séparateurs de commands ; et regroupement avec des parenthèses ( ) , parfois – mais ensuite séparés ou comme premier caractère – le - pour stdin ou des options de commands. Il y a aussi la command test [ et les guillemets que nous avons déjà utilisés ' " , ainsi que le callback des commands précédentes avec un point d'exclamation ou des commentaires avec un hash # et l'astérisque * et un point d'interrogation pour les caractères multiples et simples. notez que le directory actuel et le directory parent sont . et .. , alors que la maison est définie sur ~/ . ; & | > < - [ \ ' " ( ) # * ! ? . ~ ^ { } ; & | > < - [ \ ' " ( ) # * ! ? . ~ ^ { } , ` newline, space, tab (et les autres caractères vides dans les locales à un octet) doivent être regardés deux fois mais tous ne sont pas" dangereux "sur le même niveau. J'espère que j'en ai oublié car il y en a pas mal.

Guillemets simples / doubles et barre oblique inverse: les guillemets simples et les barres obliques inverses sont équivalents en termes de puissance. Il est beaucoup plus pratique d'utiliser des guillemets simples pour citer une longue string avec des espaces, des tabulations, des returns à la ligne, ()[]*$><?|{}~&;\"`^!# Et probablement d'autres caractères que j'oublie Mais vous pouvez get des résultats exactement équivalents avec juste des backslashes (attention à la surcharge des backslashes dans les backticks ( `...` ))

Les guillemets doubles sont uniques, cependant. $ expand à l'intérieur des guillemets, mais pas simple. "$ foo" développe foo, mais protège le résultat étendu du fractionnement de mots et de l'expansion glob.

http://mywiki.wooledge.org/BashFAQ pourrait être un bon endroit pour commencer. Le manuel bash ne consacre pas beaucoup de time à l'utilisation de toutes les fonctionnalités qu'il décrit, à la façon dont ils fonctionnent individuellement.


Il est littéralement impossible de passer une string contenant un zéro octet comme argument de command line ou un appel système. L'interface ABI (application binary interface) qui spécifie exactement comment datatables sont transmises entre les process et le kernel utilise les strings C pour tout (sauf datatables binarys), y compris les arguments de command line et les arguments file / path pour les appels système. Les strings C sont des arrays de caractères où la fin de string est marquée par un zéro. Il n'y a aucun moyen de "s'échapper" d'un octet zéro pour indiquer que ce n'est pas la fin d'une string.

Toute tentative de faire quelque chose comme touch $'foo\0bar' aboutirait simplement à touch voir sa list d'arguments comme

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

Même si vous êtes assis en memory, argv[1] = "foo\0bar\0" , le premier \0 marque la fin de la string. En fait, "foo \ 0bar \ 0" ne le ferait pas aussi loin que le nouveau process argv. Il ne serait pas sorti du tableau argv dans l'appel système exevce(2) qui a fonctionné touch .

Et même si vous avez écrit un programme C ou perl avec des arrays / strings de caractères contenant des octets Nuls, les passer à un appel système comme open(2) entraînerait la même interprétation de la string par le kernel. Les appels système qui doivent gérer des données arbitraires, comme read(2) et write(2) , prennent un argument de longueur ainsi qu'un pointeur vers le buffer.


Il n'est même pas possible de faire beaucoup de choses avec des octets Nuls avec bash. Comme le souligne jimmij, la syntaxe bash pour écrire une string littérale avec le traitement de la séquence d'échappement est $'ssortingng' , mais écrire un \0 dans votre string littérale agit comme une terminaison de string dans bash. Je suppose que cela signifie bash stocke les strings en interne en tant que strings C, pas avec une longueur explicite.

 str=$'foo\0bar' 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\0bar' # 4 (includes a newline) 

Nous ne pouvons donc pas utiliser cette syntaxe pour envoyer un octet nul n'importe où. Nous devrions utiliser tr ou quelque chose.

Comme jimmij le souligne, cependant, nous pouvons utiliser printf '%b' 'foo\0bar' pour imprimer les octets NULL en stdout.

Impression du caractère nul

Sur de nombreux shells récents, vous pouvez écrire un caractère nul avec les guillemets simples au format $'\0' , au format hexadécimal \x00 , au format unicode \u0000 ou \U00000000 , ou tout simplement avec '\0' octal '\0' . Le point est que la command doit comprendre ce qu'il faut faire avec les caractères antislash-échappés. Par exemple en cas d' echo il faut généralement append l'option -e et dans le cas d' printf qui serait %b .

Vérifions si cela fonctionne:

 $ echo -ne '\0' $ 

Donc ne produit rien, tout comme echo -ne '' , similaire

 $ printf '%b' '\0' $ 

Ajoutons quelques caractères (je m'en printf '%b' à printf '%b' partir de maintenant comme plus robuste, mais l'effet similaire est avec echo -ne ):

 $ printf '%b' a'\0'b ab 

Seulement deux caractères ont été imprimés, où est passé le nul ?

 $ printf '%b' a'\0'b | wc -c 3 

Comparons-le avec a''b :

 $ printf '%b' a''b | wc -c 2 

Enfin, vérifiez que nous imprimons vraiment le caractère null avant d'essayer de créer un file, passons la valeur imprimée à la command qui lèvera l'erreur, comme xargs :

 $ printf '%b' a'\0'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 

Remarquez comment seulement a été imprimé à la fin. Bien sûr, xargs -0 fonctionne bien:

 $ printf '%b' a'\0'b | xargs -0 echo ab 

Créer le file avec null?

Maintenant essayons de créer le file avec le caractère nul:

 $ touch $'\0' touch: cannot touch '': No such file or directory $ mkdir $'\0' mkdir: cannot create directory '': No such file or directory # let's try another approach - using printf in command substitution: $ touch "$(printf '%b' '\0')" touch: cannot touch '': No such file or directory $ mkdir "$(printf '%b' '\0')" mkdir: cannot create directory '': No such file or directory 

Le résultat est exactement le même que dans le touch '' , il semble null est simplement ignoré tous set. Et si nous sautons des guillemets doubles autour de la substitution de commands?

 $ touch $(printf '%b' '\0') touch: missing file operand Try 'touch --help' for more information. $ mkdir $(printf '%b' '\0') mkdir: missing operand Try 'mkdir --help' for more information. 

C'est la même situation que touch / mkdir sans arguments du tout. Encore un autre résultat est si nous entourons null avec du text:

 $ touch "$(printf '%b' a'\0'b)" $ ls a # in zsh ab # in bash 

On peut aussi essayer de redirect la sortie standard vers $'\0' mais tout ce que l'on obtient est un type d'erreur différent.

Dans les noms de files, '/' est interdit car il s'agit d'un séparateur de directorys. C'est la seule raison. Et si vous éditez manuellement un système de files, vous pourriez même créer un file avec '/' dans le nom (pas recommandé, car vous ne pourrez pas en faire grand-chose).

Le caractère NUL ne peut pas être utilisé comme nom de file, car les appels système pertinents utilisent des conventions de passage de string C et NUL est le terminateur d'une telle string. Donc, il ne peut pas être interprété comme faisant partie du nom.

Notez que la création d'un file appelé \0 n'est pas la même que la création d'un file contenant un NUL – le premier est un nom de file contenant les deux caractères '\' et '0' .