Comment puis-je convertir des tabulations en espaces dans chaque fichier d’un répertoire (éventuellement récursivement)?
En outre, existe-t-il un moyen de définir le nombre d’espaces par onglet?
Attention: Cela va casser votre repo.
Cela corrompra les fichiers binarys , y compris ceux sous
svn
,.git
! Lisez les commentaires avant d’utiliser!
find . -type f -exec sed -i.orig 's/\t/ /g' {} +
Le fichier d’origine est enregistré sous le [filename].orig
.
Inconvénients:
Le remplacement simple avec sed
est correct, mais pas la meilleure solution possible. S’il y a des espaces “supplémentaires” entre les tabs, ils seront toujours là après la substitution, donc les marges seront irrégulières. Les tabs étendus au milieu des lignes ne fonctionneront pas correctement. En bash
, on peut dire à la place
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
appliquer à tous les fichiers Java de l’arborescence de répertoires en cours. Supprimez / remplacez l’argument -name
si vous ciblez d’autres types de fichiers. Comme l’un des commentaires le mentionne, soyez très prudent lorsque vous supprimez -name
ou utilisez un caractère générique faible. Vous pouvez facilement débloquer le référentiel et les autres fichiers cachés sans intention. C’est pourquoi la réponse originale comprenait ceci:
Vous devez toujours faire une copie de sauvegarde de l’arborescence avant d’essayer quelque chose comme cela au cas où quelque chose ne va pas.
Essayez l’outil d’ expand
ligne de commande.
expand -i -t 4 input | sponge output
où
-i
est utilisé pour développer uniquement les tabs principaux sur chaque ligne; -t 4
signifie que chaque onglet sera converti en 4 caractères d’espacement (8 par défaut). sponge
provient du paquet moreutils
et évite de moreutils
le fichier d’entrée . Enfin, vous pouvez utiliser gexpand
sur OSX, après avoir installé coreutils
avec Homebrew ( brew install coreutils
).
Utilisez sed
antislash.
Sous linux:
Remplacez tous les tabs par 1 trait d’union dans tous les fichiers * .txt:
sed -i $'s/\t/-/g' *.txt
Remplacez tous les tabs par 1 espace dans tous les fichiers * .txt:
sed -i $'s/\t/ /g' *.txt
Remplacez tous les tabs par 4 espaces dans tous les fichiers * .txt:
sed -i $'s/\t/ /g' *.txt
Sur un mac:
Remplacez tous les tabs par 4 espaces dans tous les fichiers * .txt:
sed -i '' $'s/\t/ /g' *.txt
Collecter les meilleurs commentaires de la réponse de Gene , la meilleure solution de loin, est d’utiliser l’ sponge
de moreutils .
sudo apt-get install moreutils # The complete one-liner: find ./ -iname '*.java' -type f -exec bash -c 'expand -t 4 "$0" | sponge "$0"' {} \;
Explication:
./
recherche récursivement dans le répertoire courant -iname
est une correspondance insensible à la casse (pour les deux *.java
et *.JAVA
aime) type -f
ne trouve que des fichiers réguliers (pas de répertoires, de binarys ou de liens symboliques) -exec bash -c
exécute les commandes suivantes dans un sous-shell pour chaque nom de fichier, {}
expand -t 4
étend toutes les tabulations à 4 espaces sponge
absorber l’entrée standard (à partir de l’ expand
) et écrire dans un fichier (le même) *. REMARQUE : * Une simple redirection de fichier ( > "$0"
) ne fonctionnera pas ici car cela écraserait le fichier trop tôt .
Avantage : toutes les permissions de fichiers d’origine sont conservées et aucun fichier tmp
intermédiaire n’est utilisé.
J’aime l’exemple “find” ci-dessus pour l’application récursive. Pour l’adapter pour ne pas être récursif, en ne modifiant que les fichiers du répertoire courant qui correspondent à un caractère générique, l’extension glob du shell peut suffire pour de petites quantités de fichiers:
ls *.java | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh -v
Si vous voulez le garder silencieux après que vous ayez confiance que cela fonctionne, déposez simplement -v
sur la commande sh
à la fin.
Bien entendu, vous pouvez choisir n’importe quel jeu de fichiers dans la première commande. Par exemple, ne listez qu’un ou plusieurs sous-répertoires particuliers de manière contrôlée, comme ceci:
ls mod/*/*.php | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
Ou à son tour exécuter find (1) avec une combinaison de parameters de profondeur, etc.:
find mod/ -name '*.php' -mindepth 1 -maxdepth 2 | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
Comment puis-je convertir des tabulations en espaces dans chaque fichier d’un répertoire (éventuellement récursivement)?
Ce n’est généralement pas ce que vous voulez.
Voulez-vous faire cela pour les images png? Fichiers PDF? Le répertoire .git? Votre Makefile
(qui nécessite des tabs)? Un dump SQL de 5 Go?
Vous pourriez, en théorie, passer beaucoup d’options d’exclusion à find
ou autre chose que vous utilisez; mais cela est fragile et se brisera dès que vous appendez d’autres fichiers binarys.
Ce que vous voulez, c’est au moins:
expand
ne le fait pas, sed
ne le fait pas). Autant que je sache, il n’y a pas d’utilitaire Unix “standard” capable de faire cela, et ce n’est pas très facile à faire avec un shell unique, donc un script est nécessaire.
Il y a quelque temps, j’ai créé un petit script appelé sanitize_files qui fait exactement cela. Il corrige également d’autres choses courantes comme le remplacement de \r\n
par \n
, l’ajout d’un \n
traînant, etc.
Vous pouvez trouver un script simplifié sans les fonctionnalités supplémentaires et les arguments de ligne de commande ci-dessous, mais je vous recommande d’utiliser le script ci-dessus car il est plus susceptible de recevoir des corrections de bogues et d’autres mises à jour que ce post.
Je voudrais également souligner, en réponse à certaines des autres réponses ici, que l’utilisation de la globalisation de shell n’est pas un moyen robuste de le faire, car tôt ou tard vous vous retrouverez avec plus de fichiers que ce qui est possible dans ARG_MAX
(on systèmes Linux modernes, il est 128k, ce qui peut sembler beaucoup, mais tôt ou tard ce n’est pas suffisant).
#!/usr/bin/env python # # http://code.arp242.net/sanitize_files # import os, re, sys def is_binary(data): return data.find(b'\000') >= 0 def should_ignore(path): keep = [ # VCS systems '.git/', '.hg/' '.svn/' 'CVS/', # These files have significant whitespace/tabs, and cannot be edited # safely # TODO: there are probably more of these files.. 'Makefile', 'BSDmakefile', 'GNUmakefile', 'Gemfile.lock' ] for k in keep: if '/%s' % k in path: return True return False def run(files): indent_find = b'\t' indent_replace = b' ' * indent_width for f in files: if should_ignore(f): print('Ignoring %s' % f) continue try: size = os.stat(f).st_size # Unresolvable symlink, just ignore those except FileNotFoundError as exc: print('%s is unresolvable, skipping (%s)' % (f, exc)) continue if size == 0: continue if size > 1024 ** 2: print("Skipping `%s' because it's over 1MiB" % f) continue try: data = open(f, 'rb').read() except (OSError, PermissionError) as exc: print("Error: Unable to read `%s': %s" % (f, exc)) continue if is_binary(data): print("Skipping `%s' because it looks binary" % f) continue data = data.split(b'\n') fixed_indent = False for i, line in enumerate(data): # Fix indentation repl_count = 0 while line.startswith(indent_find): fixed_indent = True repl_count += 1 line = line.replace(indent_find, b'', 1) if repl_count > 0: line = indent_replace * repl_count + line data = list(filter(lambda x: x is not None, data)) try: open(f, 'wb').write(b'\n'.join(data)) except (OSError, PermissionError) as exc: print("Error: Unable to write to `%s': %s" % (f, exc)) if __name__ == '__main__': allfiles = [] for root, dirs, files in os.walk(os.getcwd()): for f in files: p = '%s/%s' % (root, f) if do_add: allfiles.append(p) run(allfiles)
Vous pouvez utiliser la commande pr
généralement disponible (page de manuel ici ). Par exemple, pour convertir des tabs en quatre espaces, procédez comme suit:
pr -t -e=4 file > file.expanded
-t
supprime les en-têtes -e=num
étend les tabs aux espaces num
Pour convertir tous les fichiers d’une arborescence de répertoires de manière récursive, en ignorant les fichiers binarys:
#!/bin/bash num=4 shopt -s globstar nullglob for f in **/*; do [[ -f "$f" ]] || continue # skip if not a regular file ! grep -qI "$f" && continue # skip binary files pr -t -e=$num "$f" > "$f.expanded.$$" && mv "$f.expanded.$$" "$f" done
La logique pour ignorer les fichiers binarys provient de cette publication .
REMARQUE:
J’ai utilisé astyle
pour recréer tout mon code C / C ++ après avoir trouvé des tabs et des espaces mixtes. Il a également des options pour forcer un style particulier si vous le souhaitez.
Ma recommandation est d’utiliser:
find . -name '*.lua' -exec ex '+%s/\t/ /g' -cwq {} \;
Commentaires:
sed
est un éditeur de stream. Utilisez ex
pour l’édition en place. Cela évite de créer des fichiers temporaires supplémentaires et de générer des shells pour chaque remplacement, comme dans la réponse supérieure . find|xargs
au lieu de find -exec
. Comme le fait remarquer @gniourf-gniourf, cela conduit à des problèmes d’espaces, de guillemets et de caractères de contrôle dans les noms de fichiers, cf. Wheeler . Pour convertir tous les fichiers Java de manière récursive dans un répertoire pour utiliser 4 espaces au lieu d’un onglet:
find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
Téléchargez et exécutez le script suivant pour convertir récursivement les tabs durs en tabs logiciels dans des fichiers en texte brut.
Exécutez le script depuis le dossier contenant les fichiers texte brut.
#!/bin/bash find . -type f -and -not -path './.git/*' -exec grep -Iq . {} \; -and -print | while read -r file; do { echo "Converting... "$file""; data=$(expand --initial -t 4 "$file"); rm "$file"; echo "$data" > "$file"; }; done;
On peut utiliser vim
pour cela:
find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;
Comme l’a déclaré Carpetsmoker, il sera modifié en fonction de vos parameters vim
. Et les modèles dans les fichiers, le cas échéant. En outre, il remplacera les tabs non seulement au début des lignes. Ce qui n’est pas ce que vous voulez généralement. Par exemple, vous pourriez avoir des littéraux, contenant des tabulations.
Vous pouvez utiliser find
avec le package tabs-to-spaces
pour cela.
Tout d’abord, installez les tabs-to-spaces
npm install -g tabs-to-spaces
ensuite, exécutez cette commande à partir du répertoire racine de votre projet;
find . -name '*' -exec t2s --spaces 2 {} \;
Cela remplacera chaque caractère de tab
par 2 spaces
dans chaque fichier.
Conversion d’tabs en espace dans les fichiers “.lua” [tabs -> 2 espaces]
find . -iname "*.lua" -exec sed -i "s#\t# #g" '{}' \;
Utilisez le vim-way:
$ ex +'bufdo retab' -cxa **/*.*
globstar
( **
) pour la récursivité, activez-le par shopt -s globstar
. **/*.c
Pour modifier le tabstop, ajoutez +'set ts=2'
.
Cependant, l’inconvénient est qu’il peut remplacer les tabs à l’intérieur des chaînes .
Donc, pour une solution légèrement meilleure (en utilisant la substitution), essayez:
$ ex -s +'bufdo %s/^\t\+/ /ge' -cxa **/*.*
Ou en utilisant ex
editor + expand
utilitaire:
$ ex -s +'bufdo!%!expand -t2' -cxa **/*.*
Pour les espaces de fin, voir: Comment supprimer les espaces de fin de fichier pour plusieurs fichiers?
Vous pouvez append la fonction suivante à votre .bash_profile
:
# Convert tabs to spaces. # Usage: retab *.* # See: https://stackoverflow.com/q/11094383/55075 retab() { ex +'set ts=2' +'bufdo retab' -cxa $* }
L’utilisation de l’ expand
comme suggéré dans d’autres réponses semble l’approche la plus logique pour cette seule tâche.
Cela dit, cela peut aussi être fait avec Bash et Awk au cas où vous souhaiteriez apporter d’autres modifications.
Si vous utilisez Bash 4.0 ou une version ultérieure , le globstar intégré de globstar
peut être utilisé pour rechercher de manière récursive avec **
.
Avec GNU Awk version 4.1 ou supérieure, des modifications de fichiers sed comme “inplace” peuvent être effectuées:
shopt -s globstar gawk -i inplace '{gsub("\t"," ")}1' **/*.ext
Si vous souhaitez définir le nombre d’espaces par onglet:
gawk -i inplace -vn=4 'BEGIN{for(i=1;i<=n;i++) c=c" "}{gsub("\t",c)}1' **/*.ext