Mon problème (dans un script avec #!/bin/sh
) est le suivant: j'essaie de faire la somme de contrôle de tous les fichiers d'un répertoire à des fins d'archivage. Le fichier de somme de contrôle (dans mon cas sha1) avec tous les noms de fichiers doit résider dans le même répertoire. Disons que nous avons un répertoire ~/test
avec des fichiers f1
et f2
:.
mkdir ~/test
cd ~/test
echo "hello" > f1
echo "world" > f2
Calculons maintenant les sommes de contrôle avec
find -maxdepth 1 -type f -printf '%P\n' | xargs shasum
fait exactement ce que je veux, il répertorie tous les fichiers du répertoire actuel uniquement et calcule les sommes sha1 (maxdepth peut être modifié ultérieurement). La sortie sur STDOUT est:
f572d396fae9206628714fb2ce00f72e94f2258f f1
9591818c07e900db7e1e0bc4b884c945e6a61b24 f2
Malheureusement, lorsque vous essayez de l'enregistrer dans un fichier avec
find -maxdepth 1 -type f -printf '%P\n' | xargs shasum > sums.sha1
le fichier résultant affiche la somme de contrôle pour lui-même:
da39a3ee5e6b4b0d3255bfef95601890afd80709 sums.sha1
f572d396fae9206628714fb2ce00f72e94f2258f f1
9591818c07e900db7e1e0bc4b884c945e6a61b24 f2
et échoue donc à une date ultérieure shasum --check
, en raison du problème évident de modification de fichier supplémentaire lors de l'enregistrement de la dernière somme.
J'ai regardé autour de moi et en utilisant -p
flag for xargs
, j'ai découvert qu'il créait en quelque sorte le fichier de sortie avant même d'exécuter la commande find, donc le fichier supplémentaire est trouvé et sera additionné ...
Je sais que pour contourner ce problème, je pourrais enregistrer la somme de contrôle dans un autre emplacement (répertoire temporaire via mktemp
) ou l'exclure dans find spécifiquement, mais j'aimerais comprendre pourquoi il se comporte comme il le fait - ce qui, à mes yeux, n'est pas très utile, par exemple, si la première commande vérifiait si le fichier de sortie est déjà sur le disque, elle n'obtiendrait jamais la bonne réponse ...
xargs
, c'est le shell lui-même qui crée ce fichier, car avant d'exécuter une commande, le shell redirige d'abord toutes les entrées, sorties et canaux, de sorte qu'aufind
démarrage, le fichier de sortie existe déjà. Utilisez à la-exec
place:find -maxdepth 1 -type f -exec sh -c 'shasum "$@" > sums.sha1' {} +
sh
invocations sont nécessaires. Notez que vous avez besoin d'un argument pour$0
avant{}
.tee
a disparu? Je l'ai essayé et cela fonctionne bien, j'ai également supprimé STDOUT avec l'ajout de1>/dev/null
. Y a-t-il eu un problème avec la réponse ou était-ce un bug?Réponses:
Vous pouvez empêcher le fichier d'atteindre en
xargs
utilisant:Pour éviter les problèmes avec les noms de fichiers comportant des blancs, des retours à la ligne ou des guillemets ou des barres obliques inverses, j'utiliserais cependant:
au lieu.
Il
--
s'agit d'éviter les problèmes avec les noms de fichiers commençant par-
. Cela n'aidera cependant pas pour un fichier appelé-
. Si vous aviez utilisé à la-print0
place de-printf '%P\0'
, vous n'auriez pas eu besoin de--
et n'auriez pas eu de problème avec le-
fichier.la source
basename
j'obtenais le nom de fichier sums.sha1 à partir du chemin complet donné (cela n'était pas inclus dans la question, mais cela pourrait aider les autres).Puisque vous utilisez
-maxdepth 1
, je suppose que vous ne voulez pas de récursivité. Si c'est le cas, faites-le simplement dans le shell à la place:Pour ignorer les répertoires, vous pouvez faire:
Si vous avez besoin d'une récursivité et utilisez
bash
, faites:Notez que toutes ces approches ont l'avantage de travailler sur des noms de fichiers arbitraires, y compris ceux avec des espaces, des sauts de ligne ou quoi que ce soit d'autre.
la source
sums.sha1
est déjà là (lors d'une précédente exécution), votre solution l'intégrera.sh
, mais votre réponse pourrait aider les autres.avec
zsh
:Le glob sera étendu avant que la redirection ne soit effectuée, donc le
sums.sha1
ne sera pas inclus s'il n'était pas là en premier lieu.D
est d'inclure des fichiers dot (fichiers cachés) comme lefind
ferait..
est de sélectionner uniquement les fichiers normaux (comme le vôtre-type f
).Pour exclure de
sums.sha1
toute façon au cas où il était là en premier lieu:Notez que ceux-ci exécutent une commande shasum, vous pouvez donc finir par voir une erreur "Arg list too long" si la liste est énorme. Pour contourner cela:
Je recommanderais d'utiliser
./*
plutôt que d'*
éviter des problèmes potentiels avec un fichier appelé-
.la source
Comme les autres réponses l'ont déjà indiqué, le problème est que le shell s'ouvre et crée le
sums.sha1
fichier, avant d'exécuter votre pipeline. Vous pouvez utiliser le programmesponge
qui fait partie dumoreutils
package de nombreuses distributions. Contrairement au shell, la redirectionsponge
attendra d'avoir tout reçu avant d'ouvrir le fichier. Il est généralement utilisé lorsque vous souhaitez écrire un fichier que vous lisez dans le même pipeline.Dans votre cas, il est utilisé comme ceci:
la source
Comme alternative à find / xargs, etc., vous voudrez peut-être sha1deep. C'est probablement dans un paquet différent - sur ma boîte, il vient dans le paquet md5deep.
Comme d'autres l'ont dit, sums.sha1 est créé par le shell avant même que la recherche ne commence. Une astuce avec
! -name sums.sha1
tofind
fonctionnera, tout commela source