¿Cómo puedo convertir pestañas a espacios en cada archivo de un directorio (posiblemente de forma recursiva)?
Además, ¿hay alguna forma de establecer el número de espacios por pestaña?
pr
es una maravillosa utilidad para esto Mira esta respuesta .
¿Cómo puedo convertir pestañas a espacios en cada archivo de un directorio (posiblemente de forma recursiva)?
Además, ¿hay alguna forma de establecer el número de espacios por pestaña?
pr
es una maravillosa utilidad para esto Mira esta respuesta .
Respuestas:
Advertencia: Esto romperá tu repositorio.
Esta voluntad archivos binarios corruptos , incluyendo los menores
svn
,.git
! Lea los comentarios antes de usar!
find . -iname '*.java' -type f -exec sed -i.orig 's/\t/ /g' {} +
El archivo original se guarda como [filename].orig
.
Reemplace '* .java' con el final de archivo del tipo de archivo que está buscando. De esta manera, puede evitar la corrupción accidental de archivos binarios.
Desventajas:
find ./ -type f -exec sed -i 's/^\t/####/g' {} \;
. Pero no conocía el comando expandir, ¡muy útil!
El reemplazo simple con sed
está bien, pero no es la mejor solución posible. Si hay espacios "adicionales" entre las pestañas, todavía estarán allí después de la sustitución, por lo que los márgenes serán desiguales. Las pestañas expandidas en el medio de las líneas tampoco funcionarán correctamente. En bash
, podemos decir en su lugar
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
para aplicar expand
a cada archivo Java en el árbol de directorios actual. Elimine / reemplace el -name
argumento si está apuntando a otros tipos de archivos. Como menciona uno de los comentarios, tenga mucho cuidado al quitar -name
o usar un comodín débil. Puede fácilmente clobber repositorio y otros archivos ocultos sin intención. Es por eso que la respuesta original incluía esto:
Siempre debe hacer una copia de seguridad del árbol antes de intentar algo como esto en caso de que algo salga mal.
{}
. Parece que no sabía $0
cuándo -c
se usa. Luego, dimo414 cambió de mi uso de una temperatura en el directorio de conversión a /tmp
, que será mucho más lento si /tmp
está en un punto de montaje diferente. Lamentablemente, no tengo una caja de Linux disponible para probar su $0
propuesta. Pero creo que tienes razón.
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
sponge
desde joeyh.name/code/moreutils , puede escribirfind . -name '*.py' ! -type d -exec bash -c 'expand -t 8 "$0" | sponge "$0"' {} \;
find . -name '*'
, sólo destruyó mi repositorio git locales
Prueba la herramienta de línea de comando expand
.
expand -i -t 4 input | sponge output
dónde
-i
se usa para expandir solo las pestañas iniciales en cada línea;-t 4
significa que cada pestaña se convertirá en 4 caracteres de espacio en blanco (8 por defecto).sponge
es del moreutils
paquete y evita borrar el archivo de entrada .Finalmente, puede usar gexpand
en OSX, después de instalar coreutils
con Homebrew ( brew install coreutils
).
-i
a expand
para reemplazar solo las pestañas iniciales en cada línea. Esto ayuda a evitar el reemplazo de pestañas que podrían ser parte del código.
input
es el mismo archivo que output
bash clobbers el contenido incluso antes de comenzar expand
. Así es como >
funciona.
Recopilar los mejores comentarios de la respuesta de Gene , la mejor solución con diferencia, es usar sponge
from moreutils .
sudo apt-get install moreutils
# The complete one-liner:
find ./ -iname '*.java' -type f -exec bash -c 'expand -t 4 "$0" | sponge "$0"' {} \;
Explicación:
./
está buscando recursivamente desde el directorio actual-iname
es una coincidencia entre mayúsculas y minúsculas (para ambos *.java
y me *.JAVA
gusta)type -f
encuentra solo archivos regulares (sin directorios, binarios o enlaces simbólicos)-exec bash -c
ejecutar los siguientes comandos en una subshell para cada nombre de archivo, {}
expand -t 4
expande todas las TAB a 4 espaciossponge
absorber la entrada estándar (desde expand
) y escribir en un archivo (el mismo) *.NOTA : * Una redirección de archivo simple ( > "$0"
) no funcionará aquí porque sobrescribiría el archivo demasiado pronto .
Ventaja : se retienen todos los permisos de archivos originales y no tmp
se utilizan archivos intermedios .
Utilice barra invertida con escape sed
.
En linux:
Reemplace todas las pestañas con 1 guión in situ, en todos los archivos * .txt:
sed -i $'s/\t/-/g' *.txt
Reemplace todas las pestañas con 1 espacio in situ, en todos los archivos * .txt:
sed -i $'s/\t/ /g' *.txt
Reemplace todas las pestañas con 4 espacios in situ, en todos los archivos * .txt:
sed -i $'s/\t/ /g' *.txt
En una mac:
Reemplace todas las pestañas con 4 espacios in situ, en todos los archivos * .txt:
sed -i '' $'s/\t/ /g' *.txt
sed -i '' $'s/\t/ /g' $(find . -name "*.txt")
Puede usar el pr
comando generalmente disponible (página man aquí ). Por ejemplo, para convertir pestañas a cuatro espacios, haga esto:
pr -t -e=4 file > file.expanded
-t
suprime encabezados-e=num
expande pestañas a num
espaciosPara convertir todos los archivos en un árbol de directorios de forma recursiva, mientras se saltan los archivos binarios:
#!/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 lógica para omitir archivos binarios es de esta publicación .
NOTA:
expand
dado que ambos son POSIX? Por ejemplo, ¿tiene una opción de cambio en línea? Git safety en: stackoverflow.com/a/52136507/895245
¿Cómo puedo convertir pestañas a espacios en cada archivo de un directorio (posiblemente de forma recursiva)?
Esto generalmente no es lo que quieres.
¿Quieres hacer esto para imágenes png? Archivos PDF? El directorio .git? Tu
Makefile
(que requiere pestañas)? ¿Un volcado de 5GB SQL?
En teoría, podría pasar muchas opciones de exclusión find
o cualquier otra cosa que esté utilizando; pero esto es frágil y se romperá tan pronto como agregue otros archivos binarios.
Lo que quieres es al menos:
expand
hace esto, sed
no).Hasta donde yo sé, no hay una utilidad "estándar" de Unix que pueda hacer esto, y no es muy fácil hacerlo con un shell de una sola línea, por lo que se necesita un script.
Hace un tiempo creé un pequeño script llamado
sanitize_files que hace exactamente eso. También corrige algunas otras cosas comunes como reemplazar \r\n
con \n
, agregar un final \n
, etc.
Puede encontrar una secuencia de comandos simplificada sin las características adicionales y los argumentos de la línea de comandos a continuación, pero le recomiendo que use la secuencia de comandos anterior, ya que es más probable que reciba correcciones de errores y otras actualizaciones que esta publicación.
También me gustaría señalar, en respuesta a algunas de las otras respuestas aquí, que el uso de shell globbing no es una forma sólida de hacerlo, porque tarde o temprano terminarás con más archivos de los que caben ARG_MAX
(en los modernos sistemas Linux es 128k, que puede parecer mucho, pero tarde o temprano es no
suficiente).
#!/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)
Me gusta el ejemplo "encontrar" anterior para la aplicación recursiva. Para adaptarlo para que no sea recursivo, solo cambiando los archivos en el directorio actual que coinciden con un comodín, la expansión de shell glob puede ser suficiente para pequeñas cantidades de archivos:
ls *.java | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh -v
Si lo desea en silencio después de confiar en que funciona, simplemente suelte -v
el sh
comando al final.
Por supuesto, puede elegir cualquier conjunto de archivos en el primer comando. Por ejemplo, enumere solo un subdirectorio (o directorios) particular de una manera controlada como esta:
ls mod/*/*.php | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
O, a su vez, ejecute find (1) con alguna combinación de parámetros de profundidad, etc.
find mod/ -name '*.php' -mindepth 1 -maxdepth 2 | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
ARG_MAX
larga. Esto es 128k en sistemas Linux, pero he encontrado este límite las veces suficientes para no confiar en el bloqueo de shell.
find
se puede decir -maxdepth 1
, y solo procesa las entradas del directorio que se está modificando, no todo el árbol.
Solía astyle
volver a sangrar todo mi código C / C ++ después de encontrar pestañas y espacios mixtos. También tiene opciones para forzar un estilo de llave en particular si lo desea.
Se puede usar vim
para eso:
find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;
Como dijo Carpetsmoker, se recuperará de acuerdo con su vim
configuración. Y modelines en los archivos, si los hay. Además, reemplazará las pestañas no solo al comienzo de las líneas. Que no es lo que generalmente quieres. Por ejemplo, puede tener literales, que contienen pestañas.
:retab
cambiará todas las pestañas de un archivo, no las del inicio. También depende de cuáles son sus :tabstop
y :expandtab
ajustes están en el vimrc o modeline, por lo que este puede no funcionar en absoluto.
tabstop
y expandtab
, funcionará si está utilizando vim
. A menos que tenga líneas de modo en los archivos.
Mi recomendación es usar:
find . -name '*.lua' -exec ex '+%s/\t/ /g' -cwq {} \;
Comentarios:
sed
es un editor de stream. Úselo ex
para la edición en el lugar. Esto evita crear archivos temporales adicionales y generar shells para cada reemplazo como en la respuesta superior .find|xargs
lugar de find -exec
. Como señaló @ gniourf-gniourf, esto lleva a problemas con espacios, comillas y caracteres de control en los nombres de archivo cf. Wheeler .ex
podría no estar disponible en todos los sistemas Unix. Sustituirlo con vi -e
podría funcionar en más máquinas. Además, su expresión regular reemplaza cualquier número de caracteres de tabulación iniciales con dos espacios. Reemplace la expresión regular con +%s/\t/ /g
una sangría de múltiples niveles para no destruir. Sin embargo, esto también afecta a los caracteres de tabulación que no se usan para la sangría.
/\t/ /
variante en mis archivos, pero opté por /\t\+//
no romper las pestañas sin sangría. ¡Perdió los problemas con la sangría múltiple! Actualizando la respuesta. [1] man7.org/linux/man-pages/man1/ex.1p.html#SEE%C2%A0ALSO
xargs
de esta manera es inútil, ineficiente y está roto (piense en nombres de archivos que contengan espacios o comillas). ¿Por qué no usas find
el -exec
interruptor en su lugar?
-print0
opciones para encontrar / xargs. Me gusta xargs -exec
porque: a) Separación de preocupaciones b) se puede intercambiar con GNU en paralelo más fácilmente.
Para convertir todos los archivos Java de forma recursiva en un directorio para usar 4 espacios en lugar de una pestaña:
find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
Puede usar find
con el tabs-to-spaces
paquete para esto.
Primero, instale tabs-to-spaces
npm install -g tabs-to-spaces
luego, ejecute este comando desde el directorio raíz de su proyecto;
find . -name '*' -exec t2s --spaces 2 {} \;
Esto reemplazará cada tab
carácter con 2 spaces
en cada archivo.
¿Ningún cuerpo mencionado rpl
? Usando rpl puedes reemplazar cualquier cadena. Para convertir pestañas en espacios,
rpl -R -e "\t" " " .
muy simple.
El uso de expand
lo sugerido en otras respuestas parece el enfoque más lógico solo para esta tarea.
Dicho esto, también se puede hacer con Bash y Awk en caso de que desee realizar otras modificaciones junto con él.
Si usa Bash 4.0 o superior, el shopt incorporado globstar
puede usarse para buscar recursivamente con **
.
Con GNU Awk versión 4.1 o superior, se pueden realizar modificaciones de archivos "in situ":
shopt -s globstar
gawk -i inplace '{gsub("\t"," ")}1' **/*.ext
En caso de que desee establecer el número de espacios por pestaña:
gawk -i inplace -v n=4 'BEGIN{for(i=1;i<=n;i++) c=c" "}{gsub("\t",c)}1' **/*.ext
Descargue y ejecute el siguiente script para convertir recursivamente las pestañas duras a pestañas suaves en archivos de texto sin formato.
Ejecute el script desde el interior de la carpeta que contiene los archivos de texto sin formato.
#!/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;
Método amigable del repositorio de 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"
)
Actúa sobre todos los archivos del directorio actual:
git-tab-to-space
Actúa solo en archivos C o C ++:
git-tab-to-space '\.(c|h)(|pp)$'
Es probable que desee esto especialmente debido a esos molestos Makefiles que requieren pestañas.
El comando git grep --cached -Il ''
:
.git
como se explica en: ¿Cómo enumerar todos los archivos de texto (no binarios) en un repositorio git?
chmod --reference
mantiene los permisos del archivo sin cambios: /unix/20645/clone-ownership-and-permissions-from-another-file Lamentablemente no puedo encontrar una alternativa sucinta de POSIX .
Si su base de código tuvo la loca idea de permitir pestañas en bruto funcionales en cadenas, use:
expand -i
y luego diviértete repasando todas las pestañas que no son de inicio de línea una por una, que puedes enumerar con: ¿Es posible obtener grep para pestañas?
Probado en Ubuntu 18.04.
Convertir pestañas a espacio solo en archivos ".lua" [pestañas -> 2 espacios]
find . -iname "*.lua" -exec sed -i "s#\t# #g" '{}' \;
expand -t 4 input >output
)
expand -t 4
expandirá la pestaña a\tb
a 3 espacios y la pestaña aa\tb
a 2 espacios, tal como debería ser. expand
toma en cuenta el contexto de una pestaña, sed
no reemplaza y reemplazará la pestaña con la cantidad de espacios que especifique, independientemente del contexto.
Usa el vim-way:
$ ex +'bufdo retab' -cxa **/*.*
globstar
( **
) para la recursividad, actívela mediante shopt -s globstar
.**/*.c
.Para modificar el tabulador, agregue +'set ts=2'
.
Sin embargo, el inconveniente es que puede reemplazar las pestañas dentro de las cadenas .
Entonces, para una solución ligeramente mejor (mediante el uso de sustitución), intente:
$ ex -s +'bufdo %s/^\t\+/ /ge' -cxa **/*.*
O usando ex
editor + expand
utilidad:
$ ex -s +'bufdo!%!expand -t2' -cxa **/*.*
Para espacios finales, vea: ¿Cómo eliminar espacios en blanco finales para múltiples archivos?
Puede agregar la siguiente función en su .bash_profile
:
# Convert tabs to spaces.
# Usage: retab *.*
# See: https://stackoverflow.com/q/11094383/55075
retab() {
ex +'set ts=2' +'bufdo retab' -cxa $*
}
:retab
puede que no funcione en absoluto , el bloqueo de shell es una mala solución para este tipo de cosas , su :s
comando reemplazará cualquier cantidad de pestañas con 2 espacios (que casi nunca quiero), comenzar ex solo para ejecutar un :!expand
proceso es una tontería ...