En utilisant nohup pour mettre une commande à exécuter en arrière-plan, certains contenus apparaissent dans le terminal.
cp: error reading ‘/mnt/tt/file.txt’: Input/output error
cp: failed to extend ‘/mnt/tt/file.txt’: Input/output error
Je veux sauvegarder ce contenu dans un fichier.
Il existe deux flux de sortie principaux sous Linux (et d'autres systèmes d'exploitation), la sortie standard (stdout) et l'erreur standard (stderr). Les messages d'erreur, comme ceux que vous affichez, sont imprimés sur l'erreur standard. L'opérateur de redirection classique (command > file
) ne redirige que la sortie standard, donc l'erreur standard est toujours affichée sur le terminal. Pour rediriger également stderr, vous avez plusieurs choix :
Rediriger stdout vers un fichier et stderr vers un autre fichier :
commande > out 2>error
Rediriger stdout vers un fichier (>out
), puis rediriger stderr vers stdout (2>&1
) :
commande >out 2>&1
Rediriger les deux vers un fichier (ceci n'est pas supporté par tous les shells, bash
et zsh
le supportent, par exemple, mais sh
et ksh
ne le font pas) :
commande &> out
Pour plus d'informations sur les différents opérateurs de contrôle et de redirection, voir ici.
La première chose à noter est qu’il y a plusieurs façons de procéder en fonction de votre objectif et de votre shell, ce qui nécessite une légère compréhension de plusieurs aspects. De plus, certaines commandes telles que time
et strace
écrivent la sortie sur stderr par défaut, et peuvent ou non fournir une méthode de redirection spécifique à cette commande.
La théorie de base derrière la redirection est qu'un processus créé par l'interpréteur de commandes (en supposant qu'il s'agit d'une commande externe et non intégrée à l'interpréteur de commandes) est créé via les appels syscall fork()
et execve()
, et avant que cela n'arrive, un autre appel syscall dup2()
effectue les redirections nécessaires avant que execve()
n'arrive. Dans ce sens, les redirections sont héritées du shell parent. Les fichiers m&>n
et m>n.txt
informent le shell sur la façon d'exécuter les appels syscall open()
et dup2()
(voir aussi Comment fonctionne la redirection d'entrée, Quelle est la différence entre redirection et pipe, et Que signifie exactement & ; dans la redirection de sortie )
La plus typique est via 2>
dans les [shells de type Bourne][4], comme dash
(qui est lié par un lien symbolique à /bin/sh
) et bash
; le premier est le shell par défaut et conforme à POSIX et l'autre est ce que la plupart des utilisateurs utilisent pour les sessions interactives. Ils diffèrent dans leur syntaxe et leurs fonctionnalités, mais heureusement pour nous, la redirection des flux d'erreurs fonctionne de la même manière (sauf pour le &>
non standard). Dans le cas de csh et de ses dérivés, la redirection stderr [ne fonctionne pas vraiment][6].
Revenons à la partie "2>". Deux choses essentielles à noter :
>signifie opérateur de redirection, où nous ouvrons un fichier et
2` entier représente le descripteur de fichier stderr ; en fait, c'est exactement comme cela que le standard POSIX pour le langage shell définit la redirection dans [section 2.7][7] :
[n]redir-op word
Pour une simple redirection >
, l'entier 1
est implicite pour stdout
, c'est-à-dire que echo Hello World > /dev/null
est juste la même chose que echo Hello World 1>/dev/null
. Notez que les entiers ou les opérateurs de redirection ne peuvent pas être cités, sinon l'interpréteur de commandes ne les reconnaît pas comme tels et les traite comme des chaînes de texte littérales. Quant à l'espacement, il est important que l'entier soit juste à côté de l'opérateur de redirection, mais le fichier peut être à côté de l'opérateur de redirection ou non, c'est-à-dire que command 2>/dev/null
et command 2> /dev/null
fonctionneront très bien.
La syntaxe quelque peu simplifiée d'une commande typique en shell serait la suivante
command [arg1] [arg2] 2> /dev/null
L'astuce ici est que la redirection peut apparaître n'importe où. C'est-à-dire que 2> command [arg1]
et command 2> [arg1]
sont tous deux valides. Notez que pour l'interpréteur de commandes bash
, il existe un moyen de rediriger les flux stdout et stderr en même temps, mais encore une fois, c'est spécifique à bash et si vous cherchez à rendre vos scripts portables, cela peut ne pas fonctionner. Voir également [Ubuntu Wiki][8] et Quelle est la différence entre &> et 2>&1.
Remarque : L'opérateur de redirection >
tronconne* un fichier et l'écrase, si le fichier existe. Le 2>>
peut être utilisé pour ajouter stderr
au fichier.
Si vous remarquez, >
est destiné à une seule commande. Pour les scripts, nous pouvons rediriger le flux stderr du script entier depuis l'extérieur comme dans myscript.sh 2> /dev/null
ou nous pouvons faire usage de exec built-in. Le exec built-in a le pouvoir de rediriger le flux pour l'ensemble de la session shell, pour ainsi dire, que ce soit de manière interactive ou via un script. Quelque chose comme
#!/bin/sh
exec 2> ./my_log_file.txt
stat /etc/non_existing_file
Dans cet exemple, le fichier journal devrait montrer stat : cannot stat '/etc/non_existing_file' : No such file or directory
.
Une autre méthode consiste à utiliser les fonctions. Comme [kopciuszek][4] l'a noté dans sa réponse, nous pouvons écrire une déclaration de fonction avec une redirection déjà attachée, c'est-à-dire
some_function(){
command1
command2
} 2> my_log_file.txt
Les commandes telles que time
et strace
écrivent leur sortie sur stderr par défaut. Dans le cas de la commande time
, la seule alternative viable est de rediriger la sortie de l'ensemble de la commande, à savoir
time echo foo 2>&1 > file.txt
Alternativement, la liste synchrone ou le sous-shell peuvent être redirigés si vous voulez séparer la sortie ( comme montré dans related post ) :
{ time sleep 1 2> sleep.stderr ; } 2> time.txt
D'autres commandes, telles que strace
ou dialog
fournissent des moyens de rediriger stderr. strace
a une option -o <filename.txt>
qui permet de spécifier le nom du fichier où la sortie doit être écrite. Il y a aussi une option pour écrire un fichier texte pour chaque sous-processus que strace
voit. La commande dialog
écrit l'interface utilisateur en texte vers stdout mais la sortie vers stderr, donc pour enregistrer sa sortie dans une variable ( parce que var=$(...)
et que pipelines ne reçoit que stderr ) nous devons échanger les descripteurs de fichiers
result=$(dialog --inputbox test 0 0 2>&1 1>/dev/tty);
mais en plus, il y a le drapeau --output-fd
, que nous pouvons aussi utiliser. Il y a aussi la méthode des pipes nommés. Je vous recommande de lire le post lié sur la commande dialog
pour une description complète de ce qui se passe.
[4] : https://unix.stackexchange.com/users/181255/kopciuszek
[6] : https://unix.stackexchange.com/q/35715/85039 [7] : http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.htm [8] : https://wiki.ubuntu.com/DashAsBinSh