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 sed
va 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 expand
a tutti i file Java nella struttura di directory corrente. Rimuovi / sostituisci l' -name
argomento se scegli come target alcuni altri tipi di file. Come menziona uno dei commenti, fai molta attenzione quando rimuovi -name
o 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 $0
quando -c
viene usato. Quindi dimo414 è cambiato dal mio uso di un temp nella directory di conversione a /tmp
, che sarà molto più lento se si /tmp
trova su un punto di montaggio diverso. Sfortunatamente non ho una scatola Linux disponibile per testare la tua $0
proposta. Ma penso che tu abbia ragione.
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
sponge
da 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).sponge
proviene dal moreutils
pacchetto ed evita di cancellare il file di input .Infine, è possibile utilizzare gexpand
su OSX, dopo l'installazione coreutils
con Homebrew ( brew install coreutils
).
-i
a expand
per 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 output
bash, blocca il contenuto prima ancora di iniziare expand
. >
Funziona così .
Raccogliere i migliori commenti dalla risposta di Gene , la soluzione migliore in assoluto , è usare sponge
da 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 *.java
e mi *.JAVA
piace)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 spazisponge
assorbire 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 tmp
vengono 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' *.txt
Sostituisci tutte le schede con 1 spazio sul posto, in tutti i file * .txt:
sed -i $'s/\t/ /g' *.txt
Sostituisci tutte le schede con 4 spazi interni, in tutti i file * .txt:
sed -i $'s/\t/ /g' *.txt
Su un mac:
Sostituisci tutte le schede con 4 spazi interni, in tutti i file * .txt:
sed -i '' $'s/\t/ /g' *.txt
sed -i '' $'s/\t/ /g' $(find . -name "*.txt")
È possibile utilizzare il pr
comando 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=num
espande le schede negli num
spaziPer 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:
expand
fatto 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 find
qualsiasi altra cosa tu stia usando; ma questo è fragile e si interromperà non appena aggiungi altri file binari.
Quello che vuoi è almeno:
expand
non 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\n
con \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 -v
il sh
comando 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_MAX
lunghezza. Questo è 128k su sistemi Linux, ma ho riscontrato questo limite abbastanza volte da non fare affidamento sul gorgogliamento della shell.
find
può essere detto -maxdepth 1
, ed elabora solo le voci della directory che viene modificata, non l'intero albero.
Ero solito astyle
ri-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 vim
per 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 vim
impostazioni. 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.
:retab
cambierà tutte le schede in un file, non quelle all'inizio. dipende anche da quali sono le tue impostazioni :tabstop
e :expandtab
in vimrc o modeline, quindi potrebbe non funzionare affatto.
tabstop
e 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 ex
per 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|xargs
al 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 .ex
potrebbe non essere disponibile su tutti i sistemi Unix. Sostituirlo con vi -e
potrebbe funzionare su più macchine. Inoltre, regex sostituisce qualsiasi numero di caratteri di tabulazione iniziale con due spazi. Sostituisci la regex con +%s/\t/ /g
nessuna 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
xargs
in questo modo è inutile, inefficiente e rotto (si pensi a nomi di file contenenti spazi o virgolette). Perché invece non usi find
l' -exec
interruttore?
-print0
opzioni per trovare / xargs. Mi piacciono gli xargs -exec
da: 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 find
con il tabs-to-spaces
pacchetto 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 tab
carattere con 2 spaces
in 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 expand
come 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 ''
:
.git
come spiegato in: Come elencare tutti i file di testo (non binari) in un repository git?
chmod --reference
mantiene 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 4
espanderà la scheda in a\tb
3 spazi e la scheda in aa\tb
2 spazi, proprio come dovrebbe essere. expand
prende in considerazione il contesto di una scheda, sed
non 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 ex
editor + expand
utility:
$ 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 $*
}
:retab
potrebbe non funzionare affatto , il globbing della shell è una cattiva soluzione per questo genere di cose , il tuo :s
comando sostituirà qualsiasi quantità di schede con 2 spazi (che quasi mai voglia), iniziare ex solo per eseguire un :!expand
processo è sciocco ...