Самы хуткі спосаб вызначыць, калі прытон прысутнічае

Калі ў мяне ёсць файл з

#!/usr/bin/env foobar

што гэта самы хуткі/лепшы спосаб вызначыць, калі гэты файл мае hashbang? Я чуў, вы можаце проста прачытаць першыя 2 байта? Як?

2
Калі ласка, не стварайце тэг для выдуманай тэрміналогіі.
дададзена аўтар Valters Vingolds, крыніца
Калі ў нас ужо ёсць тэг для чагосьці выкарыстаць гэты тэг. Можа прапанаваць сінонім, калі вы думаеце, што б значэнне ў ім.
дададзена аўтар javanix, крыніца
hashbang/прытон адно і тое ж праўда?
дададзена аўтар Alexander Mills, крыніца
ня адказ няма, я стварыў hashbang тэг для іншага пытання раней сёння :)
дададзена аўтар Alexander Mills, крыніца
дададзена аўтар Alexander Mills, крыніца
дададзена аўтар Alexander Mills, крыніца
можа быць, вы можаце аформіць праўку на гэтую старонку Вікіпедыі
дададзена аўтар Alexander Mills, крыніца
Я думаю, што ёсць людзі, якія ведаюць «hashbang», але не ведаюць «прытона» ... можа быць, SE павінен клапаціцца аб сінонімы дакладна? Ці, можа быць, мадэратары могуць вызначыць сінонімы? незнайка
дададзена аўтар Alexander Mills, крыніца
не ўпэўнены, які адказ прыняць
дададзена аўтар Alexander Mills, крыніца

5 адказы

З ЗШ :

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

Тое ж самае з ksh93 або баш :

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

хоць баш будзе даваць ілжывыя спрацоўвання для файлаў, якія пачынаюцца з NULs варта # і прачытае усе вядучага NUL байт, так што чытаць адзін tebibyte файл, створаны з дапамогай ўсячэнне -s1T файл цалкам 2 байта ў той час, напрыклад.

Так што з баш , было б лепш выкарыстоўваць:

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

Гэта чытаецца <�ет> да 2 байта ў NUL-падзельнікамі запісу.

Тыя ня вілачныя працэсы і ня выконваць дадатковыя каманды, як прачытаць , [ і рэха каманды ўсё убудаваныя.

POSIXly, вы можаце зрабіць:

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

Гэта стражэй у тым, што яна таксама патрабуе поўнай лініі. У Linux, па меншай меры, сімвал новага радка не патрабуецца для правільнага прытон, хоць.

Такім чынам, вы можаце зрабіць:

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

Гэта крыху менш эфектыўным ў тым, што яна патэнцыйна можа прачытаць больш байтаў, з некаторымі абалонкамі адзін байт за адзін раз. З нашым разрэджаным файлам 1TiB, што спатрэбілася б шмат часу ў большасці абалонак (і, магчыма, выкарыстоўваць шмат памяці).

З іншымі, чым абалонак ЗШ , ён таксама можа даваць ілжывыя спрацоўвання для файлаў, якія пачынаюцца з NULs варта #! .

З йаш абалонкі, яно не будзе, калі прытон ўтрымлівае паслядоўнасці байтаў, якія не ўтвараюць дапушчальныя сімвалы ў бягучай лакалі (нават церпяць няўдачу (прынамсі, з 2.39 і старэйшыя), калі прытон што ўтрымліваецца ня -ASCII сімвалы ў C лякаль, нават калі лакаль C маюцца на ўвазе, каб быць адзін, дзе ўсе знакі з'яўляюцца адзінкавымі байтамі і ўсе значэння байта ўтвараюць сапраўдны --Но, калі не абавязкова defined-- сімвалаў)

Калі вы хочаце, каб знайсці ўсе файлы, змесціва якіх пачынаецца з # , вы можаце зрабіць:

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

Мы разглядаем толькі тыя файлы, якія па крайняй меры, 5 байт вялікі ( #!/Х \ п мінімальны рэалістычны прытон).

  • with -exec perl... {} +, we pass as many file paths to perl as possible so run as few invocations as possible
  • -T is to work around that limitation of perl -n and also means it won't work for files whose name ends in ASCII spacing characters or |.
  • PERLIO=raw causes perl to use read() system calls directly without any IO buffering layer (affects the printing of file names as well) so it will do reads of size 2.
  • $/ = \2 when the record separator is set as a reference to a number, it causes records to be fixed length ones.
  • close ARGV skips the rest of the current file after we've read the first record.
3
дададзена

Гэта павінна зрабіць гэта:

if [ "`head -c 2 infile`" = "#!" ]; then
    echo "Hashbang present"
else
    echo "no Hashbang present"
fi
3
дададзена
@don_crissti Сапраўды. Вы можаце рэдагаваць. І, сага, гэта не для ўсіх відавочна, што вы павінны перанакіраваць стандартны ўвод з файла, вы павінны растлумачыць, што такія рэчы.
дададзена аўтар Valters Vingolds, крыніца
калі не на OpenBSD, дзе галава ня не атрымала няма -c сцяга. хітрая, хітрая партатыўнасць ...
дададзена аўтар DanB, крыніца
для партатыўнасці вы можаце выкарыстоўваць дзень замест галавы дд БС = 1 з = 2, калі = входной_файл
дададзена аўтар tman, крыніца
@Gilles дзякуй за ўказанне, і карэкціроўкі.
дададзена аўтар saga, крыніца

Хутка можа ці не можа быць лепш, у залежнасці ад вашых пачуццяў па складанні кучы C (ці, магчыма, некаторыя зборкі, каб атрымаць усё, што накладныя выдаткі на C з шляху. І ўсё, што праверка стомнай памылкі, Sheesh ...)

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

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);
}

Гэта запатрабуе некаторых налад, калі вы хочаце "не" на стандартны вывад разам з адным з (шмат!) <�Код> памыляецца выходзіць з вышэйпералічанага. Верагодна, лепш, каб праверыць слова стану выхаду.

Чым больш павольна шлях абалонкі з галоўкі -c 2 файла не ўдаецца хуткі тэст партатыўнасць ў OpenBSD.

$ head -c 2 /etc/passwd
head: unknown option -- c
usage: head [-count | -n count] [file ...]
$ 
1
дададзена

Вы можаце вызначыць свае ўласныя «чароўныя ўзоры» ў /і г.д./чароўным і выкарыстоўвайце Файл для праверкі:

$ 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 is hex of '#!' in reverse order:

$ 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 `!'

Пры жаданні можна паставіць:

0 string \#\! shebang is present

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

1
дададзена

выкарыстоўваць Grep у растворы адзін-лайнер

if head -1 file | grep "^#\!" > /dev/null;then echo "true"; fi
1
дададзена