Um arquivo de script executável executado no POSIX e no Windows


16

Desafio : escreva um único arquivo de script foo.cmdque possa ser chamado no cmd.exeprompt do Windows, baunilha (não no PowerShell, não no modo administrador), para executar códigos arbitrários específicos do Windows ...

> .\foo.cmd
Hello Windows! 

... mas também ser invocado inalterado de um típico compatível com POSIX (Linux / OSX) janela de comandos ( bash, tcshou zsh), para executar arbitrária código específico do POSIX:

$ chmod a+x foo.cmd
$ ./foo.cmd
Hello POSIX!

... sem a necessidade de instalação ou criação de intérpretes / ferramentas de terceiros.

Eu sei que isso é possível, mas com cruft (ou seja, no Windows, uma ou duas linhas de lixo / mensagem de erro são impressas em stderr ou stdout antes de "Hello Windows!").

O critério vencedor é a minimização (primeiro) do número de linhas de cruft e (segundo) do número de caracteres de cruft.

Cruft pode ser definido como qualquer saída do console (stdout ou stderr) que não seja produzida pelo código de carga útil (arbitrária). As linhas em branco são contadas na contagem de linhas. Novas linhas não são contadas na contagem de caracteres. As pontuações de cruft devem ser somadas nas duas plataformas. Vamos desconsiderar mecanismos como clsesse que varrem a crosta, mas com o custo de também anular a saída anterior do terminal. Se o Windows repetir seus comandos porque você ainda não ativou @echo off, vamos excluir os caracteres que ele gasta na impressão do diretório e prompt atuais.

Um critério secundário é a simplicidade / elegância da solução interna foo.cmd: se "infraestrutura" for definida como qualquer caractere não diretamente envolvido no código de carga arbitrária, minimize primeiro o número de linhas que contêm caracteres de infraestrutura e depois o número total de infraestrutura personagens.

Parabéns extra se a parte do POSIX funcionar apesar do arquivo ter terminações de linha CRLF! (Não tenho certeza de que a última parte seja possível.)

Minha solução existente, que postarei aqui depois que outros tiverem chance, usa 6 linhas de código de infraestrutura (52 caracteres, excluindo novas linhas). Ele produz 5 linhas de cruft, duas das quais estão em branco, todas ocorrendo no Windows (30 caracteres excluindo novas linhas e excluindo a cadeia de diretório / prompt atual que aparece em duas dessas linhas).


tem que ser um script de shell / lote, não é?
cat

1
Alguém conhece um ambiente de teste online para o DOS?
Digital Trauma

1
Encontrei este, mas ele não me permite digitar os caracteres ":", "\", "{" ou "}": - /
Digital Trauma

@DigitalTrauma Há vinho (que você deve ter instalado, se você não fizer isso, porque é acessível)
gato

1
@cat obrigado - Minha resposta parece funcionar com vinho agora.
Digital Trauma

Respostas:


15

0 linhas de cruft, 0 caracteres de cruft, 2 infra. linhas, 21 infra. caracteres, CRLF ok

:<<@goto:eof
@echo Hello Windows!
@goto:eof
echo "Hello POSIX!" #

Removida a outra solução.

17 caracteres usando exit /ba resposta do Digital Trauma:

:<<@exit/b
@echo Hello Windows!
@exit/b
echo "Hello POSIX!" #

Contendor forte! Particularmente a segunda versão (no critério de simplicidade / elegância, é muito melhor não precisar mover as linhas de carga da maneira que a primeira versão faz). Isso funciona para mim no Windows e OSX. Inicialmente, recebi uma linha de cruft dizendo : command not foundsempre que uma linha em branco entrava na carga útil posix, mas finalmente descobri que não estava :na primeira linha, mas sim por causa de CRLFs desprotegidos por ela #. Foi novidade para mim que uma #!linha não é necessária - que foi responsável por duas das linhas de cruft do Windows na minha versão anterior.
Jez

A primeira versão é difícil de classificar - já que presumivelmente adiciona os caracteres : ` >&3` \ a todas as linhas da carga útil, acho que você poderia dizer que o custo da infraestrutura é arbitrariamente alto.
Jez

@jez Você está calculando uma pontuação numérica para obter respostas? Nesse caso, você precisa deixar muito mais claro na questão de como fazer isso.
Digital Trauma

