J'ai une exigence, si j'exécute un script ./123
avec des arguments de chemin vide, disons /usr/share/linux-headers-3.16.0-34-generic/.tmp_versions
(ce répertoire est vide). Il doit afficher "le répertoire est vide"
Mon code est:
#!/bin/bash
dir="$1"
if [ $# -ne 1 ]
then
echo "please pass arguments"
exit
fi
if [ -e $dir ]
then
printf "minimum file size: %s\n\t%s\n" \
$(du $dir -hab | sort -n -r | tail -1)
printf "maximum file size: %s\n\t%s\n" \
$(du $dir -ab | sort -n | tail -1)
printf "average file size: %s"
du $dir -sk | awk '{s+=$1}END{print s/NR}'
else
echo " directory doesn't exists"
fi
if [ -d "ls -A $dir" ]
then
echo " directory is empty"
fi
J'ai une erreur qui s'affiche comme si j'exécute le nom du script ./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions
(ce répertoire est vide).
minimum file size: 4096
/usr/src/linux-headers-3.16.0-34-generic/.tmp_versions
maximum file size: 4096
/usr/src/linux-headers-3.16.0-34-generic/.tmp_versions
average file size: 4
au lieu d'afficher uniquement la sortie "le répertoire est vide", il affiche la sortie ci-dessus
La sortie ci-dessous doit être affichée si j'exécute le script avec des arguments corrects (je veux dire avec un chemin de répertoire correct). dire./123 /usr/share
minimum file size: 196
/usr/share
maximum file size: 14096
/usr/share
average file size: 4000
ma sortie attendue est: ./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions
directory is empty.
shell-script
Bouddha Sreekanth
la source
la source
Réponses:
Ce qui précède est un test compatible POSIX - et devrait être très rapide.
ls
listera tous les fichiers / répertoires dans un répertoire à l' exception.
et..
chacun par ligne et sera-q
Uote tous les caractères non-imprimables (pour inclure\n
ewlines) dans la sortie avec un?
point d'interrogation. De cette façon, sigrep
reçoit même un seul caractère en entrée, il retournera vrai - sinon faux.Pour le faire dans un shell POSIX seul:
Un POSIX-coquille (qui n'a pas désactivé auparavant
-f
génération de ilename) seraset
la"$@"
matrice-paramètre de positionnement soit aux chaînes littérales suivies par laset
commande ci - dessus, ou bien pour les champs générés par les opérateurs de glob à la fin de chaque. Que ce soit le cas, cela dépend si les globes correspondent réellement à quelque chose. Dans certains shells, vous pouvez demander à un glob non résolu de s'étendre à null - ou rien du tout. Cela peut parfois être bénéfique, mais il n'est pas portable et s'accompagne souvent de problèmes supplémentaires - tels que la nécessité de définir des options de shell spéciales et de les désactiver ensuite.Le seul moyen portable de gérer des arguments à valeur nulle implique des variables vides ou non définies ou
~
des extensions tilde. Et ce dernier, soit dit en passant, est beaucoup plus sûr que le premier.Au-dessus du shell, les tests de
-e
xistence des fichiers ne sont testés que si aucun des trois globs spécifiés ne se résout en plusieurs correspondances. Ainsi, lafor
boucle n'est exécutée que pour trois itérations ou moins, et uniquement dans le cas d'un répertoire vide, ou dans le cas où un ou plusieurs des modèles ne se résolvent qu'en un seul fichier. Lefor
égalementbreak
s si l' une des petites boules représentent un fichier réel - et comme je l' ai arrangé les petites boules dans l'ordre le plus probable au moins probable, il devrait quitter à peu près à la première itération à chaque fois.Dans tous les cas, cela ne devrait impliquer qu'un seul
stat()
appel système - le shell etls
ne devrait à la fois avoir besoin questat()
du répertoire interrogé et répertorier les fichiers que son rapport de dentisterie contient. Ceci est contrasté par le comportement defind
ce qui serait à la place destat()
chaque fichier que vous pourriez lister avec.la source
ls
cas. globes et[
sont silencieux quand ils n'y ont pas accès. Par exemple,[ -e /var/spool/cron/crontabs/stephane ]
signalera silencieusement false, alors qu'il existe un fichier portant ce nom.Avec GNU ou BSD modernes
find
, vous pouvez faire:( ce qui suppose
$dir
ne ressemble pas à unfind
prédicat (!
,(
,-name...
).POSIX:
la source
[-z $dir ]
se plaint qu'aucune commande n'est appelée[-z
sur la plupart des systèmes. Vous avez besoin d'espaces autour des supports .[ -z $dir ]
se trouve être vrai sidir
est vide, et est faux pour la plupart des autres valeurs dedir
, mais il n'est pas fiable, par exemple, il est vrai si la valeur dedir
est= -z
ou-o -o -n -n
. Utilisez toujours des guillemets doubles autour des substitutions de commandes (cela vaut également pour le reste de votre script).[ -z "$dir" ]
teste si la valeur de la variabledir
est vide. La valeur de la variable est une chaîne, qui se trouve être le chemin d'accès au répertoire. Cela ne vous dit rien sur le répertoire lui-même.Il n'y a pas d'opérateur pour tester si un répertoire est vide, comme c'est le cas pour un fichier normal (
[ -s "$dir" ]
c'est vrai pour un répertoire même s'il est vide). Un moyen simple de tester si un répertoire est vide consiste à répertorier son contenu; si vous obtenez du texte vide, le répertoire est vide.Sur les anciens systèmes qui n'en ont pas
ls -A
, vous pouvez utiliserls -a
, mais alors.
et..
sont répertoriés.(Ne mettez pas en retrait la ligne commençant par,
..
car la chaîne doit contenir uniquement une nouvelle ligne, pas une nouvelle ligne et un espace supplémentaire.)la source
$(…)
est une substitution de commandeVous regardez les attributs du répertoire lui-même.
Vous voulez compter le nombre de fichiers qui s'y trouvent:
Ainsi, le test pour "le répertoire est vide" est:
Comme ça:
la source
Ma réponse tldr est:
Il est compatible POSIX et, peu importe, il est généralement plus rapide que la solution qui répertorie le répertoire et dirige la sortie vers grep.
Usage:
J'aime la réponse /unix//a/202276/160204 , que je réécris comme:
Il répertorie le répertoire et dirige le résultat vers grep. Au lieu de cela, je propose une fonction simple qui est basée sur l'expansion et la comparaison globales.
Cette fonction n'est pas POSIX standard et appelle un sous-shell avec
$()
. J'explique cette fonction simple d'abord afin que nous puissions mieux comprendre la solution finale (voir la réponse tldr ci-dessus) plus tard.Explication:
Le côté gauche (LHS) est vide lorsqu'aucune expansion ne se produit, ce qui est le cas lorsque le répertoire est vide. L'option nullglob est requise car sinon, en l'absence de correspondance, le glob lui-même est le résultat de l'expansion. (Le fait que le RHS corresponde aux globes du LHS lorsque le répertoire est vide ne fonctionne pas en raison des faux positifs qui se produisent lorsqu'un glob LHS correspond à un seul fichier nommé glob:
*
le glob correspond à la sous-chaîne*
du nom de fichier. ) L'expression d'accolade{,.[^.],..?}
couvre les fichiers cachés, mais pas..
ou.
.Parce qu'il
shopt -s nullglob
est exécuté à l'intérieur$()
(un sous-shell), il ne change pas l'nullglob
option du shell actuel, ce qui est normalement une bonne chose. D'un autre côté, c'est une bonne idée de définir cette option dans les scripts, car il est sujet à erreur d'avoir un glob qui renvoie quelque chose lorsqu'il n'y a pas de correspondance. Ainsi, on pourrait définir l'option nullglob au début du script et elle ne sera pas nécessaire dans la fonction. Gardons cela à l'esprit: nous voulons une solution qui fonctionne avec l'option nullglob.Mises en garde:
Si nous n'avons pas accès en lecture au répertoire, la fonction signale la même chose que s'il y avait un répertoire vide. Cela s'applique également à une fonction qui répertorie le répertoire et grep la sortie.
La
shopt -s nullglob
commande n'est pas POSIX standard.Il utilise le sous-shell créé par
$()
. Ce n'est pas un gros problème, mais c'est bien si nous pouvons l'éviter.Pro:
Ce n'est pas vraiment important, mais cette fonction est quatre fois plus rapide que la précédente, mesurée avec la quantité de temps CPU passé dans le noyau au cours du processus.
Autres solutions:
Nous pouvons supprimer la
shopt -s nullglob
commande non POSIX sur le LHS et mettre la chaîne"$1/* $1/.[^.]* $1/..?*"
dans le RHS et éliminer séparément les faux positifs qui se produisent lorsque nous n'avons que des fichiers nommés'*'
,.[^.]*
ou..?*
dans le répertoire:Sans la
shopt -s nullglob
commande, il est désormais logique de supprimer le sous-shell, mais nous devons être prudents car nous voulons éviter le fractionnement de mots tout en permettant l'expansion globale sur le LHS. En particulier, la citation pour éviter le fractionnement de mots ne fonctionne pas, car elle empêche également l'expansion des globes. Notre solution consiste à considérer les globes séparément:Nous avons encore le fractionnement de mots pour le glob individuel, mais ça va maintenant, car cela ne produira une erreur que lorsque le répertoire n'est pas vide. Nous avons ajouté 2> / dev / null, pour supprimer le message d'erreur lorsqu'il y a beaucoup de fichiers correspondant au glob donné sur le LHS.
Nous rappelons que nous voulons une solution qui fonctionne également avec l'option nullglob. La solution ci-dessus échoue avec l'option nullglob, car lorsque le répertoire est vide, le LHS est également vide. Heureusement, il ne dit jamais que le répertoire est vide alors qu'il ne l'est pas. Il ne fait que dire qu'il est vide quand il l'est. Nous pouvons donc gérer séparément l'option nullglob. Nous ne pouvons pas simplement ajouter les cas
[ "$1/"* = "" ]
etc., car ceux-ci se développeront en tant que[ = "" ]
, etc. qui sont syntaxiquement incorrects. Nous utilisons donc[ "$1/"* "" = "" ]
etc. à la place. Nous devons à nouveau considérer les trois cas*
,..?*
et.[^.]*
faire correspondre les fichiers cachés, mais pas.
et..
. Ceux-ci n'interféreront pas si nous n'avons pas l'option nullglob, car ils ne disent jamais non plus qu'il est vide alors qu'il ne l'est pas. Ainsi, la solution finale proposée est:Problème de sécurité:
Créez deux fichiers
rm
etx
dans un répertoire vide et exécutez*
à l'invite. Le glob*
se développerarm x
et sera exécuté pour être suppriméx
. Ce n'est pas un problème de sécurité, car dans notre fonction, les globes sont situés là où les extensions ne sont pas vues comme des commandes, mais comme des arguments, tout comme dansfor f in *
.la source
$(set -f; echo "$1"/*)
semble une manière d'écrire assez compliquée"$1/*"
. Et cela ne correspondra pas aux répertoires ou fichiers cachés."$1/*"
(notez les guillemets inclus*
), donc leset -f
sous-shell et ne sont pas nécessaires pour cela en particulier../* ./.[!.]* ./..?*
). Vous pouvez peut-être l'incorporer pour compléter la fonction.Voici une autre façon simple de le faire. Supposons que D est le nom de chemin complet du répertoire que vous souhaitez tester pour la vacuité.
ensuite
Cela fonctionne parce que
a comme sortie
awk supprime le D et si la taille est 0, le répertoire est vide.
la source
la source
echo
, tout ce dont vous avez besoin estlist="$somedir/*"
. En outre, cela est délicat et sujet aux erreurs. Vous n'avez pas mentionné que l'OP devrait donner le chemin du répertoire cible, y compris la barre oblique de fin, par exemple.