2016-01-20 3 views
1

J'ai réduit un problème auquel je fais face à un MWE. Quelqu'un peut-il confirmer si c'est un bug ou s'il me manque quelque chose de rudimentaire?Pourquoi l'état de sortie de l'appel system() dans Ruby change-t-il avec la redirection?

Plate-forme:

  • Ruby 1.9.3p484 (révision 22.11.2013 43786) [x86_64 Linux]
  • bash GNU version 4.3.11 (1) -release (x86_64- pc-linux-gnu)
  • Ubuntu 14.04.2 x86_64

Étape 1: Créer un script Ruby comme suit nommé demo.rb

ret = system("./segfault") 
print "Return value: ", ret, "\n" 
print "Exit status: ", ($?.exitstatus) ? $?.exitstatus : "nil", "\n" 
print "Status code: ", $?.to_i, "\n" 
puts "-----------------------------------" 
ret = system("./segfault 2>&1") 
print "Return value: ", ret, "\n" 
print "Exit status: ", ($?.exitstatus) ? $?.exitstatus : "nil", "\n" 
print "Status code: ", $?.to_i, "\n" 

Étape 2: Créer un programme C comme suit le nom segfault.c

#include <string.h> 

int main() 
{ 
    memset((char *)0x0, 1, 100); 
    return 1; 
} 

Étape 3: Compile le programme C.

gcc segfault.c -o segfault 

Étape 4: Exécutez le script Ruby.

$ ruby segfault.rb 
Return value: false 
Exit status: nil 
Status code: 139 
----------------------------------- 
sh: line 1: 8181 Segmentation fault  (core dumped) ./segfault 2>&1 
Return value: false 
Exit status: 139 
Status code: 35584 

Je ne peux pas comprendre pourquoi l'état de sortie et les changements de code d'état avec stderr étant redirigent à stdout. Quelqu'un at-il une explication raisonnable de ce comportement?

Répondre

0

La commande que vous donnez au système peut prendre plusieurs formes. Si c'est juste le chemin de la commande ou si c'est un chemin et un tableau d'arguments alors ruby ​​forks et execs directement cette commande.

Si c'est quelque chose de plus compliqué alors il exécute ce qu'il considère comme le shell standard et passe la commande au shell.

La raison pour cela est pertinent est dans la documentation pour exitstatus qui disent:

Renvoie les moins huit bits significatifs du code de retour de stat. Uniquement disponible si exited? est true.

Et les docs pour exited? dire

Renvoie true si stat est sorti normalement (par exemple en utilisant un appel exit() ou la fin du programme).

Votre binaire c ne sort pas normalement, donc dans votre premier cas exited? est faux et exitstatus retour précis ou chiffré NiL Dans le second cas, alors que votre binaire ne parvient toujours pas de la même manière, le processus réel exécuté par Ruby était une instance de votre coquille, qui a quitté normalement.

Je reçois des résultats différents en ce qui concerne $ ?. to_i.Les 8 bits bas contiennent des informations telles que si un signal a été élevé et les 8 bits hauts contiennent l'état de sortie. Je reçois 11 et 35584 (139 < < 8) dans vos 2 cas, mais votre version de ruby ​​semble se comporter comme bash et ajouter 128 à la valeur du signal (segfault est le signal 11). Dans les deux cas, la cause de la différence est que la commande est exécutée via un shell: le shell voit le processus échoué et transforme sa valeur de signal en statut de sortie (stocké dans les 8 bits élevés) alors que lorsqu'il est exécuté directement, aucun état de sortie et aucune information d'erreur ne se trouvent dans les 8 bits inférieurs.

En aparté, vous pouvez rediriger la sortie en utilisant les options transmises au système par exemple

system("mycommand", err: :out) 

stderr vers stdout que vous faisiez

+0

Même moi, je l'ai vu '$ ?. to_i == 11' en autres cas. Je ne suis pas en mesure de reproduire la même chose cependant. Il apparaît juste dans un autre programme plus grand, apparemment au hasard. Par conséquent, je ne peux pas vraiment compter sur '$?'. Quelle combinaison de Bash/Ruby utilisez-vous? –

+0

Si j'utilise 'system (" ./ segfault ", err:: out), ce qui suit n'apparaît pas:' sh: ligne 1: 8181 Erreur de segmentation (core dumped) ./segfault 2> & 1' –

+0

Ruby 2.3 .0, bash 3.2.57. Personnellement j'éviterais toujours l'utilisation de la coquille en invoquant la forme du système –