Como posso converter guias em espaços em todos os arquivos de um diretório (possivelmente recursivamente)?
Além disso, existe uma maneira de definir o número de espaços por guia?
pré uma utilidade maravilhosa para isso. Veja esta resposta .
Como posso converter guias em espaços em todos os arquivos de um diretório (possivelmente recursivamente)?
Além disso, existe uma maneira de definir o número de espaços por guia?
pré uma utilidade maravilhosa para isso. Veja esta resposta .
Respostas:
Aviso: Isso interromperá seu repo.
Esta vontade arquivos binários corruptos , incluindo aqueles sob
svn,.git! Leia os comentários antes de usar!
find . -iname '*.java' -type f -exec sed -i.orig 's/\t/ /g' {} +
O arquivo original é salvo como [filename].orig.
Substitua '* .java' pelo final do arquivo que você está procurando. Dessa forma, você pode impedir a corrupção acidental de arquivos binários.
Desvantagens:
find ./ -type f -exec sed -i 's/^\t/####/g' {} \;. Mas eu não estava ciente do comando de expansão - muito útil!
A substituição simples por sedestá correta, mas não é a melhor solução possível. Se houver espaços "extras" entre as guias, eles ainda estarão lá após a substituição, portanto as margens serão irregulares. Guias expandidas no meio das linhas também não funcionarão corretamente. Em bash, podemos dizer em vez disso
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
para aplicar expanda todos os arquivos Java na árvore de diretórios atual. Remova / substitua o -nameargumento se você estiver direcionando outros tipos de arquivo. Como um dos comentários menciona, tenha muito cuidado ao remover -nameou usar um caractere curinga fraco. Você pode facilmente recuperar repositórios e outros arquivos ocultos sem intenção. É por isso que a resposta original incluiu isso:
Você sempre deve fazer uma cópia de segurança da árvore antes de tentar algo assim, caso algo dê errado.
{}. Parece que ele não sabia $0quando -cé usado. Em seguida, o dimo414 mudou do meu uso de um temp no diretório de conversão para /tmp, que será muito mais lento se /tmpestiver em um ponto de montagem diferente. Infelizmente, não tenho uma caixa Linux disponível para testar sua $0proposta. Mas acho que você está correto.
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
spongede joeyh.name/code/moreutils , você pode escreverfind . -name '*.py' ! -type d -exec bash -c 'expand -t 8 "$0" | sponge "$0"' {} \;
find . -name '*', eu só destruiu o meu local repo git
Experimente a ferramenta de linha de comando expand.
expand -i -t 4 input | sponge output
Onde
-i é usado para expandir apenas as guias principais em cada linha;-t 4 significa que cada guia será convertida em 4 caracteres de espaço em branco (8 por padrão).spongeé do moreutilspacote e evita limpar o arquivo de entrada .Finalmente, você pode usar gexpandno OSX, depois de instalar coreutilscom o Homebrew ( brew install coreutils).
-ipara expandpara substituir apenas as guias principais em cada linha. Isso ajuda a evitar a substituição de guias que podem fazer parte do código.
inputé o mesmo arquivo que outputo bash clobbers o conteúdo antes mesmo de começar expand. É assim que >funciona.
Coletando os melhores comentários de resposta de Gene , a melhor solução, de longe, é usando spongede moreutils .
sudo apt-get install moreutils
# The complete one-liner:
find ./ -iname '*.java' -type f -exec bash -c 'expand -t 4 "$0" | sponge "$0"' {} \;
Explicação:
./ está pesquisando recursivamente a partir do diretório atual-inameé uma correspondência que não diferencia maiúsculas de minúsculas (para ambos *.javae *.JAVAgostos)type -f localiza apenas arquivos regulares (sem diretórios, binários ou links simbólicos)-exec bash -c execute os seguintes comandos em uma subshell para cada nome de arquivo, {}expand -t 4 expande todos os TABs para 4 espaçosspongeabsorva a entrada padrão (de expand) e grave em um arquivo (o mesmo) *.NOTA : * Um simples redirecionamento de arquivo ( > "$0") não funcionará aqui porque substituirá o arquivo muito cedo .
Vantagem : Todas as permissões de arquivo originais são mantidas e nenhum tmparquivo intermediário é usado.
Use escape com barra invertida sed.
No linux:
Substitua todas as guias por 1 hífen no local, em todos os arquivos * .txt:
sed -i $'s/\t/-/g' *.txtSubstitua todas as guias por 1 espaço no local, em todos os arquivos * .txt:
sed -i $'s/\t/ /g' *.txtSubstitua todas as guias com 4 espaços no local, em todos os arquivos * .txt:
sed -i $'s/\t/ /g' *.txtEm um mac:
Substitua todas as guias com 4 espaços no local, em todos os arquivos * .txt:
sed -i '' $'s/\t/ /g' *.txtsed -i '' $'s/\t/ /g' $(find . -name "*.txt")
Você pode usar o prcomando geralmente disponível (página de manual aqui ). Por exemplo, para converter guias em quatro espaços, faça o seguinte:
pr -t -e=4 file > file.expanded
-t suprime cabeçalhos-e=numexpande guias para numespaçosPara converter todos os arquivos em uma árvore de diretórios recursivamente, ignorando arquivos binários:
#!/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
A lógica para pular arquivos binários é desta postagem .
NOTA:
expanddado que ambos são POSIX? Por exemplo, tem uma opção de alteração em linha? Git
Como posso converter guias em espaços em todos os arquivos de um diretório (possivelmente recursivamente)?
Isso geralmente não é o que você deseja.
Deseja fazer isso para imagens png? Arquivos PDF? O diretório .git? Seu
Makefile(que requer guias)? Um dump SQL de 5 GB?
Você poderia, em teoria, passar muitas opções de exclusão para findou o que quer que esteja usando; mas isso é frágil e será interrompido assim que você adicionar outros arquivos binários.
O que você quer é pelo menos:
expandisso sed
não ocorre).Até onde eu sei, não existe um utilitário Unix "padrão" que possa fazer isso, e não é muito fácil fazer isso com um liner de shell, portanto é necessário um script.
Há um tempo atrás, criei um pequeno script chamado
sanitize_files, que faz exatamente isso. Ele também corrige outras coisas comuns, como substituir \r\npor \n, adicionar um final \n, etc.
Você pode encontrar um script simplificado sem os recursos extras e os argumentos da linha de comando abaixo, mas eu recomendo que você use o script acima, pois é mais provável que você receba correções de bugs e outras atualizações atualizadas além desta publicação.
Eu também gostaria de salientar, em resposta a algumas das outras respostas aqui, que o uso de globbing de shell não é uma maneira robusta de fazer isso, porque mais cedo ou mais tarde você terá mais arquivos do que o necessário ARG_MAX(nos modernos sistemas Linux é 128k, que pode parecer muito, mas mais cedo ou mais tarde, é não
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)
Eu gosto do exemplo "find" acima para o aplicativo recursivo. Para adaptá-lo para não ser recursivo, alterando apenas os arquivos no diretório atual que correspondem a um curinga, a expansão do shell glob pode ser suficiente para pequenas quantidades de arquivos:
ls *.java | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh -v
Se você deseja silenciá-lo depois de confiar que ele funciona, basta soltar -vo shcomando no final.
Claro que você pode escolher qualquer conjunto de arquivos no primeiro comando. Por exemplo, liste apenas um subdiretório (ou diretórios) específico de uma maneira controlada como esta:
ls mod/*/*.php | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
Ou, por sua vez, execute find (1) com alguma combinação de parâmetros de profundidade, etc:
find mod/ -name '*.php' -mindepth 1 -maxdepth 2 | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
ARG_MAXcomprimento. Isso é 128k em sistemas Linux, mas eu encontrei esse limite o tempo suficiente para não confiar no globbing do shell.
findpode ser informado -maxdepth 1e processa apenas as entradas do diretório que está sendo modificado, não a árvore inteira.
Eu costumava astylereentrar todo o meu código C / C ++ depois de encontrar guias e espaços mistos. Ele também possui opções para forçar um estilo de chave específico, se você desejar.
Pode-se usar vimpara isso:
find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;
Como o Carpetsmoker declarou, ele fará uma nova revisão de acordo com vim configurações. E modelos nos arquivos, se houver. Além disso, substituirá as guias não apenas no início das linhas. O que não é o que você geralmente deseja. Por exemplo, você pode ter literais, contendo guias.
:retabalterará todas as guias de um arquivo, não aquelas no início. também depende de quais são suas configurações :tabstope :expandtabno vimrc ou modeline, portanto, isso pode não funcionar.
tabstope expandtab, funcionará se você estiver usando vim. A menos que você tenha linhas de modo nos arquivos.
Minha recomendação é usar:
find . -name '*.lua' -exec ex '+%s/\t/ /g' -cwq {} \;
Comentários:
sedé um editor de stream. Use expara edição no local. Isso evita a criação de arquivos temporários extras e conchas de desova para cada substituição, como na resposta superior .find|xargsvez de find -exec. Como apontado por @ gniourf-gniourf, isso leva a problemas com espaços, aspas e caracteres de controle nos nomes de arquivos, cf. Wheeler .expode não estar disponível em todos os sistemas Unix. Substituí-lo por vi -epode funcionar em mais máquinas. Além disso, seu regex substitui qualquer número de caracteres da guia inicial por dois espaços. Substitua o regex por +%s/\t/ /gnão destruir o recuo de vários níveis. No entanto, isso também afeta os caracteres de tabulação que não são usados para indentação.
/\t/ /variante em meus arquivos, mas optei por /\t\+//não quebrar guias não recuadas. Perdeu os problemas com a multi-indentação! Atualizando a resposta. [1] man7.org/linux/man-pages/man1/ex.1p.html#SEE%C2%A0ALSO
xargsdessa maneira é inútil, ineficiente e quebrado (pense em nomes de arquivos que contenham espaços ou aspas). Por que você não usa findo -execcomutador?
-print0opções para encontrar / xargs. Gosto do xargs -execdesde: a) Separação de preocupações b) pode ser trocado com o GNU paralelo mais facilmente.
Para converter todos os arquivos Java recursivamente em um diretório para usar 4 espaços em vez de uma guia:
find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
Você pode usar findcom o tabs-to-spacespacote para isso.
Primeiro, instale tabs-to-spaces
npm install -g tabs-to-spaces
em seguida, execute este comando no diretório raiz do seu projeto;
find . -name '*' -exec t2s --spaces 2 {} \;
Isso substituirá cada tabcaractere por 2 spacesem cada arquivo.
Nenhum corpo mencionado rpl? Usando o rpl, você pode substituir qualquer string. Para converter guias em espaços,
rpl -R -e "\t" " " .
muito simples.
O uso de expand como sugerido em outras respostas parece a abordagem mais lógica para esta tarefa sozinha.
Dito isto, também pode ser feito com o Bash e o Awk, caso você queira fazer outras modificações.
Se estiver usando o Bash 4.0 ou superior, o shopt interno globstar pode ser usado para pesquisar recursivamente **.
Com o GNU Awk versão 4.1 ou superior, é possível fazer modificações no arquivo "inplace":
shopt -s globstar
gawk -i inplace '{gsub("\t"," ")}1' **/*.ext
Caso você queira definir o número de espaços por guia:
gawk -i inplace -v n=4 'BEGIN{for(i=1;i<=n;i++) c=c" "}{gsub("\t",c)}1' **/*.ext
Faça o download e execute o script a seguir para converter recursivamente guias rígidas em guias flexíveis em arquivos de texto sem formatação.
Execute o script de dentro da pasta que contém os arquivos de texto sem formatação.
#!/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 amigável ao repositório 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"
)
Atue em todos os arquivos no diretório atual:
git-tab-to-space
Atue apenas em arquivos C ou C ++:
git-tab-to-space '\.(c|h)(|pp)$'
Você provavelmente deseja isso principalmente por causa dos Makefiles irritantes que exigem guias.
O comando git grep --cached -Il '':
.gitcomo explicado em: Como listar todos os arquivos de texto (não binários) em um repositório git?
chmod --referencemantém as permissões de arquivo inalteradas: /unix/20645/clone-ownership-and-permissions-from-another-file Infelizmente, não consigo encontrar uma alternativa POSIX sucinta .
Se sua base de código teve a ideia maluca de permitir guias funcionais em strings, use:
expand -i
e divirta-se examinando todas as guias que não são do início da linha, uma a uma, com as quais você pode listar: É possível obter o grep para as guias?
Testado no Ubuntu 18.04.
Convertendo guias em espaço apenas em arquivos ".lua" [guias -> 2 espaços]
find . -iname "*.lua" -exec sed -i "s#\t# #g" '{}' \;
expand -t 4 input >output)
expand -t 4expandirá a guia em a\tb3 espaços e a guia em aa\tb2 espaços, exatamente como deveria ser. expandleva em consideração o contexto de uma guia, sednão substitui e substituirá a guia pela quantidade de espaços que você especificar, independentemente do contexto.
Use o vim-way:
$ ex +'bufdo retab' -cxa **/*.*
globstar( **) para recursão, ative por shopt -s globstar.**/*.c.Para modificar o tabstop, adicione +'set ts=2'.
No entanto, o lado negativo é que ele pode substituir as guias dentro das strings .
Portanto, para uma solução um pouco melhor (usando substituição), tente:
$ ex -s +'bufdo %s/^\t\+/ /ge' -cxa **/*.*
Ou usando o exeditor + expandutilitário:
$ ex -s +'bufdo!%!expand -t2' -cxa **/*.*
Para espaços à direita, consulte: Como remover espaços em branco à direita para vários arquivos?
Você pode adicionar a seguinte função ao seu .bash_profile:
# Convert tabs to spaces.
# Usage: retab *.*
# See: https://stackoverflow.com/q/11094383/55075
retab() {
ex +'set ts=2' +'bufdo retab' -cxa $*
}
:retabpode não funcionar , shell globbing é uma solução ruim para esse tipo de coisa , seu :scomando substituirá qualquer quantidade de guias por 2 espaços (que você quase nunca quero), começando ex só para executar um :!expandprocesso é bobagem ...