Remova os espaços em branco antes de possíveis shebang

1

Eu tenho uma grande coleção de arquivos não binários em um só lugar. Alguns deles têm shebangs e alguns deles (por alguns motivos inexplicáveis) whitespaces na frente dos shebangs. Isso inclui linhas e linhas vazias com apenas espaços em branco!

Exemplo 1:

    #!/usr/bin/env foo bar

Exemplo 2:


   #!/usr/bin/env foo bar

Exemplo 3:

#! /bin/sh -e

Exemplo 4:


    ______          
   / ____/___  ____ 
  / /_  / __ \/ __ \
 / __/ / /_/ / /_/ /
/_/    \____/\____/ 


This is Foo News #324 with the tip of the day:
Don't forget to put #!/bin/sh on top of your shell script files!

Eu adoraria uma solução para sistemas baseados em GNU (Linux) que removeriam os espaços em branco iniciais do arquivo, por exemplo, 1 e 2, deixando 3 e especialmente 4 sozinhos (mesmo que inclua algo como um shebang dentro isso.

Os exemplos 1 e 2 seriam:

#!/usr/bin/env foo bar

O que eu tentei sem sucesso até agora:

  • Como um primeiro passo tentando discernir entre os exemplos 1-3 e 4:

    grep -Pzo '^[ \t\n]+#! ?[ \w/.-]+'
    

    Não funcionou porque grep: unescaped ^ or $ not supported with -Pz .

  • Usando awk :

    awk 'BEGIN {ws_check=1} !/[ \t]+/ {ws_check=0}  /#! ?[ \w/.-]+/,0 && ws_check { print }'
    

    Ainda haveria muito trabalho para detectar o exemplo 4, mas também para imprimir apenas as partes da linha esquerda com o shebang, mas sem aparar o resto.

por phk 19.06.2016 / 18:39

2 respostas

4

Eu usaria perl para introduzir o arquivo na memória e remover qualquer espaço em branco se e somente se o primeiro caractere que não é espaço em branco no arquivo for um shebang:

perl -i.bak -0pe 's/^\s+(?=#!)//' file

Ou, para muitos arquivos:

for f in ./*; do perl -i.bak -0pe 's/^\s+(?=#!)//' "$f"; done

O (?=#!) é lookahead positivo , portanto, o operador de substituição só removerá o espaço em branco (incluindo novas linhas e tabs) desde o início do arquivo que são seguidos por um #! . O -i.bak garante que você mantenha backups de todos os arquivos modificados, apenas no caso. Se tiver certeza de que funciona como esperado, você pode rm *.bak .

As opções perl usadas aqui são:

  • -0 : especifica o separador de registro de entrada ( $/ ) como um número octal ou hexadecimal. Usar -0 por si só faz perl fazer slurp no arquivo e basicamente tratá-lo como uma única linha. * -i.bak : edite o arquivo i nplace e crie um backup do original com a extensão .bak .
  • -p : processa um arquivo de entrada linha por linha e imprime cada linha depois de aplicar o script fornecido por -e .
  • -e : passa um script para ser executado como um parâmetro de linha de comando.
por 19.06.2016 / 19:00
1
perl -i -p -e 'if ($. == 1) {s/^\s+#!/#!/}' *

Isso removerá o espaço em branco inicial antes de #! SOMENTE na primeira linha ( $. == 1 ) de todos os arquivos. todas as outras linhas são passadas sem modificação. perl atualizará os arquivos se alguma coisa mudou ou não (por exemplo, eles terão um novo inode e os timestamps serão atualizados). Veja man perlrun e procure pela segunda ocorrência de -i\[ para detalhes)

Se você quiser apenas modificar (alterar o registro de data e hora, inode etc) arquivos com o espaço em branco anterior a # !, tente algo assim:

awk '/^[[:blank:]]+#!/ && FNR==1 { printf "%s
perl -i -p -e 'if ($. == 1) {s/^\s+#!/#!/}' *
", FILENAME }; {nextfile}' * | xargs -0r perl -i -p -e 'if ($. == 1) {s/^\s+#!/#!/}'

awk exibe uma lista de arquivos correspondentes (a primeira linha possui espaço em branco antes de #!), delimitada por NULs. Isso é alimentado em xargs -0r para executar o perl one-liner neles.

A função nextfile requer o% GNUawk. Ele pode ser omitido em outras versões de awk , mas será executado mais lentamente (já que precisa ler em todas as linhas de todos os arquivos, em vez de ir para o próximo arquivo depois de examinar a primeira linha).

Isso poderia ter sido feito inteiramente em perl , mas isso exigiria muito mais código do que apenas direcionar a saída de awk para xargs perl

    
por 24.06.2016 / 06:01