Come posso convertire le schede in spazi in ogni file di una directory (possibilmente ricorsivamente)?
Inoltre, esiste un modo per impostare il numero di spazi per scheda?
prè una meravigliosa utility per questo. Vedere questo risposta .
Come posso convertire le schede in spazi in ogni file di una directory (possibilmente ricorsivamente)?
Inoltre, esiste un modo per impostare il numero di spazi per scheda?
prè una meravigliosa utility per questo. Vedere questo risposta .
Risposte:
Avviso: questo interromperà il tuo repository.
Questa volontà file binari danneggiati , compresi quelli sotto
svn,.git! Leggi i commenti prima di utilizzare!
find . -iname '*.java' -type f -exec sed -i.orig 's/\t/ /g' {} +
Il file originale viene salvato come [filename].orig.
Sostituisci "* .java" con la fine del tipo di file che stai cercando. In questo modo è possibile prevenire il danneggiamento accidentale dei file binari.
Svantaggi:
expand.
find ./ -type f -exec sed -i 's/^\t/####/g' {} \;. Ma non ero a conoscenza del comando di espansione - molto utile!
La sostituzione semplice con sedva bene ma non è la migliore soluzione possibile. Se ci sono spazi "extra" tra le schede, questi saranno ancora lì dopo la sostituzione, quindi i margini saranno irregolari. Anche le schede espanse nel mezzo delle linee non funzioneranno correttamente. In bash, possiamo invece dire
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
da applicare expanda tutti i file Java nella struttura di directory corrente. Rimuovi / sostituisci l' -nameargomento se scegli come target alcuni altri tipi di file. Come menziona uno dei commenti, fai molta attenzione quando rimuovi -nameo usi un carattere jolly debole. Puoi facilmente bloccare il repository e altri file nascosti senza intenzione. Ecco perché la risposta originale includeva questo:
Dovresti sempre fare una copia di backup dell'albero prima di provare qualcosa del genere nel caso in cui qualcosa vada storto.
{}. Sembra che non sapesse $0quando -cviene usato. Quindi dimo414 è cambiato dal mio uso di un temp nella directory di conversione a /tmp, che sarà molto più lento se si /tmptrova su un punto di montaggio diverso. Sfortunatamente non ho una scatola Linux disponibile per testare la tua $0proposta. Ma penso che tu abbia ragione.
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
spongeda joeyh.name/code/moreutils , puoi scriverefind . -name '*.py' ! -type d -exec bash -c 'expand -t 8 "$0" | sponge "$0"' {} \;
find . -name '*', ho appena distrutto il mio repo git locale
Prova lo strumento da riga di comando expand.
expand -i -t 4 input | sponge output
dove
-i viene utilizzato per espandere solo le schede iniziali su ogni riga;-t 4 significa che ogni scheda verrà convertita in 4 caratteri di spazi bianchi (8 per impostazione predefinita).spongeproviene dal moreutilspacchetto ed evita di cancellare il file di input .Infine, è possibile utilizzare gexpandsu OSX, dopo l'installazione coreutilscon Homebrew ( brew install coreutils).
-ia expandper sostituire solo le schede iniziali su ogni riga. Ciò consente di evitare la sostituzione di schede che potrebbero far parte del codice.
inputè lo stesso file di outputbash, blocca il contenuto prima ancora di iniziare expand. >Funziona così .
Raccogliere i migliori commenti dalla risposta di Gene , la soluzione migliore in assoluto , è usare spongeda moreutils .
sudo apt-get install moreutils
# The complete one-liner:
find ./ -iname '*.java' -type f -exec bash -c 'expand -t 4 "$0" | sponge "$0"' {} \;
Spiegazione:
./ cerca ricorsivamente dalla directory corrente-inameè una corrispondenza senza distinzione tra maiuscole e minuscole (per entrambi *.javae mi *.JAVApiace)type -f trova solo i file regolari (nessuna directory, binari o link simbolici)-exec bash -c eseguire i seguenti comandi in una subshell per ciascun nome di file, {}expand -t 4 espande tutti i TAB a 4 spazispongeassorbire l'input standard (da expand) e scrivere in un file (lo stesso) *.NOTA : * Un semplice reindirizzamento di file ( > "$0") non funzionerà qui perché sovrascriverebbe il file troppo presto .
Vantaggio : vengono mantenute tutte le autorizzazioni per i file originali e non tmpvengono utilizzati file intermedi .
Usa backslash-escape sed.
Su Linux:
Sostituisci tutte le schede con 1 trattino sul posto, in tutti i file * .txt:
sed -i $'s/\t/-/g' *.txtSostituisci tutte le schede con 1 spazio sul posto, in tutti i file * .txt:
sed -i $'s/\t/ /g' *.txtSostituisci tutte le schede con 4 spazi interni, in tutti i file * .txt:
sed -i $'s/\t/ /g' *.txtSu un mac:
Sostituisci tutte le schede con 4 spazi interni, in tutti i file * .txt:
sed -i '' $'s/\t/ /g' *.txtsed -i '' $'s/\t/ /g' $(find . -name "*.txt")
È possibile utilizzare il prcomando generalmente disponibile (pagina man qui ). Ad esempio, per convertire le schede in quattro spazi, procedere come segue:
pr -t -e=4 file > file.expanded
-t sopprime le intestazioni-e=numespande le schede negli numspaziPer convertire ricorsivamente tutti i file in un albero di directory, saltando i file binari:
#!/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 logica per saltare i file binari è da questo post .
NOTA:
expandfatto che entrambi sono POSIX? Ad esempio, ha un'opzione di modifica in linea? Sicurezza Git a: stackoverflow.com/a/52136507/895245
Come posso convertire le schede in spazi in ogni file di una directory (possibilmente ricorsivamente)?
Questo di solito non è quello che vuoi.
Vuoi fare questo per le immagini PNG? File PDF? La directory .git? Il tuo
Makefile(che richiede schede)? Un dump SQL da 5 GB?
In teoria, potresti passare molte opzioni di esclusione a findqualsiasi altra cosa tu stia usando; ma questo è fragile e si interromperà non appena aggiungi altri file binari.
Quello che vuoi è almeno:
expandnon lo sed
fa).Per quanto ne so, non esiste un'utilità Unix "standard" in grado di farlo, e non è molto facile da fare con una shell one-liner, quindi è necessario uno script.
Qualche tempo fa ho creato un piccolo script chiamato
sanitize_files che fa esattamente questo. Risolve anche alcune altre cose comuni come la sostituzione \r\ncon \n, l'aggiunta di un finale \n, ecc.
Puoi trovare uno script semplificato senza le funzionalità extra e gli argomenti della riga di comando di seguito, ma ti consiglio di utilizzare lo script sopra poiché è più probabile che riceva correzioni di bug e altri aggiornamenti rispetto a questo post.
Vorrei anche sottolineare, in risposta ad alcune delle altre risposte qui, che l'uso di shell globbing non è un modo affidabile per farlo, perché prima o poi finirai con più file di quelli che si adatteranno ARG_MAX(nella moderna I sistemi Linux sono 128k, che può sembrare molto, ma prima o poi non è
abbastanza).
#!/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)
Mi piace l'esempio "trova" sopra per l'applicazione ricorsiva. Per adattarlo a non ricorsivo, cambiando solo i file nella directory corrente che corrispondono a un carattere jolly, l'espansione della shell glob può essere sufficiente per piccole quantità di file:
ls *.java | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh -v
Se vuoi che sia silenzioso dopo che ti fidi che funzioni, basta rilasciare -vil shcomando alla fine.
Ovviamente puoi scegliere qualsiasi set di file nel primo comando. Ad esempio, elenca solo una particolare sottodirectory (o directory) in modo controllato come questo:
ls mod/*/*.php | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
O a sua volta esegui find (1) con una combinazione di parametri di profondità ecc:
find mod/ -name '*.php' -mindepth 1 -maxdepth 2 | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
ARG_MAXlunghezza. Questo è 128k su sistemi Linux, ma ho riscontrato questo limite abbastanza volte da non fare affidamento sul gorgogliamento della shell.
findpuò essere detto -maxdepth 1, ed elabora solo le voci della directory che viene modificata, non l'intero albero.
Ero solito astyleri-rientrare tutto il mio codice C / C ++ dopo aver trovato spazi e schede misti. Ha anche opzioni per forzare un particolare stile di controvento, se lo desideri.
Si può usare vimper quello:
find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;
Come affermato da Carpetsmoker, eseguirà il retab secondo le tue vimimpostazioni. E eventuali modeline nei file, se presenti. Inoltre, sostituirà le schede non solo all'inizio delle righe. Che non è quello che generalmente desideri. Ad esempio, potresti avere letterali, contenenti schede.
:retabcambierà tutte le schede in un file, non quelle all'inizio. dipende anche da quali sono le tue impostazioni :tabstope :expandtabin vimrc o modeline, quindi potrebbe non funzionare affatto.
tabstope expandtab, funzionerà se stai usando vim. A meno che non ci siano linee di modalità nei file.
La mia raccomandazione è di usare:
find . -name '*.lua' -exec ex '+%s/\t/ /g' -cwq {} \;
Commenti:
sedè un editor di stream. Utilizzare exper la modifica sul posto. Questo evita la creazione di file temporanei extra e la generazione di shell per ogni sostituzione come nella risposta principale .find|xargsal posto di find -exec. Come sottolineato da @ gniourf-gniourf questo porta a problemi con spazi, virgolette e caratteri di controllo nei nomi dei file cfr. Wheeler .expotrebbe non essere disponibile su tutti i sistemi Unix. Sostituirlo con vi -epotrebbe funzionare su più macchine. Inoltre, regex sostituisce qualsiasi numero di caratteri di tabulazione iniziale con due spazi. Sostituisci la regex con +%s/\t/ /gnessuna distruzione del rientro multi livello. Tuttavia, ciò influisce anche sui caratteri di tabulazione che non vengono utilizzati per il rientro.
/\t/ /variante sui miei file, ma ho optato per /\t\+//non rompere le schede senza rientro. Mancati i problemi con il rientro multiplo! Aggiornamento della risposta. [1] man7.org/linux/man-pages/man1/ex.1p.html#SEE%C2%A0ALSO
xargsin questo modo è inutile, inefficiente e rotto (si pensi a nomi di file contenenti spazi o virgolette). Perché invece non usi findl' -execinterruttore?
-print0opzioni per trovare / xargs. Mi piacciono gli xargs -execda: a) Separazione delle preoccupazioni b) può essere scambiato con GNU parallelamente più facilmente.
Per convertire ricorsivamente tutti i file Java in una directory per utilizzare 4 spazi anziché una scheda:
find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
È possibile utilizzare findcon il tabs-to-spacespacchetto per questo.
Innanzitutto, installa tabs-to-spaces
npm install -g tabs-to-spaces
quindi, eseguire questo comando dalla directory principale del progetto;
find . -name '*' -exec t2s --spaces 2 {} \;
Ciò sostituirà ogni tabcarattere con 2 spacesin ogni file.
Nessun corpo menzionato rpl? Usando rpl puoi sostituire qualsiasi stringa. Per convertire le schede in spazi,
rpl -R -e "\t" " " .
molto semplice.
L'uso di expandcome suggerito in altre risposte sembra l'approccio più logico solo per questo compito.
Detto questo, può anche essere fatto con Bash e Awk nel caso in cui tu voglia fare qualche altra modifica insieme ad esso.
Se si utilizza Bash 4.0 o versioni successive, è globstar possibile utilizzare la funzione shopt built- in per cercare ricorsivamente **.
Con GNU Awk versione 4.1 o successiva, è possibile apportare modifiche al file sed come "inplace":
shopt -s globstar
gawk -i inplace '{gsub("\t"," ")}1' **/*.ext
Nel caso in cui si desideri impostare il numero di spazi per scheda:
gawk -i inplace -v n=4 'BEGIN{for(i=1;i<=n;i++) c=c" "}{gsub("\t",c)}1' **/*.ext
Scarica ed esegui il seguente script per convertire ricorsivamente le schede rigide in schede morbide in file di testo semplice.
Eseguire lo script dall'interno della cartella che contiene i file di testo normale.
#!/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;
Metodo amichevole repository 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"
)
Agisci su tutti i file nella directory corrente:
git-tab-to-space
Agire solo su file C o C ++:
git-tab-to-space '\.(c|h)(|pp)$'
Probabilmente lo desideri in particolare a causa di quei fastidiosi Makefile che richiedono schede.
Il comando git grep --cached -Il '':
.gitcome spiegato in: Come elencare tutti i file di testo (non binari) in un repository git?
chmod --referencemantiene invariate le autorizzazioni dei file: /unix/20645/clone-ownership-and-permissions-from-another-file Purtroppo non riesco a trovare una succinta alternativa POSIX .
Se la tua base di codice ha avuto la folle idea di consentire schede raw funzionali nelle stringhe, usa:
expand -i
e poi divertiti a esaminare tutte le schede non start una per una, che puoi elencare con: È possibile usare grep per le schede?
Testato su Ubuntu 18.04.
Conversione di schede nello spazio in file ".lua" [schede -> 2 spazi]
find . -iname "*.lua" -exec sed -i "s#\t# #g" '{}' \;
expand -t 4 input >output)
expand -t 4espanderà la scheda in a\tb3 spazi e la scheda in aa\tb2 spazi, proprio come dovrebbe essere. expandprende in considerazione il contesto di una scheda, sednon sostituisce la scheda con la quantità di spazi specificata, indipendentemente dal contesto.
Usa il modo vim:
$ ex +'bufdo retab' -cxa **/*.*
globstar( **) per la ricorsione, attivare entro shopt -s globstar.**/*.c.Per modificare tabstop, aggiungi +'set ts=2'.
Tuttavia, il lato negativo è che può sostituire le linguette all'interno delle stringhe .
Quindi, per una soluzione leggermente migliore (usando la sostituzione), prova:
$ ex -s +'bufdo %s/^\t\+/ /ge' -cxa **/*.*
O usando exeditor + expandutility:
$ ex -s +'bufdo!%!expand -t2' -cxa **/*.*
Per gli spazi finali, vedi: Come rimuovere gli spazi bianchi finali per più file?
È possibile aggiungere la seguente funzione nel proprio .bash_profile:
# Convert tabs to spaces.
# Usage: retab *.*
# See: https://stackoverflow.com/q/11094383/55075
retab() {
ex +'set ts=2' +'bufdo retab' -cxa $*
}
:retabpotrebbe non funzionare affatto , il globbing della shell è una cattiva soluzione per questo genere di cose , il tuo :scomando sostituirà qualsiasi quantità di schede con 2 spazi (che quasi mai voglia), iniziare ex solo per eseguire un :!expandprocesso è sciocco ...