Je veux combiner plusieurs conditions dans une instruction shell if et annuler la combinaison. J'ai le code de travail suivant pour une combinaison simple de conditions:
if [ -f file1 ] && [ -f file2 ] && [ -f file3 ] ; then
# do stuff with the files
fi
Cela fonctionne bien. Si je veux l'annuler, je peux utiliser le code de travail suivant:
if ! ( [ -f file1 ] && [ -f file2 ] && [ -f file3 ] ) ; then
echo "Error: You done goofed."
exit 1
fi
# do stuff with the files
Cela fonctionne également comme prévu. Cependant, il me vient à l'esprit que je ne sais pas ce que les parenthèses font réellement là-bas. Je veux les utiliser uniquement pour le regroupement, mais est-ce réellement un sous-shell? (Comment savoir?) Dans l'affirmative, existe-t-il un moyen de regrouper les conditions sans engendrer un sous-shell?
bash
shell-script
Caractère générique
la source
la source
if ! [ -f file1 ] || ! [ -f file 2 ] || ! [ -f file3 ] ; then
mais je voudrais une réponse plus générale.if [[ ! -f file1 ]] && [[ ! -f file2 ]]; then
if [ ! 1 -eq 2 ] && [ ! 2 -eq 3 ]; then echo yep; fi
et ça marche. J'écris juste toujours des tests à double accolade par habitude. De plus, pour m'assurer que ce n'est pas le casbash
, j'ai encore testéif /bin/test ! 1 -eq 2 && /bin/test ! 2 -eq 3 ; then echo yep; fi
et cela fonctionne aussi de cette façon.[ ! -e file/. ] && [ -r file ]
supprimera les répertoires. niez-le comme vous le souhaitez. bien sûr, c'est ce qui-d
fait.Réponses:
Vous devez utiliser
{ list;}
au lieu de(list)
:Les deux sont des commandes de regroupement , mais
{ list;}
exécutent des commandes dans l'environnement shell actuel.Notez que, l'
;
en{ list;}
est nécessaire pour délimiter la liste de}
mots inverse, vous pouvez utiliser d' autres delimiter ainsi. L'espace (ou autre délimiteur) après{
est également requis.la source
awk
plus souvent qu'enbash
) est le besoin d'espaces après l'accolade ouverte. Vous mentionnez "vous pouvez également utiliser un autre délimiteur"; des exemples?zsh
, vous pouvez utiliser des espaces blancs&
c'est aussi un séparateur, vous pouvez l'utiliser{ echo 1;:&}
, plusieurs sauts de ligne peuvent également être utilisés.they must be separated from list by whitespace or another shell metacharacter.
En bash ils sont:metacharacter: | & ; ( ) < > space tab
. Pour cette tâche spécifique, je crois que tout& ; ) space tab
fonctionnera. .... .... De zsh (comme commentaire)each sublist is terminated by
&',
&!', or a newline.
Vous pouvez utiliser entièrement la
test
fonctionnalité pour réaliser ce que vous voulez. À partir de la page de manuel detest
:Votre état pourrait donc ressembler à:
Pour annuler l'utilisation de parenthèses d'échappement:
la source
Pour annuler de manière portative un conditionnel complexe en shell, vous devez soit appliquer la loi de De Morgan et pousser la négation jusqu'en bas dans les
[
appels ...... ou vous devez utiliser
then :; else
...if ! command
n'est pas disponible sur le marché et ni l'un ni l'autre[[
.Si vous n'avez pas besoin d'une portabilité totale, n'écrivez pas de script shell . Vous êtes en fait plus susceptible de trouver
/usr/bin/perl
sur un Unix sélectionné au hasard que vousbash
.la source
!
est POSIX qui est de nos jours suffisamment portable. Même les systèmes livrés avec le shell Bourne ont également un sh POSIX ailleurs sur le système de fichiers que vous pouvez utiliser pour interpréter votre syntaxe standard./bin/sh
. Les scripts Autoconf font comme vous le suggérez, mais je pense que nous savons tous à quel point cette approbation est faible.d'autres ont noté le regroupement des
{
commandes composées;}
, mais si vous effectuez des tests identiques sur un ensemble, vous pouvez utiliser un type différent:... comme cela a été démontré ailleurs
{ :;}
, l'imbrication des commandes composées ne pose aucun problème ...Notez que les tests ci-dessus (généralement) pour les fichiers réguliers . Si vous cherchez seulement existants , lisibles les fichiers qui ne sont pas des répertoires:
Si vous ne vous souciez pas qu'il s'agisse de répertoires ou non:
... fonctionne pour tout fichier lisible et accessible , mais se bloquera probablement pour fifos sans écrivains.
la source
Ce n'est pas une réponse complète à votre question principale, mais j'ai remarqué que vous mentionnez les tests composés (fichier lisible) dans un commentaire; par exemple,
Vous pouvez consolider cela un peu en définissant une fonction shell; par exemple,
Ajoutez la gestion des erreurs à (par exemple,
[ $# = 1 ]
) au goût. La premièreif
déclaration ci-dessus peut maintenant être condensée enet vous pouvez encore raccourcir cela en raccourcissant le nom de la fonction. De même, vous pouvez définir
not_readable_file()
(ounrf
pour faire court) et inclure la négation dans la fonction.la source
readable_files() { [ $# -eq 0 ] && return 1 ; for file in "$@" ; do [ -f "$file" ] && [ -r "$file" ] || return 1 ; done ; }
(Avertissement: j'ai testé ce code et il fonctionne mais ce n'était pas un trait. J'ai ajouté des points-virgules pour les poster ici mais je n'ai pas testé leur placement.)if readable_files file1 file2 file3
ne sait pas si la fonction faisait AND ou OR - bien que (a) je suppose que c'est assez intuitif, (b) si vous ne distribuez pas le script, c'est assez bien si vous le comprenez, et (c) puisque j'ai suggéré d'abréger le nom de la fonction en obscurité, je ne suis pas en mesure de parler de lisibilité. … (Suite)for file in "$@" ; do
parfor file do
- elle est par défautin "$@"
, et (un peu contre-intuitivement);
non seulement inutile mais découragée.Vous pouvez également annuler les tests d'accolade, donc pour réutiliser votre code d'origine:
la source
||
au lieu de&&
||
exécuterait le prédicat si aucun des fichiers n'est présent, pas si et seulement si tous les fichiers ne sont pas présents. NON (A ET B ET C) est identique à (NON A) ET (NON B) ET (NON C); voir la question d'origine.||
de&&
. Voir mon premier commentaire ci-dessous ma question elle-même.not (a and b)
est le même que(not a) or (not b)
. Voir en.wikipedia.org/wiki/De_Morgan%27s_lawsOui, les commandes dans le
(...)
sont exécutées dans un sous-shell.Pour tester si vous êtes dans un sous-shell, vous pouvez comparer le PID de votre shell actuel avec le PID du shell interactif.
Exemple de sortie, démontrant que les parenthèses génèrent un sous-shell et les accolades ne font pas:
la source
()
ne spawn subshell..peut-être que vous vouliez dire{}
....echo $$ && ( echo $$ )
pour comparer les PID et ils étaient les mêmes, mais juste avant de soumettre le commentaire vous disant que vous aviez tort, j'ai essayé une autre chose.echo $BASHPID && ( echo $BASHPID )
donne différents PIDecho $BASHPID && { echo $BASHPID; }
donne le même PID que vous avez suggéré serait le cas. Merci pour l'éducation$BASHPID
, c'est l'autre chose qui me manquait.