Convertendo guias em espaços em muitos arquivos

11

Eu tenho muitos arquivos com abas espalhadas por toda parte e gostaria de convertê-los em espaços. Eu sei sobre o expandcomando, mas infelizmente eu teria que digitar cada arquivo usando-o. Existe alguma maneira mais fácil de fazer isso no Linux?

pessoa
fonte

Respostas:

12

Tente o seguinte:

find ./ -type f -exec sed -i 's/\t/ /g' {} \;

Se você quiser quatro espaços, tente:

find ./ -type f -exec sed -i 's/\t/    /g' {} \;
Nicolas Raoul
fonte
Isso substituirá cada guia por um único espaço. Desde que a pessoa mencionou o uso expand, presumo que ele deseja que o alinhamento do texto seja preservado.
garyjohn
Você precisa 's/\t/ /g'substituir mais do que apenas uma guia por linha.
Daniel Andersson
1
Uma aceleração substancial se houver muitos arquivos em execução " find ./ -type f -exec sed -i ’s/\t/ /g’ {} +" (ou seja, " +" em vez de " \;")), se a findversão suportar (e eu não conheci pessoalmente nenhuma versão que não suporta, mas não é um padrão POSIX , então acho que isso pode acontecer em alguns sistemas. " -exec command {} +" Consulte o manual). Em vez de iniciar uma instância de sedpara cada arquivo, isso criará uma lista de argumentos com o mesmo número de argumentos de nome de arquivo que o sistema suporta ( getconf ARG_MAX= 2097152 no meu sistema), assim como xargse, portanto, iniciará muito menos sedprocessos.
Daniel Andersson
6
Observe para todos os usuários de Mac que encontrarem isso: A versão do OS X sednão entende a \tsequência de escape da guia. Você pode substituí-lo por um caractere de tabulação literal, no qual você pode inserir no shell [Ctrl]+V, [Tab].
Jeremy Banks
expandé provavelmente melhor do que sedpara isso, como explicado em: stackoverflow.com/a/11094620/131824
David Weinraub
6

Existem várias maneiras de fazer isso. Também existem várias maneiras de dar um tiro no pé enquanto faz isso se você não for cuidadoso ou se for novo no Linux como parece. Supondo que você possa criar uma lista de arquivos que deseja converter, usando algo parecido findou manualmente com um editor, basta canalizar essa lista para o seguinte.

while read file
do
   expand "$file" > /tmp/expandtmp
   mv /tmp/expandtmp "$file"
done

Uma maneira de dar um tiro no pé com isso é digitar um erro de digitação, criando um arquivo vazio para todos os nomes de arquivo especificados, excluindo assim o conteúdo de todos os seus arquivos. Portanto, tenha cuidado e teste o que você faz primeiro em um pequeno conjunto de arquivos dos quais você fez backup.

garyjohn
fonte
3
Fazer a mvcondicional no sucesso de expand:expand ... && mv ...
pausa até novo aviso.
Não se esqueça expand -t 4de expandir as guias para 4 espaços. Além disso, esse método pode criar novas linhas à direita. Mas, caso contrário, funciona.
MGold
3
find . -type f -iname "*.js" -print0 | xargs -0 -I foo tab2space foo foo

-I foo cria uma variável de modelo foo para cada linha de entrada, para que você possa consultar a entrada mais de uma vez.

-print0e -0diga aos dois comandos para usar \ 0 como um separador de linhas em vez de ESPAÇO, para que esse comando funcione para caminhos com espaços.

Dustin Getz
fonte
1
find -name \*.js -exec bash -c 'expand -t 4 "$0" | tee "$0"' {} \;

Contras:
arquivos maiores que o tamanho do buffer do canal ( 64 KB ) são truncados

Prós:
nenhum arquivo temporário
maior que o tamanho do buffer do pipe é truncado

raylu
fonte
0

Isto é melhor:

find . -name *.java ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
oDarek
fonte
3
Por que isso é melhor? Não é uma boa idéia usar, /tmp/eporque se algo mais estiver usando esse arquivo, isso vai atrapalhar. Como se dois usuários quisessem usar isso ao mesmo tempo.
Kevin Panko
0

Dei uma chance a este problema com os seguintes requisitos em mente:

  • Filtre os arquivos com base em seus nomes, para processar, por exemplo, apenas arquivos .cpp ou .json
  • Suporte ao processamento paralelo. Caso haja muitos arquivos, isso pode proporcionar uma enorme velocidade
  • A solução deve caber em uma linha para facilitar o uso

O último requisito foi o mais difícil de cumprir porque "expandir" não permite modificar os arquivos no local.

Eu vim com a seguinte solução:

find . -type f -regextype egrep -regex '.*\.(c|cpp|h|hpp)'  -print0 | xargs -0 -n 1 -P 10 -IFILE bash -c ' ( echo "Processing FILE..." && expand -t 4 "FILE" > /tmp/expand.$$ && mv /tmp/expand.$$ "FILE" ) || exit 255'

Aqui está uma explicação:

  • "find" localiza os arquivos a serem processados. "-regextype egrep" permite filtrá-los com base em seus nomes e em uma expressão regular no formato "egrep"
  • o parâmetro "-type f" garante que corresponderemos apenas aos arquivos regulares, não por diretórios de instância ou qualquer outra coisa especial
  • o parâmetro "-regexp" é a própria expressão regular, que corresponde nesse caso a qualquer arquivo que termine com .c, .cpp, .h ou .hpp (o nome inteiro deve corresponder, portanto "file.c2" não , que é o que queremos)
  • "-print0" instrui "find" para imprimir os caminhos do arquivo em sua saída padrão com o caractere 0 no final de cada caminho. Juntamente com a opção "-0" para "xargs", permite passar nomes contendo carrinhos de retorno de uma ferramenta para outra (mesmo que seja uma situação bastante rara ...)
  • O xargs inicia um novo processo para cada caminho ("-n 1"), mas pode executar até 10 processos em paralelo ("-P 10")
  • O xargs usa o alias "FILE" para passar cada caminho do arquivo para o comando, que é um script bash
  • o script bash chama "expand" e salva o resultado em um arquivo temporário cujos nomes contêm o ID do processo atual ($$), para que todos os processos em execução paralelo em um determinado arquivo usem arquivos temporários diferentes
  • o comando inteiro usa o padrão (command1 && command2 && command3) para que o processo pare se algum subcomando retornar um erro
  • se houver algum erro da cadeia "&&" anterior, o script bash retornará um código de saída 255 que fará com que xargs parem imediatamente
ocroquette
fonte