Comment convertir des tabulations en espaces dans chaque fichier d'un répertoire (éventuellement de manière récursive)?
De même, existe-t-il un moyen de définir le nombre d'espaces par onglet?
Attention: cela va casser votre repo.
Ceci corrompra les fichiers binaires , y compris ceux de
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 nom [filename].orig
.
Inconvénients:
Un remplacement simple avec sed
est correct mais pas la meilleure solution possible. S'il y a des "extra" espaces entre les onglets, ils seront toujours là après la substitution, les marges seront alors déchiquetées. Les onglets développés au milieu des lignes ne fonctionneront pas non plus correctement. Dans 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 expand
à chaque fichier Java de l’arborescence de répertoires actuelle. 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 clobber le référentiel et d'autres fichiers cachés sans intention. C'est pourquoi la réponse originale incluait ceci:
Vous devriez toujours faire une copie de sauvegarde de l’arbre avant d’essayer quelque chose comme cela au cas où quelque chose se passerait mal.
Essayez l'outil de ligne de commande expand
.
expand -i -t 4 input | sponge output
où
-i
est utilisé pour développer uniquement les onglets de tête sur chaque ligne;-t 4
signifie que chaque onglet sera converti en 4 caractères d'espacement (8 par défaut).sponge
est issu du moreutils
package et évite l'effacement du fichier d'entrée .Enfin, vous pouvez utiliser gexpand
sur OSX, après avoir installé coreutils
avec Homebrew (brew install coreutils
).
Recueillir les meilleurs commentaires de La réponse de Gene , la meilleure solution de loin, consiste à utiliser 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:
./
cherche récursivement dans le répertoire en cours-iname
est une correspondance insensible à la casse (pour les goûts *.Java
et *.Java
)type -f
ne trouve que des fichiers normaux (pas de répertoires, binaires ou liens symboliques)-exec bash -c
exécute les commandes suivantes dans un sous-shell pour chaque nom de fichier, {}
expand -t 4
étend tous les onglets à 4 espacessponge
absorber les entrées standard (à partir de expand
) et écrire dans un fichier (le même) *. NOTE: * Une simple redirection de fichier (> "$0"
) ne fonctionnera pas ici car elle écraserait le fichier trop tôt .
Advantage: toutes les autorisations de fichier d'origine sont conservées et aucun fichier tmp
intermédiaire n'est utilisé.
Utilisez sed
avec une barre oblique inversée.
Sur linux:
Remplacez tous les onglets par 1 trait d'union, dans tous les fichiers * .txt:
sed -i $'s/\t/-/g' *.txt
Remplacez tous les onglets par 1 espace inséré, dans tous les fichiers * .txt:
sed -i $'s/\t/ /g' *.txt
Remplacez tous les onglets par 4 espaces, dans tous les fichiers * .txt:
sed -i $'s/\t/ /g' *.txt
Sur un mac:
Remplacez tous les onglets par 4 espaces, dans tous les fichiers * .txt:
sed -i '' $'s/\t/ /g' *.txt
J'aime l'exemple de "trouver" ci-dessus pour l'application récursive. Pour l'adapter de manière non récursive, en modifiant uniquement les fichiers du répertoire en cours qui correspondent à un caractère générique, l'extension globale du shell peut être suffisante 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 souhaitez le garder silencieux une fois que vous avez confiance que cela fonctionne, supprimez simplement le -v
sur la commande sh
à la fin.
Bien sûr, vous pouvez choisir n’importe quel ensemble de fichiers dans la première commande. Par exemple, répertoriez uniquement un sous-répertoire particulier (ou des répertoires) 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 alors exécutez find (1) avec une combinaison de paramètres de profondeur, etc.:
find mod/ -name '*.php' -mindepth 1 -maxdepth 2 | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
Vous pouvez utiliser la commande pr
généralement disponible (page de manuel ici ). Par exemple, pour convertir des tabulations en quatre espaces, procédez comme suit:
pr -t -e=4 file > file.expanded
-t
supprime les en-têtes-e=num
étend les onglets à num
espacesPour convertir tous les fichiers d'une arborescence de répertoires de manière récursive, en ignorant les fichiers binaires:
#!/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 binaires provient de this post .
REMARQUE:
Comment puis-je convertir des tabulations en espaces dans chaque fichier d'un répertoire (éventuellement Récursivement)?
C’est généralement pas ce que vous voulez.
Voulez-vous faire cela pour les images png? PDF fichiers? Le répertoire .git? Votre Makefile
(qui nécessite tabs)? Un dump SQL de 5 Go?
Vous pouvez, en théorie, passer beaucoup d'options d'exlude à find
ou à quoi que ce soit d'autre Que vous utilisez; mais ceci est fragile et se cassera dès que vous ajoutez d'autres fichiers binaires
Ce que vous voulez, c'est au moins:
expand
le fait, sed
Ne le fait pas).Autant que je sache, il n’existe pas d’utilitaire Unix "standard" capable de le faire, et ce n’est pas très facile à faire avec une ligne de commande Shell, un script est donc 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 problèmes courants, tels que le remplacement de \r\n
par \n
, En ajoutant un \n
final, 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 bugs et Autres mises à jour que ce message. .
Je voudrais également souligner, en réponse à certaines des réponses données ici: Que l'utilisation de Shell globbing n'est pas une manière robuste de le faire, car plus tôt ou plus tard vous finirez par vous retrouver avec plus de fichiers que le contenu de ARG_MAX
(sur les systèmes modernes Linux, il s’agit de 128 Ko, ce qui peut sembler beaucoup, mais tôt ou tard, il suffira de pas.__).
#!/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 __== '__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)
Pour convertir tous les fichiers Java de manière récursive dans un répertoire, utilisez 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 de manière récursive des onglets fixes en onglets souples dans des fichiers texte.
Exécutez le script depuis l'intérieur du dossier contenant les fichiers de 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;
Pas de corps mentionné rpl
? En utilisant rpl, vous pouvez remplacer n’importe quelle chaîne. Pour convertir des onglets en espaces,
rpl -R -e "\t" " " .
très simple.
J'ai utilisé astyle
pour réindenter tout mon code C/C++ après avoir trouvé des tabulations et des espaces mélangés. Il a également des options pour forcer un style d'accolade particulier si vous le souhaitez.
L'utilisation de expand
comme suggéré dans d'autres réponses semble être l'approche la plus logique pour cette tâche uniquement.
Cela dit, cela peut également être fait avec Bash et Awk au cas où vous voudriez faire d'autres modifications avec.
Si vous utilisez Bash 4.0 ou une version ultérieure, utilisez shopt builtinglobstar
pour rechercher récursivement avec **
.
Avec GNU Awk version 4.1 ou supérieure, le fichier "inplace" peut être modifié:
shopt -s globstar
gawk -i inplace '{gsub("\t"," ")}1' **/*.ext
Si vous souhaitez définir le nombre d'espaces par onglet:
gawk -i inplace -v n=4 'BEGIN{for(i=1;i<=n;i++) c=c" "}{gsub("\t",c)}1' **/*.ext
Ma recommandation est d'utiliser:
find . -name '*.lua' -exec ex '+%s/\t/ /g' -cwq {} \;
Commentaires:
sed
est un éditeur de flux. Utilisez ex
pour la modification sur place. Cela évite de créer des fichiers temporaires supplémentaires et de générer des shells pour chaque remplacement, comme indiqué dans le réponse du haut . find|xargs
au lieu de find -exec
. Comme l'a souligné @ gniourf-gniourf, cela entraîne des problèmes d'espaces, de guillemets et de caractères de contrôle dans les noms de fichiers, cf. Wheeler .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’indique Carpetsmoker, les modifications seront effectuées selon vos paramètres vim
. Et des modelines dans les fichiers, le cas échéant. En outre, il remplacera les tabulations 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 onglets.
Vous pouvez utiliser find
avec tabs-to-spaces
package pour cela.
Tout d'abord, installez 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 tab
par 2 spaces
dans chaque fichier.
Méthode conviviale pour le dépôt Git
git-tab-to-space() (
d="$(mktemp -d)"
git grep --cached -Il '' | grep -E "${1:-.}" | \
xargs -I'{}' bash -c '\
f="${1}/f" \
&& expand -t 4 "$0" > "$f" && \
chmod --reference="$0" "$f" && \
mv "$f" "$0"' \
'{}' "$d" \
;
rmdir "$d"
)
Agit sur tous les fichiers du répertoire actuel:
git-tab-to-space
Agissez uniquement sur les fichiers C ou C++:
git-tab-to-space '\.(c|h)(|pp)$'
Vous voulez probablement cela, notamment à cause de ces Makefiles agaçants qui nécessitent des onglets.
La commande git grep --cached -Il ''
:
.git
comme expliqué à: Comment lister tous les fichiers texte (non binaires) dans un référentiel git?
chmod --reference
conserve les autorisations de fichiers inchangées: https://unix.stackexchange.com/questions/20645/clone-ownership-and-permissions-from-another-file Malheureusement, je impossible de trouver un alternative POSIX succincte .
Si votre base de code a eu l’idée folle d’autoriser les onglets bruts fonctionnels dans les chaînes, utilisez:
expand -i
et amusez-vous ensuite en parcourant un par un tous les onglets autres que le début de la ligne, que vous pouvez répertorier avec: Est-il possible de git grep pour les onglets?
Testé sur Ubuntu 18.04.