web-dev-qa-db-fra.com

Comment puis-je utiliser les commandes if test et find de bash ensemble?

J'ai un répertoire avec des journaux de plantage et j'aimerais utiliser une instruction conditionnelle dans un script bash basé sur une commande find.

Les fichiers journaux sont stockés dans ce format:

/var/log/crashes/app-2012-08-28.log
/var/log/crashes/otherapp-2012-08-28.log

Je souhaite que l'instruction if ne renvoie true que s'il existe un journal de plantage pour une application spécifique qui a été modifiée au cours des 5 dernières minutes. La commande find que j'utiliserais serait:

find /var/log/crashes -name app-\*\.log -mmin -5

Je ne sais pas comment l'incorporer correctement dans une instruction if. Je pense que cela pourrait fonctionner:

if [ test `find /var/log/crashes -name app-\*\.log -mmin -5` ] then
 service myapp restart
fi

Il y a quelques domaines où je ne suis pas clair:

  • J'ai regardé le si drapeaux mais je ne sais pas lequel, le cas échéant, je devrais utiliser.
  • Ai-je besoin de la directive test ou dois-je simplement traiter directement les résultats de la commande find, ou peut-être utiliser find... | wc -l pour obtenir un nombre de lignes à la place?
  • Pas 100% nécessaire pour répondre à cette question, mais test est pour tester par rapport aux codes de retour que les commandes retournent? Et ils sont en quelque sorte invisibles - en dehors de stdout/stderr? J'ai lu la page man mais je ne sais toujours pas quand utiliser test et comment le déboguer.
26
cwd

[ Et test sont des synonymes (sauf que [ Nécessite ]), Donc vous ne voulez pas utiliser [ test:

[ -x /bin/cat ] && echo 'cat is executable'
test -x /bin/cat && echo 'cat is executable'

test renvoie un état de sortie nul si la condition est vraie, sinon différente de zéro. Cela peut en fait être remplacé par n'importe quel programme pour vérifier son état de sortie, où 0 indique le succès et non nul indique l'échec:

# echoes "command succeeded" because echo rarely fails
if /bin/echo hi; then echo 'command succeeded'; else echo 'command failed'; fi

# echoes "command failed" because rmdir requires an argument
if /bin/rmdir; then echo 'command succeeded'; else echo 'command failed'; fi

Cependant, tous les exemples ci-dessus ne testent que l'état de sortie du programme et ignorent la sortie du programme.

Pour find, vous devrez tester si une sortie a été générée. -n Teste une chaîne non vide:

if [[ -n $(find /var/log/crashes -name "app-*.log" -mmin -5) ]]
then
    service myapp restart
fi

Une liste complète des arguments de test est disponible en appelant help test À la ligne de commande bash.

Si vous utilisez bash (et non sh), vous pouvez utiliser [[ condition ]], Qui se comporte de manière plus prévisible lorsqu'il y a des espaces ou d'autres cas spéciaux dans votre condition. Sinon, cela revient généralement à utiliser [ condition ]. J'ai utilisé [[ condition ]] Dans cet exemple, comme je le fais chaque fois que possible.

J'ai également changé `command` En $(command), qui se comporte généralement de la même manière, mais est plus agréable avec les commandes imbriquées.

28
mrb

find se fermera correctement s'il n'y a pas eu d'erreurs, vous ne pouvez donc pas compter sur son état de sortie pour savoir s'il a trouvé un fichier. Mais, comme vous l'avez dit, vous pouvez compter le nombre de fichiers trouvés et tester ce nombre.

Ce serait quelque chose comme ceci:

if [ $(find /var/log/crashes -name 'app-*.log' -mmin -5 | wc -l) -gt 0 ]; then
    ...
fi

test (alias [) ne vérifie pas les codes d'erreur des commandes, il a une syntaxe spéciale pour effectuer des tests, puis se termine avec un code d'erreur de 0 si le test a réussi, ou 1 sinon. C'est if celui qui vérifie le code d'erreur de la commande que vous lui passez et exécute son corps en fonction de celui-ci.

Voir man test (ou help test, si vous utilisez bash) et help if (idem).

Dans ce cas, wc -l affichera un nombre. Nous utilisons l'option de test-gt pour tester si ce nombre est supérieur à 0. Si c'est le cas, test (ou [) retournera avec le code de sortie 0. if interprétera ce code de sortie comme un succès et exécutera le code dans son corps.

9
angus

Ce serait

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)" ]; then

ou

if test -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)"; then

Les commandes test et [ … ] sont exactement synonymes. La seule différence est leur nom et le fait que [ nécessite une fermeture ] comme dernier argument. Comme toujours, utilisez des guillemets doubles autour de la substitution de commande, sinon la sortie de la commande find sera divisée en mots, et ici vous obtiendrez une erreur de syntaxe s'il y a plus d'un fichier correspondant (et quand il y a n'y a pas d'arguments, [ -n ] est vrai, alors que vous voulez [ -n "" ] ce qui est faux).

Dans ksh, bash et zsh mais pas dans ash, vous pouvez également utiliser [[ … ]] qui a des règles d'analyse différentes: [ est une commande ordinaire, tandis que [[ … ]] est une construction d'analyse différente. Vous n'avez pas besoin de guillemets doubles à l'intérieur de [[ … ]] (bien qu'ils ne fassent pas de mal). Vous avez toujours besoin du ; après la commande.

if [[ -n $(find /var/log/crashes -name app-\*\.log -mmin -5) ]]; then

Cela peut être potentiellement inefficace: s'il y a beaucoup de fichiers dans /var/log/crashes, find les explorera tous. Vous devez arrêter la recherche dès qu'elle trouve une correspondance ou peu de temps après. Avec GNU find (Linux non intégré, Cygwin), utilisez le -quit primaire.

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print -quit)" ]; then

Avec d'autres systèmes, canalisez find dans head pour au moins quitter peu après la première correspondance (find mourra d'un tube cassé).

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print | head -n 1)" ]; then

(Vous pouvez utiliser head -c 1 si votre commande head le prend en charge.)


Vous pouvez également utiliser zsh.

crash_files=(/var/log/crashes/**/app-*.log(mm-5[1]))
if (($#crash_files)); then

Cela devrait fonctionner

if find /var/log/crashes -name 'app-\*\.log' -mmin -5 | read
then
  service myapp restart
fi
1
Steven Penny
find /var/log/crashes -name app-\*\.log -mmin -5 -exec service myapp restart ';' -quit

est une solution appropriée ici.

-exec service myapp restart ';' fait que find appelle la commande que vous souhaitez exécuter directement, plutôt que d'avoir besoin du Shell pour interpréter quoi que ce soit.

-quit provoque la fermeture de find après le traitement de la commande, empêchant ainsi la commande d'être exécutée à nouveau s'il se trouve que plusieurs fichiers correspondent aux critères.

0
Jules