Qu'est-ce qui génère le message «fichier texte occupé» sous Unix?

137

Quelle opération génère l'erreur "fichier texte occupé"? Je suis incapable de dire exactement.

Je pense que cela est lié au fait que je crée un script python temporaire (en utilisant tempfile) et en utilise execl, mais je pense que execl change le fichier en cours d'exécution.

Penz
la source

Réponses:

130

Cette erreur signifie qu'un autre processus ou utilisateur accède à votre fichier. Utilisez lsofpour vérifier quels autres processus l'utilisent. Vous pouvez utiliser la killcommande pour le tuer si nécessaire.

jaypal singh
la source
115
L' Text file busyerreur spécifique consiste à essayer de modifier un exécutable pendant son exécution. Le "Texte" fait ici référence au fait que le fichier en cours de modification est le segment de texte d'un programme en cours d'exécution. Il s'agit d'un cas très particulier, et non du cas générique que semble suggérer votre réponse. Même ainsi, votre réponse n'est pas entièrement incorrecte.
ArjunShankar
4
La réponse avec le commentaire semble complète.
Penz
L'OP a demandé quelle opération génère l'erreur, pas pour une explication de ce que signifie l'erreur.
WonderWorker
Je pense que le fait qu'unix suppose que les fichiers sont des "fichiers texte" est illogique, dans mon cas, c'était un fichier binaire qui a provoqué cette erreur.
Felipe Valdes
1
@FelipeValdes Le nom est historique de la terminologie d'il y a un demi-siècle. Par exemple, dans les multics, le segment de texte d'un programme était distinct du segment de lien, et même auparavant, les gens parlaient de texte binaire. stackoverflow.com/a/1282540/833300
jma
30

Cela fait un moment que je n'ai pas vu ce message, mais il était répandu dans System V R3 ou à peu près il y a une bonne vingtaine d'années. À l'époque, cela signifiait que vous ne pouviez pas modifier un exécutable de programme pendant son exécution.

Par exemple, je construisais un makeworkalike appelé rmk, et après un certain temps, il s'est auto-entretenu. Je lancerais la version de développement et lui ferais construire une nouvelle version. Pour le faire fonctionner, il était nécessaire d'utiliser la solution de contournement:

gcc -g -Wall -o rmk1 main.o -L. -lrmk -L/Users/jleffler/lib/64 -ljl
if [ -f rmk ] ; then mv rmk rmk2 ; else true; fi ; mv rmk1 rmk

Ainsi, pour éviter les problèmes avec le 'fichier texte occupé', la construction a créé un nouveau fichier rmk1, puis déplacé l'ancien rmkvers rmk2(renommer n'était pas un problème; dissocier l'était), puis déplacé le nouveau fichier rmk1vers rmk.

Je n'ai pas vu l'erreur sur un système moderne depuis un bon moment ... mais je n'ai pas souvent des programmes qui se reconstruisent.

Jonathan Leffler
la source
3
Voici un super rapide reproducer: echo -e '#include <unistd.h>\nint main(void){sleep (5);return 0;}' > slowprog.c && cc slowprog.c && cp a.out b.out && (./a.out &) ; sleep 1 && cp b.out a.out. Produit le message d'erreur "cp: impossible de créer le fichier régulier 'a.out': Fichier texte occupé" sur mon nouveau Fedora.
ArjunShankar
3
Bien sûr, cette réponse est correcte et obtient un +1. Vous souhaiterez peut-être supprimer la clause de non-responsabilité "Cela fait longtemps".
ArjunShankar
@ArjunShankar voici une reproduction C sur un Linux moderne avec des appels système "directs": stackoverflow.com/questions/16764946/... GCC ne peut écraser que les exécutables en cours de nos jours car si le premier fait un unlinkpar défaut.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
14

Cela se produit lorsque vous essayez d'écrire dans un fichier en cours d'exécution par le noyau ou que vous exécutez un fichier actuellement ouvert en écriture.

Source: http://wiki.wlug.org.nz/ETXTBSY

Messa
la source
6

Exemple de reproduction minimale C POSIX exécutable

Je recommande de comprendre l'API sous-jacente pour mieux voir ce qui se passe.

sommeil.c

#define _XOPEN_SOURCE 700
#include <unistd.h>

int main(void) {
    sleep(10000);
}

occupé.c

#define _XOPEN_SOURCE 700
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(void) {
    int ret = open("sleep.out", O_WRONLY|O_TRUNC);
    assert(errno == ETXTBSY);
    perror("");
    assert(ret == -1);
}

