Jak przekonwertować tabulatory na spacje w każdym pliku katalogu (ewentualnie rekurencyjnie)?
Czy istnieje również sposób ustawienia liczby spacji na zakładkę?
pr
to wspaniałe narzędzie do tego. Zobacz tę odpowiedź .
Jak przekonwertować tabulatory na spacje w każdym pliku katalogu (ewentualnie rekurencyjnie)?
Czy istnieje również sposób ustawienia liczby spacji na zakładkę?
pr
to wspaniałe narzędzie do tego. Zobacz tę odpowiedź .
Odpowiedzi:
Ostrzeżenie: spowoduje to uszkodzenie Twojego repozytorium.
This will uszkodzone pliki binarne , w tym osób poniżej
svn
,.git
! Przeczytaj komentarze przed użyciem!
find . -iname '*.java' -type f -exec sed -i.orig 's/\t/ /g' {} +
Oryginalny plik zostanie zapisany jako [filename].orig
.
Zamień „* .java” na końcówkę pliku, którego szukasz. W ten sposób można zapobiec przypadkowemu uszkodzeniu plików binarnych.
Wady:
expand
.
find ./ -type f -exec sed -i 's/^\t/####/g' {} \;
. Ale nie wiedziałem o poleceniu rozwijania - bardzo przydatne!
Prosta wymiana sed
jest w porządku, ale nie jest najlepszym możliwym rozwiązaniem. Jeśli między zakładkami znajdują się „dodatkowe” spacje, nadal będą one występować po zamianie, więc marginesy będą nierówne. Karty rozszerzone w środku linii również nie będą działać poprawnie. W bash
, możemy powiedzieć, zamiast
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
do zastosowania expand
do każdego pliku Java w bieżącym drzewie katalogów. Usuń / zamień -name
argument, jeśli celujesz na inne typy plików. Jak wspomina jeden z komentarzy, zachowaj ostrożność podczas usuwania -name
lub używania słabej, wieloznacznej karty. Możesz z łatwością zamykać repozytorium i inne ukryte pliki bez intencji. Oto dlaczego pierwotna odpowiedź zawierała to:
Zawsze powinieneś wykonać kopię zapasową drzewa przed wypróbowaniem czegoś takiego na wypadek, gdyby coś poszło nie tak.
{}
. Wygląda na to, że nie wiedział, $0
kiedy -c
jest używany. Następnie dimo414 zmienił się z mojego użycia temp w katalogu konwersji na /tmp
, który będzie znacznie wolniejszy, jeśli /tmp
będzie w innym punkcie montowania. Niestety nie mam dostępnego Linux-a do przetestowania twojej $0
propozycji. Ale myślę, że masz rację.
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
sponge
z joeyh.name/code/moreutils , możesz napisaćfind . -name '*.py' ! -type d -exec bash -c 'expand -t 8 "$0" | sponge "$0"' {} \;
find . -name '*'
, właśnie zniszczyłem moje lokalne repozytorium git
Wypróbuj narzędzie wiersza polecenia expand
.
expand -i -t 4 input | sponge output
gdzie
-i
służy do rozwijania tylko wiodących kart w każdej linii;-t 4
oznacza, że każda karta zostanie przekonwertowana na 4 znaki białych znaków (domyślnie 8).sponge
pochodzi z moreutils
pakietu i pozwala uniknąć czyszczenia pliku wejściowego .Wreszcie możesz korzystać gexpand
z systemu OSX po instalacji za coreutils
pomocą Homebrew ( brew install coreutils
).
-i
do, expand
aby zastąpić tylko wiodące tabulatory w każdej linii. Pomaga to uniknąć zamiany kart, które mogą być częścią kodu.
input
jest tym samym plikiem, co output
bash blokuje zawartość, zanim jeszcze się uruchomi expand
. Tak to >
działa.
Zbieranie najlepszych komentarzy z odpowiedzi Gene'a , najlepszym jak dotąd najlepszym rozwiązaniem jest korzystanie sponge
z moreutils .
sudo apt-get install moreutils
# The complete one-liner:
find ./ -iname '*.java' -type f -exec bash -c 'expand -t 4 "$0" | sponge "$0"' {} \;
Wyjaśnienie:
./
rekurencyjnie szuka z bieżącego katalogu-iname
jest dopasowanie bez rozróżniania wielkości liter (dla obu *.java
i *.JAVA
polubień)type -f
znajduje tylko zwykłe pliki (bez katalogów, plików binarnych lub dowiązań symbolicznych)-exec bash -c
wykonaj następujące polecenia w podpowłoce dla każdej nazwy pliku, {}
expand -t 4
rozszerza wszystkie tabele do 4 pólsponge
wchłonąć standardowe wejście (z expand
) i zapisać do pliku (tego samego) *.UWAGA : * Proste przekierowanie pliku ( > "$0"
) nie będzie tutaj działać, ponieważ zbyt wcześnie nadpisze plik .
Zaleta : wszystkie oryginalne uprawnienia do plików są zachowane i nie tmp
są używane żadne pliki pośrednie .
Użyj znaku odwrotnego ukośnika sed
.
W systemie Linux:
Zamień wszystkie karty na 1 łącznik na miejscu we wszystkich plikach * .txt:
sed -i $'s/\t/-/g' *.txt
Zamień wszystkie karty na 1 miejsce w miejscu, we wszystkich plikach * .txt:
sed -i $'s/\t/ /g' *.txt
Zamień wszystkie tabulatory na 4 spacje we wszystkich plikach * .txt:
sed -i $'s/\t/ /g' *.txt
Na komputerze Mac:
Zamień wszystkie tabulatory na 4 spacje we wszystkich plikach * .txt:
sed -i '' $'s/\t/ /g' *.txt
sed -i '' $'s/\t/ /g' $(find . -name "*.txt")
Możesz użyć ogólnie dostępnego pr
polecenia ( tutaj strona podręcznika ). Na przykład, aby przekonwertować tabulatory na cztery spacje, wykonaj następujące czynności:
pr -t -e=4 file > file.expanded
-t
pomija nagłówki-e=num
rozwija tabulatory do num
spacjiAby rekurencyjnie przekonwertować wszystkie pliki w drzewie katalogów, pomijając pliki binarne:
#!/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
Logika pomijania plików binarnych pochodzi z tego postu .
UWAGA:
expand
tym, że oba są POSIX? Np. Czy ma wbudowaną opcję zmiany? Git safety at: stackoverflow.com/a/52136507/895245
Jak przekonwertować tabulatory na spacje w każdym pliku katalogu (ewentualnie rekurencyjnie)?
Zwykle nie jest to, czego chcesz.
Czy chcesz to zrobić dla obrazów PNG? Pliki PDF? Katalog .git? Twój
Makefile
(który wymaga zakładek)? Zrzut SQL 5 GB?
Teoretycznie możesz przekazać wiele opcji wykluczania find
lub cokolwiek innego, czego używasz; ale jest to kruche i zepsuje się, gdy tylko dodasz inne pliki binarne.
Co chcesz, to co najmniej:
expand
robi to, sed
nie robi).O ile mi wiadomo, nie ma „standardowego” narzędzia uniksowego, które mogłoby to zrobić i nie jest to bardzo łatwe w przypadku powłoki jednowierszowej, więc potrzebny jest skrypt.
Jakiś czas temu stworzyłem mały skrypt o nazwie
sanitize_files, który właśnie to robi. Naprawia również kilka innych rzeczy jak zastąpienie wspólnego \r\n
z \n
dodanie końcowego znaku \n
, itd.
Możesz znaleźć uproszczony skrypt bez dodatkowych funkcji i argumentów wiersza poleceń poniżej, ale zalecamy użycie powyższego skryptu, ponieważ jest bardziej prawdopodobne, że otrzyma poprawki błędów i inne zaktualizowane niż ten post.
Chciałbym również wskazać, w odpowiedzi na niektóre inne odpowiedzi tutaj, że użycie globowania powłoki nie jest solidnym sposobem na zrobienie tego, ponieważ wcześniej czy później skończy się więcej plików, niż zmieści się ARG_MAX
(w nowoczesnych Systemy Linux to 128k, co może wydawać się dużo, ale wcześniej czy później to nie
wystarczy).
#!/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)
Podoba mi się powyższy przykład „znajdź” dla aplikacji rekurencyjnej. Aby dostosować go tak, aby nie był rekurencyjny, zmieniając tylko pliki w bieżącym katalogu, które pasują do znaku wieloznacznego, rozszerzenie globu powłoki może być wystarczające dla małych ilości plików:
ls *.java | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh -v
Jeśli chcesz to cichy po ufasz, że to działa, po prostu upuść -v
na sh
polecenia na końcu.
Oczywiście możesz wybrać dowolny zestaw plików w pierwszym poleceniu. Na przykład, wypisz tylko określony podkatalog (lub katalogi) w kontrolowany sposób:
ls mod/*/*.php | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
Lub z kolei uruchom find (1) z pewną kombinacją parametrów głębokości itp .:
find mod/ -name '*.php' -mindepth 1 -maxdepth 2 | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
ARG_MAX
długa. Jest to 128k w systemach Linux, ale napotkałem ten limit wystarczająco dużo razy, aby nie polegać na globowaniu powłoki.
find
można powiedzieć -maxdepth 1
, i przetwarza tylko wpisy modyfikowanego katalogu, a nie całe drzewo.
Użyłem astyle
do re-indent wszyscy moi C / C ++ kod po znalezieniu mieszane kart i spacji. Ma także opcje wymuszenia określonego stylu nawiasów, jeśli chcesz.
Można vim
do tego użyć :
find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;
Jak stwierdził Carpetsmoker, nastąpi zmiana w zależności od twoich vim
ustawień. I modeliny w plikach, jeśli istnieją. Ponadto zastąpi tabulatory nie tylko na początku linii. Którego na ogół nie chcesz. Na przykład możesz mieć literały zawierające tabulatory.
:retab
zmieni wszystkie zakładki w pliku, a nie te na początku. zależy to również od tego, jakie są twoje ustawienia :tabstop
i :expandtab
ustawienia w vimrc lub modeline, więc może to wcale nie działać.
tabstop
i expandtab
, zadziała, jeśli używasz vim
. Chyba że w plikach znajdują się linie trybu.
Polecam użyć:
find . -name '*.lua' -exec ex '+%s/\t/ /g' -cwq {} \;
Komentarze:
sed
jest edytorem strumieniowym. Użyj ex
do edycji na miejscu. Pozwala to uniknąć tworzenia dodatkowych plików tymczasowych i odradzania powłok dla każdej zamiany, jak w pliku pierwszej odpowiedzi .find|xargs
zamiast find -exec
. Jak wskazał @ gniourf-gniourf, prowadzi to do problemów ze spacjami, cudzysłowami i znakami kontrolnymi w nazwach plików por. Wheeler .ex
może nie być dostępny w każdym systemie Unix. Zastąpienie go vi -e
może działać na większej liczbie komputerów. Ponadto wyrażenie regularne zamienia dowolną liczbę początkowych znaków tabulacji na dwie spacje. Zamień wyrażenie regularne na, +%s/\t/ /g
aby nie niszczyć wielopoziomowego wcięcia. Wpływa to jednak również na znaki tabulacji, które nie są używane do wcięcia.
/\t/ /
wariantu w moich plikach, ale zdecydowałem /\t\+//
się nie łamać tabulatorów bez wcięć. Pominięto problemy z wielokrotnym wcięciem! Aktualizacja odpowiedzi. [1] man7.org/linux/man-pages/man1/ex.1p.html#SEE%C2%A0ALSO
xargs
w ten sposób jest bezużyteczne, nieefektywne i zepsute (pomyśl o nazwach plików zawierających spacje lub cudzysłowy). Dlaczego nie używacie find
„s -exec
przełącznik zamiast?
-print0
opcje find / xargs. Lubię xargs -exec
od kiedy: a) Rozdzielenie obaw b) można łatwiej zamieniać GNU równolegle.
Aby przekonwertować rekurencyjnie wszystkie pliki Java w katalogu, tak aby używały 4 spacji zamiast tabulacji:
find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
Można skorzystać find
z tabs-to-spaces
pakietu do tego.
Najpierw zainstaluj tabs-to-spaces
npm install -g tabs-to-spaces
następnie uruchom to polecenie z katalogu głównego swojego projektu;
find . -name '*' -exec t2s --spaces 2 {} \;
Spowoduje to zastąpienie każdego tab
znaku wartością 2 spaces
w każdym pliku.
Nie wspomniano o ciele rpl
? Za pomocą rpl możesz zamienić dowolny ciąg. Aby przekonwertować tabulatory na spacje,
rpl -R -e "\t" " " .
bardzo prosty.
Zastosowanie, expand
jak sugerowano w innych odpowiedziach, wydaje się najbardziej logicznym podejściem do samego zadania.
To powiedziawszy, można to również zrobić za pomocą Bash i Awk, na wypadek, gdybyś mógł chcieć wprowadzić inne modyfikacje wraz z nim.
Jeśli używasz Bash 4.0 lub nowszego, wbudowanego shopt globstar
można używać do wyszukiwania rekurencyjnego **
.
W GNU Awk w wersji 4.1 lub nowszej można wprowadzać modyfikacje plików typu „inplace”:
shopt -s globstar
gawk -i inplace '{gsub("\t"," ")}1' **/*.ext
Jeśli chcesz ustawić liczbę spacji na zakładkę:
gawk -i inplace -v n=4 'BEGIN{for(i=1;i<=n;i++) c=c" "}{gsub("\t",c)}1' **/*.ext
Pobierz i uruchom następujący skrypt, aby rekurencyjnie konwertować twarde karty na miękkie karty w zwykłych plikach tekstowych.
Uruchom skrypt z folderu zawierającego zwykłe pliki tekstowe.
#!/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;
Metoda przyjazna dla repozytorium 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"
)
Działaj na wszystkie pliki w bieżącym katalogu:
git-tab-to-space
Działaj tylko na plikach C lub C ++:
git-tab-to-space '\.(c|h)(|pp)$'
Prawdopodobnie chcesz tego, szczególnie z powodu tych irytujących plików Makefile, które wymagają zakładek.
Polecenie git grep --cached -Il ''
:
.git
jak wyjaśniono na: Jak wyświetlić listę wszystkich plików tekstowych (niebinarnych) w repozytorium git?
chmod --reference
uprawnienia do plików pozostają niezmienione: /unix/20645/clone-ownership-and-permissions-from-another-file Niestety nie mogę znaleźć zwięzłej alternatywy POSIX .
Jeśli twoja baza kodu wpadła na szalony pomysł, aby zezwolić na funkcjonalne nieprzetworzone tabulacje w łańcuchach, użyj:
expand -i
a potem baw się dobrze, przeglądając kolejno wszystkie nie uruchamiane karty linii, które możesz wypisać: Czy można używać grep dla kart?
Testowane na Ubuntu 18.04.
Konwersja tabulatorów na spacje w plikach „.lua” [tabs -> 2 spacje]
find . -iname "*.lua" -exec sed -i "s#\t# #g" '{}' \;
expand -t 4 input >output
)
expand -t 4
powiększy tabulator a\tb
do 3 spacji, a tab aa\tb
do 2 spacji, tak jak powinien być. expand
bierze pod uwagę kontekst karty, sed
nie zastępuje i zastępuje określoną przez Ciebie ilością spacji, niezależnie od kontekstu.
Użyj vim-way:
$ ex +'bufdo retab' -cxa **/*.*
globstar
( **
) do rekurencji, aktywuj za pomocą shopt -s globstar
.**/*.c
.Aby zmodyfikować tabstop, dodaj +'set ts=2'
.
Jednak wadą jest to, że może zastąpić zakładki wewnątrz ciągów .
Aby uzyskać nieco lepsze rozwiązanie (przy użyciu podstawienia), spróbuj:
$ ex -s +'bufdo %s/^\t\+/ /ge' -cxa **/*.*
Lub za pomocą ex
edytora + expand
narzędzie:
$ ex -s +'bufdo!%!expand -t2' -cxa **/*.*
Aby zobaczyć końcowe spacje, zobacz: Jak usunąć końcowe białe spacje dla wielu plików?
Możesz dodać następującą funkcję do swojego .bash_profile
:
# Convert tabs to spaces.
# Usage: retab *.*
# See: https://stackoverflow.com/q/11094383/55075
retab() {
ex +'set ts=2' +'bufdo retab' -cxa $*
}
:retab
może w ogóle nie działać , globowanie powłoki jest złym rozwiązaniem dla tego rodzaju rzeczy , twoje :s
polecenie zastąpi dowolną liczbę tabulatorów 2 spacjami (które prawie nigdy nie chcę), rozpoczęcie ex po prostu uruchomienia :!expand
procesu jest głupie ...