Eu sou novo no codegolf, então TBH eu pensei que era esperado obter respostas objetivamente de alguma forma. No entanto, acho que a questão deixa claro: primeiro, número de linhas de cruft; rompe com isso pelo número de caracteres de cruft; depois, no número de linhas de infraestrutura e no número de caracteres de infraestrutura. Eu não esperava soluções que abafassem as linhas de carga, como a primeira solução de jimmy. Eu vou mudar a pergunta ligeiramente para deixar claro que é indesejável (desculpe por isso ligeira mudança trave, mas eu não acho que isso afeta sua segunda solução ou qualquer versão do seu até agora)
Jez

Parece que nossas respostas estão convergindo ;-) Você tem a vantagem de pontuar com o uso do heredoc. O final é #necessário?
Digital Trauma

4

Pontuação 0 cruft + 4 infra-linhas + 32 infra-caracteres. LF e CRLF OK.

Isso se baseia no que encontrei neste blog , com os bits Amiga e outras linhas desnecessárias removidas. Eu escondi as linhas do DOS entre aspas comentadas, em vez de \continuar usando a linha, para que isso funcione com o CRLF e o LF.

@REM ()(:) #
@REM "
@ECHO Hello Windows!
@EXIT /B
@REM "
echo Hello POSIX!

Com as terminações de linha DOS CRLF ou * nix LF, ele funciona no Ubuntu, OSX e wine:

ubuntu@ubuntu:~$ ./dosix.bat
Hello POSIX!
ubuntu@ubuntu:~$ wine cmd.exe
Wine CMD Version 5.1.2600 (1.6.2)

Z:\home\ubuntu>dosix.bat
Hello Windows!

Z:\home\ubuntu>exit
ubuntu@ubuntu:~$ 

Para criar isso exatamente (com CRLFs) em uma máquina * nix (incluindo OSX), cole o seguinte em um terminal:

[ $(uname) = Darwin ] && decode=-D || decode=-d
ubuntu@ubuntu:~$ base64 $decode > dosix.bat << EOF
QFJFTSAoKSg6KSAjDQpAUkVNICINCkBFQ0hPIEhlbGxvIFdpbmRvd3MhDQpARVhJVCAvQg0KQFJF
TSAiDQplY2hvIEhlbGxvIFBPU0lYIQ0K
EOF

Parece legal! dosixtambém é um nome legal. Mas no meu Mac (OS 10.9.4, Darwin Kernel versão 13.3.0, GNU bash versão 3.2.51) ele falha ao ser executado com: ./dosix.cmd: line 13: syntax error: unexpected end of file Alguma idéia de por quê?
Jez

@jez certifique-se de salvar o arquivo codificado com UTF-8 e preservar as terminações de linha do Windows!
gato

Ah, isso estava acontecendo porque, sem saber, eu tinha finais de linha do Windows e estava usando a primeira versão.
Jez

Observe que você pode inserir o caractere CR no Bash inserindo (ou Cv), digite (ou Cm).
jimmy23013

@jez Portanto, isso corresponde a 0 linhas de cruft + 4 linhas de infraestrutura + 32 caracteres de infraestrutura. Esses números devem ser combinados de maneira significativa para criar uma pontuação única para comparação?
Digital Trauma

2

Já vou postar a solução que estava usando, pois já foi vencida. É cortesia de um colega meu que acho que deve ter lido a mesma entrada de blog que o Digital Trauma .

#!/bin/sh # >NUL 2>&1
echo \
@goto c \
>/dev/null
echo "Hello Posix!"
exit
:c
@echo Hello Windows!
  • Cruft do Windows: 5 linhas (das quais duas estão em branco) / 30 caracteres
  • Crosta OSX: 0
  • Infraestrutura: 6 linhas / 52 caracteres
  • Compatibilidade com CRLF: somente se o intérprete nomeado na #!linha não se importar (portanto, para intérpretes padrão como shamigos e amigos, ele falha)

No POSIX, um script sempre começa com #!, o que significa que a sua é a única resposta até agora para ser um script válido em um sistema POSIX. Certamente alguns dos outros podem executar se - mas somente se forem iniciados a partir de um shell com uma solução alternativa para scripts com defeito.
kasperd