Compilez et exécutez:

gcc -std=c99 -o sleep.out ./sleep.c
gcc -std=c99 -o busy.out ./busy.c
./sleep.out &
./busy.out 

busy.outtransmet les assertions et les perrorsorties:

Text file busy

nous en déduisons donc que le message est codé en dur dans la glibc elle-même.

Alternativement:

echo asdf > sleep.out

rend la sortie Bash:

-bash: sleep.out: Text file busy

Pour une application plus complexe, vous pouvez également l'observer avec strace:

strace ./busy.out

qui contient:

openat(AT_FDCWD, "sleep.out", O_WRONLY) = -1 ETXTBSY (Text file busy)

Testé sur Ubuntu 18.04, noyau Linux 4.15.0.

L'erreur ne se produit pas si vous d' unlinkabord

notbusy.c:

#define _XOPEN_SOURCE 700
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main(void) {
    assert(unlink("sleep.out") == 0);
    assert(open("sleep.out", O_WRONLY|O_CREAT) != -1);
}

Ensuite, compilez et exécutez de manière analogue à ce qui précède, et ces affirmations sont acceptées.

Cela explique pourquoi cela fonctionne pour certains programmes mais pas pour d'autres. Par exemple, si vous faites:

gcc -std=c99 -o sleep.out ./sleep.c
./sleep.out &
gcc -std=c99 -o sleep.out ./sleep.c

cela ne génère pas d'erreur, même si le deuxième gccappel est en cours d'écriture sleep.out.

Un rapide stracemontre que GCC dissocie d'abord avant d'écrire:

 strace -f gcc -std=c99 -o sleep.out ./sleep.c |& grep sleep.out

contient:

[pid  3992] unlink("sleep.out")         = 0
[pid  3992] openat(AT_FDCWD, "sleep.out", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3

La raison pour laquelle il n'échoue pas est que lorsque vous unlinkréécrivez le fichier, il crée un nouvel inode et conserve un inode suspendu temporaire pour le fichier exécutable en cours d'exécution.

Mais si vous ne le faites writepas unlink, alors il essaie d'écrire dans le même inode protégé que l'exécutable en cours d'exécution.

POSIX 7 open()

http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html

[ETXTBSY]

Le fichier est un fichier de procédure pure (texte partagé) qui est en cours d'exécution et oflag est O_WRONLY ou O_RDWR.

homme 2 ouvert

ETXTBSY

chemin fait référence à une image exécutable en cours d'exécution et un accès en écriture a été demandé.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
1
La justification du cas de dissociation est que le fichier n'est plus accessible depuis ce répertoire, mais que l'inode est toujours là avec un refcount> 0. Si vous réutilisez le nom, c'est alors un nouveau fichier dans un nouvel inode - alors que si vous ne dissociez pas d'abord, vous essayez en fait d'écrire sur l'inode protégé.
Penz
@Penz obrigado pour le commentaire, Leandro. Je me demande aussi pourquoi, au contraire, cela donne-t-il une erreur si vous essayez d'écrire sans unlink. Linux a-t-il déjà lu le fichier plus d'une fois après le premier execappel?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Ce qui génère l'ETXTBSY, c'est la protection des inodes. Sans dissociation, toutes les écritures vont à l'inode qui est protégé par l'exécution du fichier; avec unlink, vous obtenez un nouvel inode qui n'est pas protégé. (pas sûr que "protégé" soit le terme ici, mais c'est l'idée)
Penz
5

Dans mon cas, j'essayais d'exécuter un fichier shell (avec une extension .sh) dans un environnement csh, et je recevais ce message d'erreur.

courir avec bash ça a marché pour moi. Par exemple

bash fichier.sh

Rafayel Paremuzyan
la source
1
Avait-il un en- #!/bin/bashtête?
Penz
Il a l'en-tête suivant #! / Bin / sh
Rafayel Paremuzyan
Vous voudrez peut-être essayer d'utiliser #!/usr/bin/cshou équivalent.
Penz
3

Si vous essayez de construire phpredissur une machine Linux, vous devrez peut-être lui laisser le temps de terminer la modification des autorisations de fichier, avec une sleepcommande, avant d'exécuter le fichier:

chmod a+x /usr/bin/php/scripts/phpize \
  && sleep 1 \
  && /usr/bin/php/scripts/phpize
Stéphane
la source
Je ne pense pas que chmodje reviendrais avant que les autorisations ne soient définies. Cela pourrait être un problème de système de fichiers.
Penz
Cela s'est produit à l'intérieur d'une image Docker en cours de création.
Stephane
1
Docker a plusieurs pilotes de stockage, je suppose que tous ne sont pas parfaits.
Penz
Néanmoins, c'est un très bon indice pour les personnes rencontrant ce problème lors de la création d'une image docker.
Maciej Gol
2

Je ne connais pas la cause mais je peux contribuer à un travail rapide et facile.

Je viens de faire l'expérience de cette bizarrerie sur CentOS 6 après "cat> shScript.sh" (coller, ^ Z) puis en éditant le fichier dans KWrite. Curieusement, il n'y avait aucune instance discernable (ps -ef) du script en cours d'exécution.

Mon travail rapide était simplement de "cp shScript.sh shScript2.sh" puis j'ai pu exécuter shScript2.sh. Ensuite, j'ai supprimé les deux. Terminé!

ScottWelker
la source
Votre problème vient du fait que vous avez suspendu le catprocessus. La prochaine fois, utilisez ^ D, pas ^ Z.
Vladimir Panteleev
Tout à fait raison Vladimir. Merci! C'est ce que j'aurais fait dans l'invite DOS / CMD. Old habbits ... ne s'est pas passé depuis :)
ScottWelker
2

