Bizarre "No such file" erreur avec "xargs" et "file"

Je voudrais get le type MIME de tous les files sous le directory courant. Pourquoi ça ne marche pas? J'ai testé bash sur OSX ou Linux. "file" se plaint qu'il ne peut pas find le file, mais si je l'exécute avec le même path exact, il fonctionne.

$ find . -type f | xargs -n 1 -I FILE echo $(file --mime-type -b FILE) ERROR: cannot open './foo.txt' (No such file or directory) ... $ file --mime-type -b ./foo.txt text/plain 

Bien sûr, dans le monde réel, je ne fais pas "écho" à la réponse, j'ai besoin d'utiliser la string de type MIME dans une autre command.

C'est le plus simple que je pourrais faire le problème. Je suppose que je ne comprends pas quelque chose sur la substitution de nom de file xargs ?

La command $(file --mime-type -b FILE) est exécutée par le shell avant d'être transmise à xargs , c'est pourquoi vous ne voyez pas ce dont vous avez besoin. $(file --mime-type -b FILE) et transmettez-le à bash -c via xargs

 find . -type f | xargs -n 1 -I FILE bash -c 'echo $(file --mime-type -b FILE)' 

Il n'y a pas besoin d'utiliser xargs ici. Il suffit de faire appel à l'interrupteur -exec .

 $ find . -type f -exec file --mime-type -- {} + 

Exemples

sortie de file par défaut

 $ find 8* -type f -exec file --mime-type -- {} + | tail -5 89999/sample10.txt: text/plain 89999/sample2.txt: text/plain 89999/sample4.txt: text/plain 89999/sample6.txt: text/plain 89999/sample9.txt: text/plain 

file -b sortie

 $ find 8* -type f -exec file --mime-type -b -- {} + | head -5 application/x-empty text/x-perl text/plain text/plain text/plain 

Alternatives

Ceci est juste un FYI, mais il existe une autre command appelée mimetype que vous pouvez également utiliser pour faire la même chose que le file --mimetype . Cette command fait partie de ce packageage sur Fedora, perl-File-MimeInfo et fonctionne de la même manière:

 $ find 8* -type f -exec mimetype -- {} + | tail -5 89999/sample10.txt: text/plain 89999/sample2.txt: text/plain 89999/sample4.txt: text/plain 89999/sample6.txt: text/plain 89999/sample9.txt: text/plain 

Un exec plus élaboré

Étant donné que vous vous êtes renseigné sur «faire plus que simplement écho ..» et que vous essayez d'utiliser xargs je vais conclure que vous pensez que vous devrez faire plus de xargs avec xargs .

Mais si vous utilisez find , ce n'est généralement pas la meilleure façon d'y aller. Vous pouvez à la place utiliser find , puis dans le commutateur -exec appeler un shell et faire des choses plus compliquées ici, plutôt que d'essayer de convaincre xargs faire de manière plus complexe.

 $ find . -type f -exec sh -c ' cmd1; cmd2; file --mime-type -b "$@"; cmd3; cmd4; ' sh {} \; 

Remarque: que j'ai changé d'utiliser le + terminator qui transmet plusieurs arguments au file --mime-type ... à \; qui passe un à la fois.

Exemple

 $ find 8* -type f -exec sh -c ' echo -n "mime-type: "; file --mime-type -b "$@" ' sh {} \; |& tail -10 mime-type: text/plain mime-type: text/plain mime-type: text/plain mime-type: text/plain mime-type: text/x-shellscript mime-type: text/plain mime-type: text/plain mime-type: text/plain mime-type: text/plain mime-type: text/plain 

Votre problème concerne l'expansion du shell. $ () est développé avant d'être remis en argument. Si vous voulez le traiter tel quel, citez-le et appelez un shell pour cela.

En outre, si vous traitez des noms de files, prenez l'habitude d'utiliser print0 et 0 car les laisser de côté produira des problèmes avec des noms de files contenant des espaces.

Exemple:

 find . -type f -print0 | xargs -0 -n 1 -I FILE bash -c 'echo $(file --mime-type -b FILE)' 

Dans votre cas cela peut être simplifié pour:

 find . -type f -print0 | xargs -0 -n 1 file --mime-type -b 

Un commentaire supplémentaire: appeler un programme pour tous les files trouvés (que ce soit 'xargs -n 1' ou find + exec) peut être un goulot d'étranglement réel. Si vous avez un directory avec des milliers de files, vous générerez des milliers de process.