La substitution de command dans la boucle for ne fonctionne pas

Je veux garder tous les files ne se terminant pas par .bat

j'ai essayé

 for f in $(ls | egrep -v .bat); do echo $f; done 

et

 for f in $(eval ls | egrep -v .bat); do echo $f; done 

Mais les deux approches produisent le même résultat, car elles impriment tout. Alors que ls | egrep -v .bat ls | egrep -v .bat et eval ls | egrep -v .bat eval ls | egrep -v .bat travail en soi, s'il est utilisé en dehors de la boucle for .

MODIFIER

Il est intéressant de voir que si je laisse le drapeau -v , la boucle fait ce qu'elle devrait et répertorie tous les files se terminant par .bat .


N'hésitez pas à modifier le titre de la question, car je n'étais pas certain du problème.

J'utilise GNU bash, version 4.1.10(4)-release (i686-pc-cygwin).


EXEMPLE

 $ ls -l | egrep -v ".bat" total 60K -rwx------+ 1 SYSTEM SYSTEM 5.3K Jun 6 20:31 fsc* -rwx------+ 1 SYSTEM SYSTEM 5.3K Jun 6 20:31 scala* -rwx------+ 1 SYSTEM SYSTEM 5.3K Jun 6 20:31 scalac* -rwx------+ 1 SYSTEM SYSTEM 5.3K Jun 6 20:31 scaladoc* -rwx------+ 1 SYSTEM SYSTEM 5.3K Jun 6 20:31 scalap* 

La command fonctionne, mais pas dans la boucle for.

 $ for f in $(ls | egrep -v .bat); do echo $f; done fsc fsc.bat scala scala.bat scalac scalac.bat scaladoc scaladoc.bat scalap scalap.bat scalac scalac.bat scaladoc scaladoc.bat scalap scalap.bat 

DEBUG $ set -x

 mike@pc /cygdrive/c/Program Files (x86)/scala/bin $ for f in $(ls | egrep -v .bat); do echo $f; done ++ ls -hF --color=tty ++ egrep --color=auto -v .bat + for f in '$(ls | egrep -v .bat)' + echo fsc fsc + for f in '$(ls | egrep -v .bat)' + echo fsc.bat fsc.bat // and so on 

Quelques petites choses dans votre code:

Utiliser la substitution de command non-cotée ( $(...) ) sans définir $IFS

Laisser les extensions non cotées est l'opérateur split + glob. La valeur par défaut est la division sur l'espace, l'onglet et la nouvelle ligne. Ici, vous ne souhaitez splitter que sur newline, vous devez donc définir IFS comme cela sinon cela ne fonctionnera pas correctement si les noms de files contiennent des espaces ou des tabulations

Utiliser la substitution de command sans guillemets sans set -f .

Laisser les extensions non cotées est l'opérateur split + glob. Ici, vous ne voulez pas de globulation, c'est-à-dire l'extension de caractères generics tels que scala* dans la list des files correspondants. Lorsque vous ne voulez pas que le shell fasse du "globbing", vous devez le désactiver avec set -f

ls aliasé à ls -F

Le problème ci-dessus est aggravé par le fait que vous avez alias à ls -F . Ce qui ajoute / aux directorys et * aux files exécutables. Donc, en général, parce que scala est exécutable, ls -F produit scala* , et en tant que model de globulation, il est étendu à tous les noms de files commençant par scala ce qui explique pourquoi egrep -v ne filter pas les files.

En supposant que les noms de files ne contiennent pas de caractères de nouvelle ligne

newline est aussi valide qu'un caractère dans un nom de file. Donc, parsingr la sortie de ls ne fonctionne généralement pas . Par exemple, la sortie de ls dans un directory contenant des files a et b est la même que dans un directory contenant un file appelé a\nb .

Au-dessus de egrep filterra les lignes des noms de files, pas les noms de files

Utiliser egrep au lieu de grep -E

egrep est obsolète. grep -E est l'équivalent standard.

Ne pas échapper à la . opérateur regex.