Cela peut être plus courant sur les partages réseau CIFS / SMB. Windows ne permet pas d'écrire un fichier lorsqu'un autre fichier est ouvert, et même si le service n'est pas Windows (il peut s'agir d'un autre produit NAS), il reproduira probablement le même comportement. Potentiellement, cela pourrait également être une manifestation d'un problème NAS sous-jacent vaguement lié au verrouillage / réplication.

Cameron Kerr
la source
2

Si vous exécutez le .sh à partir d'une connexion ssh avec un outil comme MobaXTerm, et si ledit outil a un utilitaire d'enregistrement automatique pour éditer le fichier distant à partir de la machine locale, cela verrouille le fichier.

La fermeture et la réouverture de la session SSH le résout.

Poutrathor
la source
1

Une de mes expériences:

Je modifie toujours le raccourci clavier par défaut de Chrome via l'ingénierie inverse. Après modification, j'ai oublié de fermer Chrome et j'ai exécuté ce qui suit:

sudo cp chrome /opt/google/chrome/chrome
cp: cannot create regular file '/opt/google/chrome/chrome': Text file busy

En utilisant strace, vous pouvez trouver plus de détails:

sudo strace cp ./chrome /opt/google/chrome/chrome 2>&1 |grep 'Text file busy'
open("/opt/google/chrome/chrome", O_WRONLY|O_TRUNC) = -1 ETXTBSY (Text file busy)
firo
la source
0

Je suis tombé sur cela en PHP lors de l'utilisation fopen()sur un fichier, puis en essayant unlink()avant de l'utiliser fclose().

Pas bien:

$handle = fopen('file.txt');
// do something
unlink('file.txt');

Bien:

$handle = fopen('file.txt');
// do something
fclose($handle);
unlink('file.txt');
dtbarne
la source
Sur les fenêtres je suppose? Sous Linux, le système nous permet généralement de supprimer les fichiers ouverts - la référence dans le répertoire est éliminée, mais les données (inode) ne sont fred que lorsque le nombre de références atteint 0.
Penz
Non, c'était sur Centos.
dtbarne
Je l'ai testé sur Linux 4.7.10 avec le système de fichiers ext4 et il n'a produit aucune erreur, a fonctionné comme Penz l'a mentionné. Fichier supprimé avec succès. Peut-être que dtbarne utilise un système de fichiers spécial.
k3a
Exécutait ceci sur vagrant - peut-être parce qu'il s'agit d'un dossier partagé.
dtbarne
0
root@h1:bin[0]# mount h2:/ /x             
root@h1:bin[0]# cp /usr/bin/cat /x/usr/local/bin/
root@h1:bin[0]# umount /x
...
root@h2:~[0]# /usr/local/bin/cat 
-bash: /usr/local/bin/cat: Text file busy
root@h2:~[126]#

ubuntu 20.04, 5.4.0-40-generic
nfsd problem, after reboot ok
Rustem
la source
Veuillez ne pas publier uniquement le code comme réponse, mais aussi expliquer ce que fait votre code et comment il résout le problème de la question. Les réponses avec une explication sont généralement plus utiles et de meilleure qualité, et sont plus susceptibles d'attirer des votes positifs.
Mark Rotteveel le