Dans certains shells de type Bourne, le read
builtin ne peut pas lire toute la ligne à partir du fichier /proc
(la commande ci-dessous doit être exécutée zsh
, remplacez-la $=shell
par $shell
d'autres shells):
$ for shell in bash dash ksh mksh yash zsh schily-sh heirloom-sh "busybox sh"; do
printf '[%s]\n' "$shell"
$=shell -c 'IFS= read x </proc/sys/fs/file-max; echo "$x"'
done
[bash]
602160
[dash]
6
[ksh]
602160
[mksh]
6
[yash]
6
[zsh]
6
[schily-sh]
602160
[heirloom-sh]
602160
[busybox sh]
6
read
la norme requiert que l'entrée standard soit un fichier texte , cette exigence provoque-t-elle des comportements variés?
Lisez la définition POSIX du fichier texte , je fais quelques vérifications:
$ od -t a </proc/sys/fs/file-max
0000000 6 0 2 1 6 0 nl
0000007
$ find /proc/sys/fs -type f -name 'file-max'
/proc/sys/fs/file-max
Il n'y a aucun NUL
caractère dans le contenu de /proc/sys/fs/file-max
, et l'a également find
signalé en tant que fichier normal (est-ce un bogue dans find
?).
Je suppose que la coquille a fait quelque chose sous le capot, comme file
:
$ file /proc/sys/fs/file-max
/proc/sys/fs/file-max: empty
strace
explication à base est beaucoup plus facile à comprendre!cat /proc/sys/fs/file-max | ...
, le problème a disparu.procfs
ne peut pas gérer plusieursread(2)
appels successifs vers le même fichier; le comportement ne dépend pas du shell. L'utilisationcat
et la tuyauterie fonctionnent carcat
lit le fichier en morceaux assez grands; le shellread
intégré lit ensuite dans le tube un caractère à la fois.mksh
.read -N 10 a < /proc/sys/fs/file-max
zsh
:read -u0 -k10
(ou utilisersysread
;$mapfile[/proc/sys/fs/file-max]
ne fonctionne pas car ces fichiers ne peuvent pas êtremmap
édités). Dans tous les cas, avec n'importe quel shell, on peut toujours le fairea=$(cat /proc/sys/fs/file-max)
. Avec certains, y comprismksh
,zsh
etksh93
,a=$(</proc/sys/fs/file-max)
fonctionne également et ne bifurque pas un processus pour faire la lecture.Si vous souhaitez savoir pourquoi? c'est ainsi, vous pouvez voir la réponse dans les sources du noyau ici :
Fondamentalement, la recherche (
*ppos
pas 0) n'est pas implémentée pour les lectures (!write
) des valeurs sysctl qui sont des nombres. Chaque fois qu'une lecture est effectuée à partir de/proc/sys/fs/file-max
, la routine en question__do_proc_doulongvec_minmax()
est appelée à partir de l'entrée pourfile-max
dans la table de configuration dans le même fichier.D'autres entrées, telles que celles
/proc/sys/kernel/poweroff_cmd
implémentées viaproc_dostring()
qui permettent les recherches, vous permettent de le fairedd bs=1
et de lire à partir de votre shell sans problème.Notez que depuis le noyau 2.6, la plupart des
/proc
lectures ont été implémentées via une nouvelle API appelée seq_file et que cela prend en charge la recherche, par exemple, la lecture/proc/stat
ne devrait pas poser de problème. L'/proc/sys/
implémentation, comme nous pouvons le voir, n'utilise pas cette API.la source
À la première tentative, cela ressemble à un bug dans les shells qui retournent moins qu'un vrai Bourne Shell ou ses dérivés reviennent (sh, bosh, ksh, heirloom).
Le Bourne Shell d'origine essaie de lire un bloc (64 octets). Les nouvelles variantes du Bourne Shell lisent 128 octets, mais elles recommencent la lecture s'il n'y a pas de nouveau caractère de ligne.
Contexte: / procfs et les implémentations similaires (par exemple le
/etc/mtab
fichier virtuel monté ) ont un contenu dynamique et unstat()
appel ne provoque pas la recréation du contenu dynamique en premier. Pour cette raison, la taille d'un tel fichier (de la lecture jusqu'à l'EOF) peut différer de ce qui eststat()
renvoyé.Étant donné que la norme POSIX oblige les utilitaires à s'attendre à de courtes lectures à tout moment, les logiciels qui croient
read()
qu'une valeur renvoyant moins que la quantité d'octets ordonnée est une indication EOF sont rompus. Un utilitaire correctement implémenté appelleread()
une deuxième fois dans le cas où il renvoie moins que prévu - jusqu'à ce qu'un 0 soit renvoyé. En cas deread
builtin, il serait bien sûr suffisant pour lire jusqu'à ce queEOF
ou jusqu'à ce qu'unNL
est vu.Si vous exécutez
truss
ou un clone de ferme, vous devriez pouvoir vérifier ce comportement incorrect pour les shells qui ne reviennent que6
dans votre expérience.Dans ce cas particulier, il semble s'agir d'un bogue du noyau Linux, voir:
Le noyau Linux renvoie 0 avec le second
read
et c'est bien sûr incorrect.Conclusion: les shells qui essaient d'abord de lire un bloc de données suffisamment volumineux ne déclenchent pas ce bogue du noyau Linux.
la source
Les fichiers sous / proc utilisent parfois le caractère NULL pour séparer les champs à l'intérieur du fichier. Il semble que la lecture ne soit pas en mesure de gérer cela.
la source