it-swarm.com.de

Wie kann ich Tabulatoren in Leerzeichen in jeder Datei eines Verzeichnisses konvertieren?

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?

213
Cynede

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:

  • Ersetzt Tabulatoren überall in einer Datei.
  • Wenn in diesem Verzeichnis ein 5 GB-SQL-Dump vorhanden ist, dauert dies lange.
61
Martin Beckett

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.

316
Gene

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.

174
kev

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 Leerzeichen
  • sponge 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.

16
not2qubit

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
    
14
e9t

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
3
drchuck

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-Bereiche

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

  1. Dies kann in einem Git oder Svn Repo gefährlich sein
  2. Dies ist nicht die richtige Lösung, wenn Sie Codedateien mit eingebetteten Registerkarten in String-Literalen haben
3
codeforester

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:

  1. Überspringen Sie Dateien mit einer bestimmten Größe.
  2. Ermitteln Sie, ob eine Datei binär ist, indem Sie das Vorhandensein eines NULL-Bytes prüfen.
  3. Ersetzen Sie nur Tabulatoren am start einer Datei (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)
3
Martin Tournoij

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;
1
daka

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.

1
Theo Belaire

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
1
John B

Meine Empfehlung ist zu verwenden:

find . -name '*.lua' -exec ex '+%s/\t/  /g' -cwq {} \;

Bemerkungen:

  1. Vor Ort bearbeiten. Backups in einem VCS aufbewahren. Es müssen keine * .orig-Dateien erstellt werden. Es ist empfehlenswert, das Ergebnis von Ihrem letzten Commit abzugleichen, um sicherzustellen, dass dies auf jeden Fall wie erwartet funktioniert.
  2. 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
  3. WARNUNG: Dies gilt für alle Registerkarten, nicht nur für die Einrückung. Außerdem werden die Registerkarten nicht kontextsensitiv ersetzt. Dies war für meinen Anwendungsfall ausreichend. Aber für Sie vielleicht nicht akzeptabel.
  4. BEARBEITEN: Eine frühere Version dieser Antwort verwendete 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 .
1

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.

0
x-yuri

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.

0
Harsh Vakharia

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

  • listet nur die verfolgten Dateien auf, also nichts in .git
  • schließt Verzeichnisse, Binärdateien (wäre beschädigt) und Symlinks (würde in reguläre Dateien umgewandelt werden) aus

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.