Au-dessus, vous avez utilisé egrep pour activer les expressions régulières étendues , mais vous n'utilisez aucun des opérateurs RE spécifiques. Le seul opérateur de RE que vous utilisez est . pour correspondre à n'importe quel personnage, alors qu'il semble que ce n'est pas ce que vous vouliez. Donc, vous pourriez aussi bien avoir utilisé grep -F ici. Ou utilisez grep -v '\.bat' .

Ne pas ancrer l'expression rationnelle à la fin de la ligne

egrep .bat va correspondre à n'importe quelle ligne qui contient n'importe quel caractère suivi par batbat , de sorte que c'est l'expression rationnelle qui signifie tout ce qui contient batbat pas en première position. Il aurait dû être grep -v '\.bat$' .

Laissant $f non coté

Laisser une expansion non cotée est l'opérateur split + glob. Là, vous ne voulez ni l'un ni l'autre, donc $f devrait être cité ( "$f" ).

Utiliser l' echo

echo développe les séquences d'échappement ANSI C dans ses arguments et / ou traite les strings comme -n ou -e fonction de l'implémentation de l' echo (et / ou de l'environnement).

Utilisez printf place .

Donc une meilleure solution:

 for f in *; do case $f in (*.bat);; (*) printf '%s\n' "$f" esac done 

Bien que s'il n'y a pas de file non caché dans le directory courant, celui-ci affichera toujours * . Vous pouvez contourner cela en zsh en changeant * en *(N) ou en bash en exécutant shopt -s nullglob .

Vous ne devriez pas utiliser ls pour parsingr les files de cette façon.

D'abord, configurez extglob globstar par shopt -s extglob globstar puis

 for f in !(*.bat) do printf '%s\n' "$f" done 

Utilisation de find

 find . -type f ! -name '*.bat' 

Utilisez l'opérateur de négation pour un traitement plus sûr des files.

Il n'y a rien d'insortingnsèquement erroné avec votre boucle for:

 $ for f in $(ls | egrep -v .bat); do echo $f; done 

Voici ma sortie pour la command ci-dessus:

 $ for f in $(ls | egrep -v .bat); do echo $f; done fsc scala scalac scaladoc scalap 

Correction possible

Une suggestion serait de citer l'argument à egrep , comme ainsi:

 $ for f in $(ls | egrep -v '.bat'); do echo $f; done 

Jetez également un coup d'œil au wiki de Bash Pitfalls pour d'autres problèmes potentiels lors de l'écriture de scripts dans Bash comme ceci. En particulier, cela semble être la raison la plus plausible pour laquelle vous rencontrez ce problème. Vérifiez si vos files returnnés contiennent des espaces.

Débogage

Une autre chose à essayer est d'activer le debugging prolixe de votre command bash, avant de l'exécuter afin que vous puissiez voir ce qui se passe dans les coulisses.

Cela permet le debugging:

 $ set -x 

Ensuite, exécutez votre command:

 $ for f in $(ls | egrep -v .bat); do echo $f; done ++ ls --color=auto ++ egrep --color=auto -v .bat + for f in '$(ls | egrep -v .bat)' + echo fsc fsc + for f in '$(ls | egrep -v .bat)' + echo scala scala + for f in '$(ls | egrep -v .bat)' + echo scalac scalac + for f in '$(ls | egrep -v .bat)' + echo scaladoc scaladoc + for f in '$(ls | egrep -v .bat)' + echo scalap scalap 

Puis désactivez-le:

 $ set +x 

Problème avec Cygwin?

Étant donné que les exemples fonctionnent bien sur un certain nombre de systèmes Linux natifs, le problème est probablement egrep à Cygwin et / ou à certaines versions de bash et egrep . Je ferais particulièrement attention au séparateur de champs dans Bash, $IFS pour voir s'il y a un problème entre les différents séparateurs de lignes sous Windows ( 0x0d,0x0a ) et Unix ( 0x0a ).

Je n'ai pas d'installation de Cygwin donc je n'ai pas de méthode pour prouver cette hypothèse.