Erreur de script Bash avec des chaînes avec des chemins qui ont des espaces et des caractères génériques

25

J'ai du mal à maîtriser les bases des scripts Bash. Voici ce que j'ai jusqu'à présent:

#!/bin/bash
FILES="/home/john/my directory/*.txt"

for f in "${FILES}"
do
  echo "${f}"
done

Tout ce que je veux faire, c'est lister tous les .txtfichiers en forboucle pour pouvoir faire des trucs avec eux. Mais l'espace dans le my directoryet l'astérisque *.txtne jouent tout simplement pas bien. J'ai essayé de l'utiliser avec et sans guillemets, avec et sans accolades sur les noms de variables et je ne peux toujours pas imprimer tous les .txtfichiers.

C'est une chose très basique, mais je continue de me battre parce que je suis fatigué et que je ne peux pas penser correctement.

Qu'est-ce que je fais mal?

J'ai réussi à appliquer le script ci-dessus si mes FICHIERS n'ont pas d'espace ou d'astérisque ... J'ai dû expérimenter avec ou sans l'utilisation de guillemets doubles et d'accolades pour le faire fonctionner. Mais au moment où j'ai à la fois des espaces et un astérisque, ça gâche tout.

John
la source

Réponses:

33

Dans les guillemets, le *ne se développera pas dans une liste de fichiers. Pour utiliser un tel caractère générique avec succès, il doit être en dehors des guillemets.

Même si le caractère générique se développait, l'expression "${FILES}"entraînerait une seule chaîne, pas une liste de fichiers.

Une approche qui fonctionnerait serait:

#!/bin/bash
DIR="/home/john/my directory/"
for f in "$DIR"/*.txt
do
  echo "${f}"
done

Dans ce qui précède, les noms de fichiers avec des espaces ou d'autres caractères difficiles seront traités correctement.

Une approche plus avancée pourrait utiliser des tableaux bash:

#!/bin/bash
FILES=("/home/john/my directory/"*.txt)
for f in "${FILES[@]}"
do
  echo "${f}"
done

Dans ce cas, FILESest un tableau de noms de fichiers. Les parens entourant la définition en font un tableau. Notez que le *est en dehors des guillemets. La construction "${FILES[@]}"est un cas spécial: elle s'étendra à une liste de chaînes où chaque chaîne est l'un des noms de fichier. Les noms de fichiers avec des espaces ou d'autres caractères difficiles seront traités correctement.

John1024
la source
1
grand qui a fonctionné
John
Il convient de noter que si vous passez des chemins comme celui-ci à travers des fonctions, vous devez vous assurer de citer la variable seule plutôt que de la concaténer dans le cadre d'une chaîne plus grande: for f in "$DIR"/*.txt= fine for f in "$DIR/*.txt"= breaks
pospi
7

Bien que l'utilisation de tableaux comme indiqué par John1024 soit beaucoup plus logique, ici, vous pouvez également utiliser l'opérateur split + glob (en laissant une variable scalaire sans guillemets).

Puisque vous ne voulez que la partie glob de cet opérateur, vous devez désactiver la partie scindée :

#! /bin/sh -
# that also works in any sh, so you don't even need to have or use bash

file_pattern="/home/john/my directory/*.txt"
# all uppercase variables should be reserved for environment variables

IFS='' # disable splitting

for f in $file_pattern # here we're not quoting the variable so
                       # we're invoking the split+glob operator.
do
  printf '%s\n' "$f" # avoid the non-reliable, non-portable "echo"
done
Stéphane Chazelas
la source
0

Ce que vous pouvez faire est de ne laisser que les caractères génériques en dehors des guillemets.
Quelque chose comme:
un dans « fichiers avec des espaces » * «txt »
ne le
traitement de
fait
Si les wildcards eux - mêmes se dilatent à des espaces, alors vous aurez besoin d' utiliser un fichier t approche par ligne, comme l' utilisation ls -l pour générer la liste des fichiers et utilisez bash read pour obtenir chaque fichier.

Marcelo Pacheco
la source
-1

Lorsque vous souhaitez traiter sur un ensemble de fichiers, vous considérez que leur nom peut contenir de l'espace ou un autre code scape, donc avant de démarrer votre processus tel que for loopou find commanddéfinissez le IFS bash env variablesur:

IFS=$(echo -en "\n\b")
Golfe Persique
la source