Préserver les espaces blancs en lisant > > écrire un fichier ligne par ligne dans bash

StackOverflow https://stackoverflow.com/questions/1648055

  •  22-07-2019
  •  | 
  •  

Question

J'essaie de parcourir un répertoire de fichiers texte et de les combiner en un seul document. Cela fonctionne très bien, mais les fichiers texte contiennent des extraits de code et toute ma mise en forme est réduite à gauche. Tous les espaces sur une ligne sont supprimés.

#!/bin/sh
OUTPUT="../best_practices.textile"
FILES="../best-practices/*.textile"
for f in "$FILES"
do
  echo "Processing $f file..."
  echo "">$OUTPUT

  cat $f | while read line; do 
      echo "$line">>$OUTPUT
  done
  echo >>$OUTPUT
  echo >>$OUTPUT
done

Je suis certes un bash noob, mais après avoir cherché haut et bas, je n’ai pas pu trouver une solution adéquate. Apparemment, BASH déteste l’espace blanc dominant en général.

Était-ce utile?

La solution

Au lieu de:

cat $f | while read line; do 
    echo "$line">>$OUTPUT
done

Faites ceci:

cat $f >>$OUTPUT

(S'il y a une raison pour laquelle vous devez faire les choses ligne par ligne, il serait bon de l'inclure dans la question.)

Autres conseils

Comme d'autres l'ont déjà fait remarquer, utiliser cat ou awk à la place d'une boucle lecture-écho est un moyen bien plus efficace de le faire - évite le problème de la réduction des espaces (et de quelques autres que vous n'avez pas rencontrés par hasard), fonctionne plus vite, et au moins avec chat, est simplement un code plus propre. Néanmoins, je voudrais essayer de faire en sorte que la boucle lecture-écho fonctionne correctement.

Premièrement, le problème de la réduction des espaces: la commande de lecture supprime automatiquement les espaces de début et de fin; cela peut être corrigé en modifiant sa définition des espaces en définissant la variable IFS sur vide. En outre, read suppose qu'une barre oblique inversée à la fin de la ligne signifie que la ligne suivante est une continuation et qu'elle doit être jointe à celle-ci. pour résoudre ce problème, utilisez son drapeau -r (raw). Le troisième problème réside dans le fait que de nombreuses implémentations d’écho interprètent les séquences d’échappement dans la chaîne (par exemple, elles peuvent transformer \ n en une nouvelle ligne); pour résoudre ce problème, utilisez printf à la place. Enfin, à titre de règle générale d'hygiène de script, vous ne devez pas utiliser cat lorsque vous n'en avez pas réellement besoin. utilisez plutôt la redirection d'entrée. Avec ces modifications, la boucle interne ressemble à ceci:

while IFS='' read -r line; do 
  printf "%s\n" "$line">>$OUTPUT
done <$f

... il y a aussi quelques autres problèmes avec le script environnant: la ligne qui essaie de définir FILES comme la liste des fichiers .textile entourés par des guillemets, ce qui signifie qu'il n'est jamais développé dans une liste réelle de fichiers . La meilleure façon de faire est d’utiliser un tableau:

FILES=(../best-practices/*.textile)
...
for f in "${FILES[@]}"

(et toutes les occurrences de $ f doivent être entre guillemets au cas où l'un des noms de fichiers comporte des espaces ou d'autres caractères amusants - devrait le faire également avec $ OUTPUT, bien que, comme cela est défini dans le script, réellement en sécurité de partir.)

Enfin, il y a un echo "" > $ OUTPUT près du sommet des fichiers en boucle qui vont effacer le fichier de sortie à chaque fois (c.-à-d. à la fin, il ne contient que le dernier fichier .textile); cela doit être déplacé avant la boucle. Je ne sais pas si l'intention ici était de mettre une seule ligne vierge au début du fichier ou trois lignes vides entre les fichiers (une au début et deux à la fin), alors je ne sais pas exactement quoi le remplacement approprié est. Quoi qu’il en soit, voici ce que je peux faire après avoir réglé tous ces problèmes:

#!/bin/sh
OUTPUT="../best_practices.textile"
FILES=(../best-practices/*.textile)

: >"$OUTPUT"
for f in "${FILES[@]}"
do
  echo "Processing $f file..."
  echo >>"$OUTPUT"

  while IFS='' read -r line; do 
    printf "%s\n" "$line">>"$OUTPUT"
  done <"$f"

  echo >>"$OUTPUT"
  echo >>"$OUTPUT"
done

C’est un moyen trop coûteux de combiner des fichiers.

cat ../best-practices/*.textile >  ../best_practices.textile

si vous souhaitez ajouter un blanc (nouvelle ligne) à chaque fichier lors de la concaténation, utilisez awk

awk 'FNR==1{print "">"out.txt"}{print > "out.txt" }' *.textile

OU

awk 'FNR==1{print ""}{print}' file* > out.txt

Cela vous permet de séparer les nouvelles lignes entre chaque fichier d'entrée comme vous l'avez fait dans votre script d'origine:

for f in $FILES; do echo -ne '\n\n' | cat "$f" -; done > $OUTPUT

Notez que $ FILES n'est pas indiqué pour que cela fonctionne (sinon les nouvelles lignes supplémentaires n'apparaissent qu'une seule fois à la fin de la sortie), mais $ f doit être cité pour protéger les espaces dans les noms de fichiers, s'ils existent.

La bonne réponse, imo, est this , reproduit ci-dessous:

while IFS= read line; do
    check=${line:0:1}
done < file.txt

Notez que cela résoudra les situations dans lesquelles l'entrée est acheminée depuis une autre commande, et pas seulement depuis un fichier réel.

Notez que vous pouvez également simplifier la redirection comme indiqué ci-dessous.

#!/bin/bash
OUTPUT="../best_practices.textile"
FILES="../best-practices/*.textile"
for f in "$FILES"
do
  echo "Processing $f file..."
  {
  echo

  while IFS= read line; do 
      echo "$line"
  done < $f
  echo
  echo;
  } > $OUTPUT
done
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top