A maneira mais rápida de determinar se shebang está presente

2

Se eu tiver um arquivo com

#!/usr/bin/env foobar

qual é a maneira mais rápida / melhor para determinar se este arquivo tem um hashbang? Eu ouvi que você pode apenas ler os primeiros 2 bytes? Como?

    
por Alexander Mills 24.11.2017 / 22:14

5 respostas

3

Isso deve ser feito:

if [ "'head -c 2 infile'" = "#!" ]; then
    echo "Hashbang present"
else
    echo "no Hashbang present"
fi
    
por 24.11.2017 / 22:22
3

com zsh :

if LC_ALL=C read -u0 -k2 shebang < file && [ "$shebang" = '#!' ]; then
  echo has shebang
fi

Mesmo com ksh93 ou bash :

if IFS= LC_ALL=C read -rN2 shebang < file && [ "$shebang" = '#!' ]; then
  echo has shebang
fi

embora bash daria falsos positivos para arquivos que iniciam com NULs seguidos por #! e leria todos os bytes NUL principais para ler um arquivo tebibyte criado com truncate -s1T file totalmente 2 bytes de cada vez, por exemplo.

Portanto, com bash , seria melhor usar:

IFS= LC_ALL=C read -rn2 -d '' shebang

Isso é lido até 2 bytes de um registro delimitado por NUL.

Eles não bifurcam processos nem executam comandos extras, pois os comandos read , [ e echo são todos internos.

POSIXly, você pode fazer:

if IFS= read -r line < file; then
  case $line in
    ("#!"*) echo has shebang
  esac
fi

É mais estrito que também requer uma linha completa. Pelo menos no Linux, a nova linha não é necessária para um shebang válido.

Então você poderia fazer:

line=
IFS= read -r line < file
case $line in
  ("#!"*) echo has shebang
esac

É um pouco menos eficiente, pois potencialmente lê mais bytes, com alguns shells um byte de cada vez. Com o nosso arquivo esparso de 1TiB, isso levaria muito tempo na maioria dos shells (e potencialmente usaria muita memória).

Com shells diferentes de zsh , ele também pode fornecer falsos positivos para arquivos que começam com NULs seguidos por #! .

Com o shell yash , ele falharia se o shebang contivesse seqüências de bytes que não formam caracteres válidos no código de idioma atual (falharia mesmo (pelo menos com 2.39 e mais) se o shebang contivesse não-ASCII caracteres na localidade C, mesmo que a localidade C deva ser aquela em que todos os caracteres são bytes únicos e todos os valores de bytes formam caracteres válidos - mesmo que não necessariamente definidos--)

Se você quiser encontrar todos os arquivos cujo conteúdo começa com #! , você pode fazer:

PERLIO=raw find . -type f -size +4c -exec perl -T -ne '
  BEGIN{$/=} print "$ARGV\n" if $_ eq "#!"; close ARGV' {} +

Estamos considerando apenas arquivos com pelo menos 5 bytes de tamanho ( #!/x\n do mínimo realista).

  • com -exec perl... {} + , passamos o máximo possível de caminhos de arquivo para perl , por isso, execute o menor número possível de invocações
  • -T é para contornar essa limitação de perl -n e também significa que não funcionará para arquivos cujo nome termine em caracteres de espaçamento ASCII ou | .
  • PERLIO=raw faz com que perl use read() chamadas de sistema diretamente sem qualquer camada de buffer de E / S (afeta também a impressão de nomes de arquivos), portanto, fará leituras de tamanho 2.
  • $/ = quando o separador de registro é definido como uma referência a um número, faz com que os registros sejam de comprimento fixo.
  • close ARGV pula o restante do arquivo atual depois que lemos o primeiro registro.
por 25.11.2017 / 09:36
1

use grep em uma solução de uma linha

if head -1 file | grep "^#\!" > /dev/null;then echo "true"; fi
    
por 24.11.2017 / 22:23
1

O jejum pode ou não ser o melhor, dependendo dos seus sentimentos em compilar um monte de C (ou talvez algum assembly para tirar toda aquela sobrecarga de C do caminho. e toda aquela tediosa verificação de erro, sheesh ...)

#include <sys/types.h>

#include <err.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>

int Flag_Quiet;                 /* -q */

void emit_help(void);

int main(int argc, char *argv[])
{
    int ch;
    char two[2];
    ssize_t amount;

    while ((ch = getopt(argc, argv, "h?q")) != -1) {
        switch (ch) {
        case 'q':
            Flag_Quiet = 1;
            break;
        case 'h':
        case '?':
        default:
            emit_help();
            /* NOTREACHED */
        }
    }
    argc -= optind;
    argv += optind;

    if (argc < 1)
        emit_help();

    if ((ch = open(*argv, O_RDONLY)) == -1)
        err(EX_IOERR, "could not open '%s'", *argv);

    amount = read(ch, two, 2);
    if (amount == -1) {
        err(EX_IOERR, "read failed on '%s'", *argv);
    } else if (amount == 0) {
        err(EX_IOERR, "EOF on read of '%s'", *argv);
    } else if (amount == 2) {
        if (two[0] == '#' && two[1] == '!') {
            amount = 0;
        } else {
            amount = 1;
        }
    } else {
        errx(EX_IOERR, "could not read two bytes from '%s'", *argv);
    }

    if (!Flag_Quiet) {
        printf("%s\n", amount ? "no" : "yes");
    }

    exit(amount);
}

void emit_help(void)
{
    fprintf(stderr, "Usage: hazshebang [-q] file\n");
    exit(EX_USAGE);
}

Isso exigirá alguns ajustes se você quiser um "não" no padrão ao lado de uma das (muitas!) err saídas do acima. Provavelmente, é melhor verificar a palavra de status de saída.

O modo de shell mais lento com head -c 2 file falha em um teste rápido de portabilidade para o OpenBSD.

$ head -c 2 /etc/passwd
head: unknown option -- c
usage: head [-count | -n count] [file ...]
$ 
    
por 25.11.2017 / 02:59
1

Você pode definir seus próprios "padrões mágicos" em /etc/magic e usar file para testar:

$ sudo vi /etc/magic
$ cat /etc/magic
# Magic local data for file(1) command.
# Insert here your local magic data. Format is described in magic(5).
0 byte 0x2123 shebang is present
$ cat /tmp/hole2.sh #To prove [1] order of hex [2] 2nd line ignored
!#/bin/bash 
#!/bin/bash
$ cat /tmp/hole.sh 
#!/bin/bash
$ file /tmp/hole2.sh 
/tmp/hole2.sh: ASCII text
$ file /tmp/hole.sh 
/tmp/hole.sh: shebang is present
$ file -b /tmp/hole.sh #omit filename
shebang is present

0x2123 é hexadecimal de '#!' na ordem inversa:

$ ascii '#' | head -n1
ASCII 2/3 is decimal 035, hex 23, octal 043, bits 00100011: prints as '#'
$ ascii '!' | head -n1
ASCII 2/1 is decimal 033, hex 21, octal 041, bits 00100001: prints as '!'

Opcionalmente, você pode colocar:

0 string \#\! shebang is present

ref: man 5 magic , man 1 file , man 1posix file

    
por 25.11.2017 / 07:31