Universal Node.js shebang?

42

O Node.js é muito popular nos dias de hoje e eu tenho escrito alguns scripts nele. Infelizmente, a compatibilidade é um problema. Oficialmente, o intérprete Node.js deve ser chamado de node , mas o Debian e o Ubuntu enviam um executável chamado nodejs .

Eu quero scripts portáveis com os quais o Node.js possa trabalhar em tantas situações quanto possível. Supondo que o nome do arquivo seja foo.js , eu realmente quero que o script seja executado de duas maneiras:

  1. ./foo.js executa o script se node ou nodejs estiver em $PATH .
  2. node foo.js também executa o script (assumindo que o interpretador é chamado node )

Observação: As respostas de xavierm02 e eu são duas variações de um script poliglota. Eu ainda estou interessado em uma solução pura de shebang, se tal existir.

    
por dancek 18.02.2013 / 21:56

6 respostas

53

O melhor que eu tenho é o "shebang de duas linhas" que é realmente um script poliglota (Bourne shell / Node.js):

#!/bin/sh
':' //; exec "$(command -v nodejs || command -v node)" "$0" "$@"

console.log('Hello world!');

A primeira linha é, obviamente, uma casca de Bourne. Node.js ignora qualquer shebang que encontrar, então este é um arquivo javascript válido no que diz respeito ao Node.js.

A segunda linha chama o shell no-op : com o argumento // e, em seguida, executa nodejs ou node com o nome desse arquivo como parâmetro. command -v é usado em vez de which para portabilidade. A sintaxe de substituição de comandos $(...) não é estritamente Bourne, portanto, opte por backticks se você executar isso nos anos 80.

O Node.js apenas avalia a string ':' , que é como um no-op, e o resto da linha é analisada como um comentário.

O restante do arquivo é simplesmente um javascript antigo. O subshell é encerrado após a conclusão da exec na segunda linha, portanto, o restante do arquivo nunca é lido pelo shell.

Obrigado a xavierm02 pela inspiração e a todos os comentadores por informações adicionais!

    
por 19.02.2013 / 10:11
10
#!/bin/sh
//bin/false || 'which node || which nodejs' << 'tail -n +2 $0'
console.log('ok');

//bin/false é a mesma coisa que /bin/false , exceto que a segunda barra transforma isso em um comentário para nó e é por isso que está aqui. Então, o lado direito do primeiro || é avaliado. 'which node || which nodejs' com backquotes em vez de aspas lança o nó e o << alimenta o que estiver à direita. Eu poderia ter usado um delimitador começando com // como o dancek, ele teria funcionado, mas acho mais limpo ter apenas duas linhas no começo, então usei tail -n +2 $0 para ter o arquivo lido sozinho, exceto os dois primeiros. linhas.

E se você executá-lo no nó, a primeira linha é reconhecida como um shebang e ignorada e a segunda é um comentário de uma linha.

(Aparentemente, o sed poderia ser usado para substituir a cauda Imprima o conteúdo do arquivo sem as primeiras e últimas linhas )

Responder antes de editar:

#!/bin/sh
'which node || which nodejs' <<__HERE__
console.log('ok');
__HERE__

Você não pode fazer o que deseja, então o que você faz é executar um script de shell, por isso o #!/bin/sh . Esse script de shell obterá o caminho do arquivo necessário para executar o nó, que é which node || which nodejs . As backquotes estão aqui para que sejam executadas, portanto, 'which node || which nodejs' (com as backoespaces em vez das aspas) simplesmente chama nó. Então, você apenas alimenta seu script com << . Os __HERE__ são os delimitadores do seu script. E o console.log('ok'); é um exemplo de script que você deve substituir pelo seu script.

    
por 18.02.2013 / 22:28
9

Este é apenas um problema em sistemas baseados em Debian, onde a política superou o sentido.

Eu não sei quando o Fedora forneceu um binário chamado nodejs, mas nunca o vi. O pacote é chamado nodejs e instala um nó chamado binário.

Basta usar um link simbólico para aplicar o senso comum em seus sistemas baseados no Debian, e então você pode usar um shebang sensato. Outras pessoas estarão usando shebangs de qualquer maneira, então você vai precisar desse symlink.

#!/usr/bin/env node

console.log("Spread the love.");
    
por 03.08.2013 / 12:46
3

Se você não se incomodar em criar um pequeno arquivo .sh , tenho uma pequena solução para você. Você pode criar um pequeno script de shell para determinar qual executável do nó deve ser usado e usar esse script no seu shebang:

shebang.sh :

#!/bin/sh
'which node || which nodejs' $@

script.js :

#!./shebang.sh
console.log('hello');

Marque os dois executáveis e execute ./script.js .

Dessa forma, você evita scripts poliglotas. Eu não acho que o uso de múltiplas linhas shebang é possível, embora pareça uma boa idéia.

Embora isso resolva o problema do jeito que você quer, parece que ninguém se importa com isso. Por exemplo, uglifyjs e coffeescript usa #!/usr/bin/env node , npm usa um script de shell como um ponto de entrada, que novamente chama o executável explicitamente com o nome node . Eu sou um usuário do Ubuntu, e eu não sabia disso, já que eu sempre compilo o nó. Estou pensando em relatar isso como um bug.

    
por 20.02.2013 / 15:00
1

Apenas pela perfeição, aqui estão algumas outras maneiras de fazer a segunda linha:

// 2>/dev/null || echo yes
false //|| echo yes

Mas nenhum dos dois tem vantagem sobre a resposta selecionada:

':' //; || echo yes

Além disso, se você sabia que node ou nodejs (mas não ambos) seriam encontrados, os seguintes trabalhos:

exec 'command -v node nodejs' "$0" "$@"

Mas isso é um grande "se", então acho que a resposta selecionada continua sendo a melhor.

    
por 29.01.2016 / 16:26
1

Eu entendo que isso não responde à pergunta, mas estou convencido de que a pergunta é feita com uma falsa premissa.

O Ubuntu está errado aqui. Escrevendo um shebang universal para o seu próprio script não vai mudar outros pacotes que você não tem controle sobre o que usar o padrão de facto de #!/usr/bin/env node . Seu sistema deve fornecer node em PATH se desejar que qualquer script que segmente o nodejs seja executado nele.

Por exemplo, nem mesmo o pacote npm fornecido pelo Ubuntu reescreve shebangs em pacotes:

$ cd
$ rm -rf test_npm
$ mkdir test_npm
$ cd test_npm
$ npm install mkdirp 2>&1 | grep -ve home
'-- [email protected]
  '-- [email protected]

npm WARN test_npm No description
npm WARN test_npm No repository field.
npm WARN test_npm No README data
npm WARN test_npm No license field.
$ node_modules/.bin/mkdirp testdir
/usr/bin/env: 'node': No such file or directory
$ head -n1 node_modules/.bin/mkdirp
#!/usr/bin/env node
$ npm --version
3.5.2
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
    
por 08.05.2017 / 20:27