Bom saber! Acho que estou confuso com os critérios estritos para a conformidade com POSIX. Meu objetivo real é ter arquivos de script que funcionem "prontos para uso" na "maioria" dos modernos sistemas de desktop, o que quer que isso signifique - Windows 7/8/10, Ubuntu, OSX ... Quão universal é a solução alternativa para scripts sem margens, Eu me pergunto? Enfim, eu não estou indo para premiar os pontos para mim mesmo :-)
Jez

Eu não tinha notado que tanto a resposta quanto a pergunta foram escritas pela mesma pessoa. Acho que todos os shells com os quais trabalhei têm uma solução alternativa para a falta de uma #!linha, mas eles usarão shells diferentes para interpretar o script. Isso significa que um "script" que não começa #!deve ser válido não apenas em um shell, mas em todos os shell pelos quais possa ser plausivelmente interpretado. Pior ainda, só funciona ao ser iniciado a partir de outro shell. Esse script pode funcionar na linha de comando, mas não no contexto em que você finalmente pretende usá-lo.
kasperd

Obrigado, isso é muito educativo e exatamente o tipo de coisa que eu esperava aprender ao iniciar esse desafio. Nos casos em que isso vai (ou pode) ser importante, a #!linha na minha resposta editada (1 linha de infra-estrutura com 21 caracteres) pode ser combinada com a resposta de qualquer outra pessoa a um custo de apenas duas linhas do Windows (somente uma em branco) ou 23 caracteres.
Jez

2

Resumo / síntese de respostas e discussão

Isso foi divertido, e eu aprendi muito.

Alguns itens específicos do Windows são inevitáveis ​​se, no seu sistema POSIX, você precisar iniciar o script com uma #!linha. Se você não tem alternativa a não ser fazer isso, então esta linha:

#!/bin/sh # 2>NUL

é provavelmente o melhor que pode obter. Isso faz com que uma linha em branco e uma linha de cruft sejam exibidas no console do Windows. No entanto, você pode se safar sem uma #!linha: na maioria dos sistemas, um dos intérpretes shell comuns acabará executando o script (o problema é que não é universalmente previsível qual intérprete será - depende, mas será não necessariamente ser idêntico ao shell que você usa para chamar o comando).

Além dessa primeira linha complicada, havia algumas soluções realmente sem criatividade. A submissão vencedora por jimmy23013 consistiu em apenas duas linhas de infraestrutura curtas e fez uso do duplo papel do :personagem para implementar uma linha "silenciosa" em ambas as plataformas (como um não-op de fatosh e amigos e como um rótulo marcador em cmd.exe):

:<<@exit/b

:: Arbitrary Windows code goes here

@exit/b
#
# Arbitrary POSIX code goes here 

Ele é possível fazer um script executado em sistemas POSIX, mesmo apesar CRLF linha-finais, mas para fazer isso para a maioria dos intérpretes você tem que acabar com todos os linha de sua seção POSIX (mesmo linhas em branco) com um caractere de comentário ou comentário.

Finalmente, aqui estão duas variantes de uma solução que desenvolvi com base nas informações de todos. Eles podem ser quase os melhores de todos os mundos, pois minimizam os danos causados ​​pela falta #!e tornam a compatibilidade com CRLF ainda mais suave. São necessárias duas linhas de infraestrutura extras. Somente uma linha (padronizada) deve ser interpretada pelo imprevisível shell POSIX, e essa linha permite selecionar o shell para o restante do script ( bashno exemplo a seguir):

:<<@GOTO:Z

@echo Hello Windows!

@GOTO:Z
bash "$@"<<:Z
#
echo "Hello POSIX!" #
ps # let's throw this into the payload to keep track of which shell is being used
#
:Z

Parte da beleza dessas soluções heredoc é que elas ainda são robustas para CRLF: desde que <<:Zchegue ao final da linha, o processador heredoc estará realmente procurando e encontrará o token:Z\r

Como uma reviravolta final, você pode se livrar desses comentários de fim de linha irritantes e ainda manter a robustez do CRLF, removendo os \rcaracteres antes de passar as linhas para o shell. Isso coloca um pouco mais de fé no shell imprevisível (seria bom usar em { tr -d \\r|bash;}vez de, (tr -d \\r|bash)mas colchetes são sintaxe somente do bash):

:<<@GOTO:Z

@echo Hello Windows!

@GOTO:Z
(tr -d \\r|bash "$@")<<:Z

echo "Hello POSIX!"
ps

:Z

Obviamente, essa abordagem sacrifica a capacidade de canalizar a entrada stdin no script.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.