web-dev-qa-db-fra.com

Existe-t-il une commande bash qui compte les fichiers?

Existe-t-il une commande bash qui compte le nombre de fichiers correspondant à un modèle?

Par exemple, je veux obtenir le nombre de tous les fichiers d'un répertoire qui correspondent à ce modèle: log*

102
hudi

Ce simple one-liner devrait fonctionner dans n’importe quel shell, pas seulement bash:

ls -1q log* | wc -l

ls -1q vous donnera une ligne par fichier, même s'ils contiennent des espaces ou des caractères spéciaux tels que des nouvelles lignes.

La sortie est acheminée vers wc -l, qui compte le nombre de lignes.

137
Daniel

Vous pouvez le faire en toute sécurité (c’est-à-dire que les fichiers avec des espaces ou \n dans leur nom ne poseront pas de problèmes) avec bash:

$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}

Vous devez activer nullglob afin que vous n'obteniez pas le *.log littéral dans le $logfilestableau si aucun fichier ne correspond. (Voir Comment "annuler" un 'set -x'? pour des exemples montrant comment le réinitialiser en toute sécurité.)

39
Mat

Beaucoup de réponses ici, mais certaines ne prennent pas en compte

  • noms de fichiers contenant des espaces, des lignes nouvelles ou des caractères de contrôle
  • noms de fichiers commençant par des traits d'union (imaginez un fichier appelé -l)
  • fichiers cachés, qui commencent par un point (si le glob était *.log au lieu de log*
  • des répertoires correspondant au glob (par exemple, un répertoire appelé logs qui correspond à log*)
  • répertoires vides (le résultat est 0)
  • des répertoires extrêmement volumineux (les lister tous pourrait épuiser la mémoire)

Voici une solution qui les gère tous:

ls 2>/dev/null -Ubad1 -- log* | wc -l

Explication:

  • -U empêche ls de ne pas trier les entrées, ce qui signifie qu'il n'est pas nécessaire de charger la liste de répertoires complète en mémoire
  • -b imprime des échappements de style C pour des caractères non graphiques, ce qui a pour effet d’imprimer les nouvelles lignes en tant que \n.
  • -a imprime tous les fichiers, même cachés (non strictement nécessaire lorsque glob log* n'implique aucun fichier caché)
  • -d imprime les répertoires sans essayer de lister les contenus du répertoire, ce que ls ferait normalement
  • -1 s'assure qu'il est sur une colonne (ls le fait automatiquement lors de l'écriture dans un tube, donc ce n'est pas strictement nécessaire)
  • 2>/dev/null redirige stderr de sorte que s'il existe 0 fichiers journaux, ignorez le message d'erreur. (Notez que shopt -s nullglob oblige ls à répertorier tout le répertoire de travail.)
  • wc -l utilise la liste de répertoires lors de sa création, de sorte que la sortie de ls ne soit jamais en mémoire, à aucun moment.
  • -- Les noms de fichier sont séparés de la commande à l'aide de -- afin de ne pas être interprétés comme des arguments de ls (dans le cas où log* est supprimé)

Le shell will étendra log* à la liste complète des fichiers, ce qui peut épuiser la mémoire s’il s’agit de beaucoup de fichiers. Il est donc préférable de l’exécuter via grep:

ls -Uba1 | grep ^log | wc -l

Ce dernier gère des répertoires de fichiers extrêmement volumineux sans utiliser beaucoup de mémoire (bien qu’il utilise un sous-shell). Le -d n'est plus nécessaire, car il ne répertorie que le contenu du répertoire actuel.

35
mogsie

Pour une recherche récursive:

find . -type f -name '*.log' | wc -l

wc -w compte le nombre de mots dans la sortie (bash développera *.log comme une liste de fichiers séparés par un espace correspondant à ce modèle), tandis que wc -l comptera le nombre de lignes (find imprime un résultat par ligne).

Pour une recherche non récursive, procédez comme suit:

find . -maxdepth 1 -type f -name '*.log' | wc -l 
35
Will Vousden

La réponse acceptée pour cette question est fausse, mais j'ai peu de représentants, je ne peux donc pas ajouter de commentaire.

La réponse correcte à cette question est donnée par Mat:

shopt -s nullglob
logfiles=(*.log)
echo ${#logfiles[@]}

Le problème avec la réponse acceptée est que wc -l compte le nombre de caractères de nouvelle ligne et les compte même s'ils impriment sur le terminal sous la forme '?'. dans la sortie de 'ls -l'. Cela signifie que la réponse acceptée échoue lorsqu'un nom de fichier contient un caractère de nouvelle ligne. J'ai testé la commande suggérée:

ls -l log* | wc -l

et il renvoie par erreur une valeur de 2 même s'il n'y a qu'un fichier correspondant au modèle dont le nom contient un caractère de nouvelle ligne. Par exemple:

touch log$'\n'def
ls log* -l | wc -l
7
Dan Yard

Si vous avez beaucoup de fichiers et que vous ne souhaitez pas utiliser les élégantes solutions shopt -s nullglob et bash array, vous pouvez utiliser find, etc. tant que vous n'imprimez pas le nom du fichier (qui peut contenir des nouvelles lignes).

find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l

Tous les fichiers correspondant au journal * et commençant par .* seront trouvés. Le "not name. *" Est redondant, mais il est important de noter que la valeur par défaut pour "ls" est de ne pas afficher les fichiers défaut pour find est de les inclure.

Cette réponse est correcte et gère tout type de nom de fichier que vous pouvez lui attribuer, car le nom de fichier n'est jamais transmis entre les commandes.

Mais, la réponse shopt nullglob est la meilleure réponse!

6
mogsie

Voici mon seul paquebot pour cela. 

 file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)
4
zee
ls -1 log* | wc -l

Ce qui signifie que vous devez répertorier un fichier par ligne, puis le rediriger vers la commande de décompte de mots avec la commutation de paramètre sur le décompte de lignes.

0
nudzo

Voici ce que je fais toujours:

ls log * | awk 'END {print NR}'

0
Shuang Liang

Vous pouvez facilement définir une telle commande à l’aide d’une fonction Shell. Cette méthode ne nécessite aucun programme externe et ne génère aucun processus enfant. Il ne tente pas d'analyser ls dangereux et gère parfaitement les caractères "spéciaux" (espaces, nouvelles lignes, barres obliques inversées, etc.). Il ne s'appuie que sur le mécanisme d'expansion du nom de fichier fourni par le shell. Il est compatible avec au moins sh, bash et zsh.

La ligne ci-dessous définit une fonction appelée count qui affiche le nombre d'arguments avec lesquels elle a été appelée.

count() { echo $#; }

Appelez-le simplement avec le motif souhaité:

count log*

Pour que le résultat soit correct lorsque le modèle de masquage ne correspond pas, l'option Shell nullglob (ou failglob - qui est le comportement par défaut sur zsh) doit être définie au moment du développement. Il peut être réglé comme ceci:

shopt -s nullglob    # for sh / bash
setopt nullglob      # for zsh

Selon ce que vous voulez compter, l'option Shell dotglob pourrait également vous intéresser.

Malheureusement, avec bash au moins, il n’est pas facile de définir ces options localement. Si vous ne souhaitez pas les définir globalement, la solution la plus simple consiste à utiliser la fonction de cette manière plus compliquée:

( shopt -s nullglob ; shopt -u failglob ; count log* )

Si vous voulez récupérer la syntaxe légère count log*, ou si vous voulez vraiment éviter de générer un sous-shell, vous pouvez pirater quelque chose dans le sens suivant:

# sh / bash:
# the alias is expanded before the globbing pattern, so we
# can set required options before the globbing gets expanded,
# and restore them afterwards.
count() {
    eval "$_count_saved_shopts"
    unset _count_saved_shopts
    echo $#
}
alias count='
    _count_saved_shopts="$(shopt -p nullglob failglob)"
    shopt -s nullglob
    shopt -u failglob
    count'

En prime, cette fonction est d'une utilisation plus générale. Par exemple:

count a* b*          # count files which match either a* or b*
count $(jobs -ps)    # count stopped jobs (sh / bash)

En transformant la fonction en un fichier de script (ou un programme C équivalent), appelable à partir de PATH, il peut également être composé avec des programmes tels que find et xargs:

find "$FIND_OPTIONS" -exec count {} \+    # count results of a search
0
Maëlan

Vous pouvez utiliser l'option -R pour trouver les fichiers avec ceux qui se trouvent dans les répertoires récursifs.

ls -R | wc -l // to find all the files

ls -R | grep php | wc -l // to find the files which contains the Word php

vous pouvez utiliser des motifs sur le grep

0
Rifaideen