web-dev-qa-db-fra.com

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

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 énumérer tous les .txt fichiers dans une boucle for pour que je puisse faire des trucs avec eux. Mais l'espace dans le my directory et l'astérisque dans *.txt ne 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 .txt des dossiers.

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.

25
John

Entre 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 s'est développé, 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, FILES est 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 particulier: il 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.

33
John1024

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 split:

#! /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
7
Stéphane Chazelas

Ce que vous pouvez faire est de ne laisser que les caractères génériques en dehors des guillemets.
Quelque chose comme:
pour un fichier "avec des espaces" * ". txt"
faire
En traitement
terminé
Si les caractères génériques eux-mêmes se développent en espaces, vous devrez utiliser une approche de fichier par ligne, comme utiliser ls -l pour générer la liste des fichiers et utiliser bash read pour obtenir chaque fichier.

1
Marcelo Pacheco