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*
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.
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 $logfiles
tableau si aucun fichier ne correspond. (Voir Comment "annuler" un 'set -x'? pour des exemples montrant comment le réinitialiser en toute sécurité.)
Beaucoup de réponses ici, mais certaines ne prennent pas en compte
-l
)*.log
au lieu de log*
logs
qui correspond à log*
)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.
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
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
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!
Voici mon seul paquebot pour cela.
file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)
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.
Voici ce que je fais toujours:
ls log * | awk 'END {print NR}'
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
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