Wie kann ich Tabulatoren in Leerzeichen in jeder Datei eines Verzeichnisses konvertieren (möglicherweise rekursiv)?
Gibt es auch eine Möglichkeit, die Anzahl der Leerzeichen pro Tab festzulegen?
Warnung: Dies wird Ihr Repo beschädigen.
Dieses wird Binärdateien beschädigen , einschließlich der unter
svn
,.git
! Lesen Sie die Kommentare vor der Verwendung!
find . -type f -exec sed -i.orig 's/\t/ /g' {} +
Die Originaldatei wird als [filename].orig
gespeichert.
Nachteile:
Ein einfacher Austausch mit sed
ist in Ordnung, aber nicht die bestmögliche Lösung. Wenn zwischen den Registerkarten "zusätzliche" Leerzeichen vorhanden sind, werden sie nach der Ersetzung immer noch vorhanden sein. In der Mitte von Zeilen erweiterte Tabs funktionieren ebenfalls nicht richtig. In bash
können wir stattdessen sagen
find . -name '*.Java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
expand
auf jede Java-Datei im aktuellen Verzeichnisbaum anwenden. Entfernen/ersetzen Sie das Argument -name
, wenn Sie auf andere Dateitypen abzielen. Seien Sie vorsichtig, wenn Sie -name
entfernen oder einen schwachen Platzhalter verwenden. Sie können das Repository und andere versteckte Dateien ohne Vorsatz leicht löschen. Deshalb enthielt die ursprüngliche Antwort Folgendes:
Sie sollten immer eine Sicherungskopie des Baums erstellen, bevor Sie so etwas versuchen, falls etwas schief geht.
Versuchen Sie das Befehlszeilentool expand
.
expand -i -t 4 input | sponge output
woher
-i
wird verwendet, um nur führende Tabs in jeder Zeile zu erweitern.-t 4
bedeutet, dass jede Registerkarte in 4 Whitespace-Zeichen konvertiert wird (standardmäßig 8).sponge
stammt aus dem moreutils
-Paket und vermeidet das Löschen der Eingabedatei .Schließlich können Sie gexpand
unter OSX verwenden, nachdem Sie coreutils
mit Homebrew (brew install coreutils
) installiert haben.
Die besten Kommentare aus Gens Antwort , der mit Abstand besten Lösung, werden durch die Verwendung von sponge
aus moreutils gesammelt.
Sudo apt-get install moreutils
# The complete one-liner:
find ./ -iname '*.Java' -type f -exec bash -c 'expand -t 4 "$0" | sponge "$0"' {} \;
Erklärung:
./
sucht rekursiv aus dem aktuellen Verzeichnis-iname
ist eine Übereinstimmung zwischen Groß- und Kleinschreibung (sowohl für *.Java
als auch für *.Java
-Likes).type -f
findet nur reguläre Dateien (keine Verzeichnisse, Binärdateien oder Symlinks)-exec bash -c
führe folgende Befehle in einer Subshell für jeden Dateinamen aus, {}
expand -t 4
erweitert alle TABs um 4 Leerzeichensponge
absorbiert die Standardeingabe (von expand
) und schreibt in eine Datei (dieselbe) *. NOTE: * Eine einfache Dateiumleitung (> "$0"
) funktioniert hier nicht, da die Datei zu früh überschreiben würde .
Advantage: Alle ursprünglichen Dateiberechtigungen werden beibehalten, und es werden keine tmp
-Zwischendateien verwendet.
Verwenden Sie einen Backslash-Escape-Befehl sed
.
Unter Linux:
Ersetzen Sie in allen * .txt-Dateien alle Registerkarten durch einen Bindestrich inplace:
sed -i $'s/\t/-/g' *.txt
Ersetzen Sie in allen * .txt-Dateien alle Registerkarten durch 1 Platz inplace:
sed -i $'s/\t/ /g' *.txt
Ersetzen Sie alle Registerkarten durch 4 Leerzeichen in allen * .txt-Dateien:
sed -i $'s/\t/ /g' *.txt
Auf einem Mac:
Ersetzen Sie alle Registerkarten durch 4 Leerzeichen in allen * .txt-Dateien:
sed -i '' $'s/\t/ /g' *.txt
Ich mag das obige Beispiel für die rekursive Anwendung. Um es so anzupassen, dass es nicht rekursiv ist und nur Dateien im aktuellen Verzeichnis geändert werden können, die mit einem Platzhalter übereinstimmen, kann die Shell-Glob-Erweiterung für kleine Mengen von Dateien ausreichen:
ls *.Java | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh -v
Wenn Sie möchten, dass es stumm bleibt, nachdem Sie sich darauf verlassen haben, dass es funktioniert, lassen Sie den -v
am Ende des Befehls sh
einfach fallen.
Natürlich können Sie im ersten Befehl einen beliebigen Dateisatz auswählen. Listen Sie beispielsweise nur ein bestimmtes Unterverzeichnis (oder Verzeichnisse) auf eine kontrollierte Weise auf:
ls mod/*/*.php | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
Oder führen Sie find (1) mit einer Kombination von Tiefenparametern usw. aus:
find mod/ -name '*.php' -mindepth 1 -maxdepth 2 | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
Sie können den allgemein verfügbaren Befehl pr
verwenden (Manpage hier ). Um beispielsweise Registerkarten in vier Leerzeichen zu konvertieren, führen Sie Folgendes aus:
pr -t -e=4 file > file.expanded
-t
unterdrückt Header-e=num
erweitert Registerkarten um num
-BereicheUm alle Dateien in einer Verzeichnisstruktur rekursiv zu konvertieren, während Binärdateien übersprungen werden:
#!/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
Die Logik zum Überspringen von Binärdateien stammt von this post .
HINWEIS:
Wie kann ich Tabulatoren in Leerzeichen in jeder Datei eines Verzeichnisses konvertieren (möglicherweise Rekursiv)?
Dies ist normalerweise nicht was Sie wollen.
Möchten Sie dies für PNG-Bilder tun? PDF Dateien? Das .git-Verzeichnis? Ihre Makefile
(welche erfordert Tabs)? Ein 5-GB-SQL-Dump
Theoretisch könnten Sie eine ganze Reihe von Ausschlussoptionen an find
oder was auch immer Übergeben, ansonsten verwenden Sie; Dies ist jedoch fragil und bricht ab, sobald Sie andere Binärdateien hinzufügen.
Was Sie wollen, ist zumindest:
expand
tut dies, sed
Nicht).Soweit ich weiß, gibt es kein "Standard" -Unix-Dienstprogramm, das dies tun kann, und es ist nicht einfach, einen Shell-Einzeiler zu verwenden, daher ist ein Skript erforderlich.
Vor einiger Zeit habe ich ein kleines Skript mit dem Namen sanitize_files erstellt, das genau das tut. Es behebt auch einige andere häufige Dinge, wie das Ersetzen von \r\n
durch \n
, Das Hinzufügen eines nachfolgenden \n
, usw.
Sie finden ein vereinfachtes Skript ohne die zusätzlichen Funktionen und Befehlszeilenargumente, aber ich empfehle Ihnen, das obige Skript zu verwenden, da es wahrscheinlich ist, dass es Bugfixes und.
Als Antwort auf einige der anderen Antworten möchte ich auch darauf hinweisen, dass die Verwendung von Shell-Globierung nicht eine robuste Methode ist, da dies früher oder später der Fall ist mit mehr Dateien als in ARG_MAX
passen (auf modernen Linux-Systemen sind es 128k, was viel erscheinen mag, aber früher oder später ist es nicht genug).
#!/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)
So konvertieren Sie alle Java-Dateien rekursiv in ein Verzeichnis, um 4 Leerzeichen anstelle einer Registerkarte zu verwenden:
find . -type f -name *.Java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
Laden Sie das folgende Skript herunter, und führen Sie es aus, um feste Registerkarten in Klartextdateien rekursiv in weiche Registerkarten zu konvertieren.
Führen Sie das Skript innerhalb des Ordners aus, der die Nur-Text-Dateien enthält.
#!/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;
Kein Körper erwähnt rpl
? Mit rpl können Sie einen beliebigen String ersetzen. So konvertieren Sie Tabulatoren in Leerzeichen:
rpl -R -e "\t" " " .
sehr einfach.
Ich habe astyle
verwendet, um meinen gesamten C/C++ - Code wieder einzurücken, nachdem gemischte Tabs und Leerzeichen gefunden wurden. Es gibt auch Optionen, um einen bestimmten Klammerstil zu erzwingen, wenn Sie möchten.
Die Verwendung von expand
, wie in anderen Antworten vorgeschlagen, scheint der logischste Ansatz für diese Aufgabe zu sein.
Das heißt, es kann auch mit Bash und Awk gemacht werden, falls Sie einige andere Modifikationen dazu machen möchten.
Wenn Sie Bash 4.0 oder höher verwenden, können Sie mit der integrierten shoptglobstar
rekursiv nach **
suchen.
Mit GNU Awk Version 4.1 oder höher können sed like "inplace" -Dateimodifikationen vorgenommen werden:
shopt -s globstar
gawk -i inplace '{gsub("\t"," ")}1' **/*.ext
Falls Sie die Anzahl der Leerzeichen pro Tab festlegen möchten:
gawk -i inplace -v n=4 'BEGIN{for(i=1;i<=n;i++) c=c" "}{gsub("\t",c)}1' **/*.ext
Meine Empfehlung ist zu verwenden:
find . -name '*.lua' -exec ex '+%s/\t/ /g' -cwq {} \;
Bemerkungen:
sed
ist ein Stream-Editor. Verwenden Sie ex
für die Bearbeitung vor Ort. Dies vermeidet die Erstellung zusätzlicher temporärer Dateien und Laichschalen für jeden Ersatz wie in der top answer . find|xargs
anstelle von find -exec
. Wie von @ gniourf-gniourf ausgeführt, führt dies zu Problemen mit Leerzeichen, Anführungszeichen und Steuerzeichen in Dateinamen, vgl. Wheeler .Man kann vim
dafür verwenden:
find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;
Wie Carpetsmoker feststellte, wird es entsprechend Ihren vim
-Einstellungen angepasst. Und Modelines in den Dateien, falls vorhanden. Außerdem werden Tabulatoren nicht nur am Anfang der Zeilen ersetzt. Welches ist nicht das, was Sie normalerweise wollen. Zum Beispiel könnten Sie Literale haben, die Tabs enthalten.
Sie können find
mit tabs-to-spaces
package dafür verwenden.
Installieren Sie zuerst tabs-to-spaces
npm install -g tabs-to-spaces
führen Sie dann diesen Befehl aus dem Stammverzeichnis Ihres Projekts aus.
find . -name '*' -exec t2s --spaces 2 {} \;
Dadurch wird jedes tab
-Zeichen in jeder Datei durch 2 spaces
ersetzt.
Git-Repository-freundliche Methode
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"
)
Betrifft alle Dateien im aktuellen Verzeichnis:
git-tab-to-space
Nur für C- oder C++ - Dateien wirksam:
git-tab-to-space '\.(c|h)(|pp)$'
Wahrscheinlich möchten Sie dies vor allem wegen der nervigen Makefiles, die Tabs benötigen.
Der Befehl git grep --cached -Il ''
:
.git
wie erklärt unter: Wie liste ich alle Textdateien (nicht-binär) in einem git-Repository auf?
chmod --reference
hält die Dateiberechtigungen unverändert: https://unix.stackexchange.com/questions/20645/clone-ownership-and-permissions-from-another-file Leider kann ich kann keine prägnante POSIX-Alternative .
Wenn Ihre Codebase die verrückte Idee hatte, funktionale Tabulatoren in Strings zuzulassen, verwenden Sie:
expand -i
und dann viel Spaß beim Durchgehen aller Tabulatoren, die nicht am Anfang der Zeile stehen, und Sie können diese mit folgendem auflisten: Ist es möglich, Grep für Tabs auszuwählen?
Getestet auf Ubuntu 18.04.