Shebangs independentes do caminho


20

Eu tenho um script que eu quero poder executar em duas máquinas. Essas duas máquinas obtêm cópias do script do mesmo repositório git. O script precisa ser executado com o intérprete correto (por exemplo zsh).

Infelizmente, tanto env e zshvivem em diferentes locais em máquinas locais e remotos:

Máquina remota

$ which env
/bin/env

$ which zsh
/some/long/path/to/the/right/zsh

Máquina local

$ which env
/usr/bin/env

$which zsh
/usr/local/bin/zsh

Como posso configurar o shebang para que a execução do script como /path/to/script.shsempre use o Zshdisponível no PATH?


8
Você tem certeza de que envnão está em / bin e / usr / bin? Tente which -a envconfirmar.
grawity

Respostas:


22

Você não pode resolver isso diretamente através do shebang, pois o shebang é puramente estático. O que você pode fazer é ter um "multiplicador menos comum" (da perspectiva do shell) no shebang e executar novamente seu script com o shell correto, se esse LCM não for zsh. Em outras palavras: já seu script executado por uma concha encontrada em todos os sistemas, teste para um zshrecurso -apenas e se as voltas de teste fora falsa, tem o script execcom zsh, onde o teste será bem-sucedida e você só continuar.

Um recurso exclusivo zsh, por exemplo, é a presença da $ZSH_VERSIONvariável:

#!/bin/sh -

[ -z "$ZSH_VERSION" ] && exec zsh - "$0" ${1+"$@"}

# zsh-specific stuff following here
echo "$ZSH_VERSION"

Neste caso simples, o script é executado pela primeira vez /bin/sh(todos os sistemas do tipo Unix pós-década de 80 compreendem #!e possuem um /bin/shBourne ou POSIX, mas nossa sintaxe é compatível com ambos). Se não$ZSH_VERSION estiver definido, o próprio script será concluído . Se estiver definido (resp. O script já foi executado ), o teste é simplesmente ignorado. Voilà.execzsh$ZSH_VERSIONzsh

Isso só falha se zshnão estiver presente $PATH.

Edit: Para certificar-se, só execum zshnos locais habituais, você poderia usar algo como

for sh in /bin/zsh \
          /usr/bin/zsh \
          /usr/local/bin/zsh; do
    [ -x "$sh" ] && exec "$sh" - "$0" ${1+"$@"}
done

Isso pode impedir você de acidentalmente execalgo no seu $PATHque não é o que zshvocê está esperando.


I upvoted isso por elegância, mas ele faz, em princípio, têm problemas de segurança / compatibilidade, se o primeiro zshna $PATHnão é o que você espera.
Re

Tentei resolver isso. A questão é se você sempre pode ter certeza se um zshbinário nos locais padrão é realmente um zsh.
Andreas Wiese

Você pode localizar dinamicamente a linha! Bang. Você também pode se perguntar zshonde está zsh -c 'whence zsh'. Mais simplesmente, você pode apenas command -v zsh. Veja minha resposta para saber como localizar dinamicamente o arquivo #!bang.
mikeserv

1
Chamar o zshbinário de $PATHpara obter o caminho do zshbinário não resolveria completamente o problema apontado por @RyanReich, seria? :)
Andreas Wiese

Não se você se executar zsh, não, acho que não. Mas se você incorporar a sequência resultante em seu hash bang e depois executar seu próprio script, pelo menos saberá o que está recebendo. Ainda assim, seria um teste mais simples do que fazer um loop.
precisa saber é o seguinte

7

Durante anos, usei algo semelhante para lidar com os vários locais do Bash em sistemas que eu precisava que meus scripts fossem executados.

Bash / Zsh / etc.

#!/bin/sh

# Determines which OS and then reruns this script with approp. shell interp.
LIN_BASH="/bin/sh";
SOL_BASH="/packages/utilities/bin/sun5/bash";

OS_TYPE=`uname -s`;

if [ $OS_TYPE = "SunOS" ]; then
  $SOL_BASH -c "`sed -n '/\#\#\# BEGIN/,$p' $0`" $0 $*;
elif [ $OS_TYPE = "Linux" ]; then
  $LIN_BASH -c "`sed -n '/\#\#\# BEGIN/,$p' $0`" $0 $*;
else
  echo "UNKNOWN OS_TYPE, $OS_TYPE";
  exit 1;
fi
exit 0;

### BEGIN

...script goes here...

O acima pode ser facilmente adaptado para uma variedade de intérpretes. O ponto principal é que esse script seja executado inicialmente como shell Bourne. Em seguida, recursivamente se chama uma segunda vez, mas analisa tudo acima do comentário ### BEGINusando sed.

Perl

Aqui está um truque semelhante para o Perl:

#!/bin/sh

LIN_PERL="/usr/bin/perl";
SOL_PERL="/packages/perl/bin/perl";

OS_TYPE=`uname -s`;

if [ $OS_TYPE = "SunOS" ]; then
  eval 'exec $SOL_PERL -x -S $0 ${1+"$@"}';
elif [ $OS_TYPE = "Linux" ]; then
  eval 'exec $LIN_PERL -x -S $0 ${1+"$@"}';
else
  echo "$OS_TYPE: UNSUPORRTED OS/PLATFORM";
  exit 0;
fi
exit 0;

#!perl

...perl script goes here...

Este método utiliza a capacidade do Perl quando um arquivo é executado para analisar o arquivo pulando todas as linhas anteriores à linha #! perl.


Uma série de problemas lá: citações desaparecidas, uso de $*, em vez de "$@", o uso inútil de eval, status de saída não avisados (você não usou execpara o primeiro), faltando -/ --, mensagens de erro e não em stderr, 0 status de saída para condições de erro , usando / bin / sh para LIN_BASH, ponto e vírgula inútil (cosmético), usando todas as letras maiúsculas para variáveis ​​não-env. uname -sé como uname(uname é para o nome Unix). Você esqueceu de mencionar que o skipping é desencadeada pela -xopção de perl.
Stéphane Chazelas

4

NOTA: @ jw013 faz a seguinte objeção sem suporte nos comentários abaixo:

O voto negativo é porque o código de modificação automática é geralmente considerado uma prática ruim. Nos velhos tempos de pequenos programas de montagem, era uma maneira inteligente de reduzir filiais condicionais e melhorar o desempenho, mas hoje em dia os riscos de segurança superam as vantagens. Sua abordagem não funcionaria se o usuário que executou o script não tivesse privilégios de gravação no script.

Eu respondi suas objeções de segurança, salientando que quaisquer permissões especiais são exigido somente uma vez por instalação / atualização de ação, a fim de instalar / atualizar o auto-instalação roteiro - que eu pessoalmente muito chamada segura. Eu também o apontei para uma man shreferência para alcançar objetivos semelhantes por meios semelhantes. Na época, eu não me incomodei em apontar que quaisquer falhas de segurança ou práticas geralmente desaconselhadas que possam ou não ser representadas na minha resposta, elas provavelmente estão mais enraizadas na própria pergunta do que na minha resposta:

Como posso configurar o shebang para que a execução do script como /path/to/script.sh sempre use o Zsh disponível no PATH?

Não satisfeito, @ jw013 continuou a objetar, promovendo seu argumento ainda não suportado com pelo menos algumas declarações erradas:

Você usa um único arquivo, não dois. O pacote [ man shreferenciado] possui um arquivo, modifica outro arquivo. Você tem um arquivo se modificando. Há uma diferença distinta entre esses dois casos. Um arquivo que recebe entrada e produz saída é bom. Um arquivo executável que muda automaticamente enquanto é executado geralmente é uma má ideia. O exemplo que você apontou não faz isso.

Em primeiro lugar:

A ÚNICA EXECUTABLE código em qualquer EXECUTABLE Shell Script é o #!SE

(embora mesmo não #!seja oficialmente especificado )

{   cat >|./file 
    chmod +x ./file 
    ./file
} <<-\FILE
    #!/usr/bin/sh
    {   ${l=lsof -p} $$
        echo "$l \$$" | sh
    } | grep \
        "COMMAND\|^..*sh\| [0-9]*[wru] "
#END
FILE

##OUTPUT

COMMAND  PID     USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
file    8900 mikeserv  txt    REG   0,33   774976  2148676 /usr/bin/bash
file    8900 mikeserv  mem    REG   0,30           2148676 /usr/bin/bash (path dev=0,33)
file    8900 mikeserv    0r   REG   0,35      108 15496912 /tmp/zshUTTARQ (deleted)
file    8900 mikeserv    1u   CHR  136,2      0t0        5 /dev/pts/2
file    8900 mikeserv    2u   CHR  136,2      0t0        5 /dev/pts/2
file    8900 mikeserv  255r   REG   0,33      108  2134129 /home/mikeserv/file
COMMAND  PID     USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
sh      8906 mikeserv  txt    REG   0,33   774976  2148676 /usr/bin/bash
sh      8906 mikeserv  mem    REG   0,30           2148676 /usr/bin/bash (path dev=0,33)
sh      8906 mikeserv    0r  FIFO    0,8      0t0 15500515 pipe
sh      8906 mikeserv    1w  FIFO    0,8      0t0 15500514 pipe
sh      8906 mikeserv    2u   CHR  136,2      0t0        5 /dev/pts/2

{    sed -i \
         '1c#!/home/mikeserv/file' ./file 
     ./file 
     sh -c './file ; echo'
     grep '#!' ./file
}

##OUTPUT
zsh: too many levels of symbolic links: ./file
sh: ./file: /home/mikeserv/file: bad interpreter: Too many levels of symbolic links

#!/home/mikeserv/file

Um script de shell é apenas um arquivo de texto - para que ele tenha algum efeito, ele deve ser lido por outro arquivo executável, suas instruções então interpretadas por esse outro arquivo executável, antes de finalmente o outro arquivo executável executar sua interpretação do script de shell. Não é possível para a execução de um arquivo de script de shell envolver menos de dois arquivos. Existe uma possível exceção no zshpróprio compilador, mas com isso eu tenho pouca experiência e não é de forma alguma representada aqui.

O hashbang de um script de shell deve apontar para o intérprete pretendido ou ser descartado como irrelevante.

O COMPORTAMENTO DE RECONHECIMENTO / EXECUÇÃO DE SHELL DO SHELL É DEFINIDO PADRÃO

O shell possui dois modos básicos de analisar e interpretar sua entrada: ou sua entrada atual está definindo um <<here_documentou está definindo um { ( command |&&|| list ) ; } &- em outras palavras, o shell interpreta um token como um delimitador para um comando que ele deve executar depois de lê-lo em ou como instruções para criar um arquivo e mapeá-lo para um descritor de arquivo para outro comando. É isso aí.

Ao interpretar comandos para executar, o shell delimita os tokens em um conjunto de palavras reservadas. Quando o shell encontra uma abertura simbólica deve continuar a ler em uma lista de comandos até que a lista é ou delimitado por um fechamento simbólico como uma nova linha - quando aplicável - ou o fechamento token de como })para ({antes da execução.

O shell distingue entre um comando simples e um comando composto. O comando composto é o conjunto de comandos que devem ser lidos antes da execução, mas o shell não executa $expansionnenhum dos seus comandos simples constituintes até que ele execute individualmente cada um.

Portanto, no exemplo a seguir, as ;semicolon palavras reservadas delimitam comandos simples individuais, enquanto o \newlinecaractere não escapado delimita entre os dois comandos compostos:

{   cat >|./file
    chmod +x ./file
    ./file
} <<-\FILE
        #!/usr/bin/sh
        echo "simple command ${sc=1}" ;\
                : > $0 ;\
                echo "simple command $((sc+2))" ;\
                sh -c "./file && echo hooray"
        sh -c "./file && echo hooray"
#END
FILE

##OUTPUT

simple command 1
simple command 3
hooray

Essa é uma simplificação das diretrizes. Fica muito mais complicado quando você considera shell-builtins, subshells, ambiente atual e etc, mas, para meus propósitos aqui, é suficiente.

E por falar em built-ins e listas de comandos, a function() { declaration ; }é apenas um meio de atribuir um comando composto a um comando simples. O shell não deve executar nenhuma $expansionsdeclaração na declaração em si - para incluir <<redirections>-, mas deve armazenar a definição como uma única cadeia literal e executá-la como um shell especial embutido quando solicitado.

Portanto, uma função de shell declarada em um script de shell executável é armazenada na memória do shell de interpretação em sua forma de string literal - não expandida para incluir documentos aqui anexados como entrada - e executada independentemente do seu arquivo de origem toda vez que é chamada de shell. enquanto o ambiente atual do shell durar.

A <<HERE-DOCUMENTÉ UM ARQUIVO EM LINHA

Os operadores de redirecionamento <<e os <<-dois permitem o redirecionamento de linhas contidas em um arquivo de entrada do shell, conhecido como documento aqui, para a entrada de um comando.

O documento aqui deve ser tratado como uma única palavra que começa após a próxima \newlinee continua até que exista uma linha contendo apenas o delimitador e a \newline, sem [:blank:]s no meio. Então, o próximo documento aqui começa, se houver um. O formato é o seguinte:

[n]<<word
    here-document 
delimiter

... em que o opcional nrepresenta o número do descritor de arquivo. Se o número for omitido, o documento aqui se refere à entrada padrão (descritor de arquivo 0).

for shell in dash zsh bash sh ; do sudo $shell -c '
        {   readlink /proc/self/fd/3
            cat <&3
        } 3<<-FILE
            $0

        FILE
' ; done

#OUTPUT

pipe:[16582351]
dash

/tmp/zshqs0lKX (deleted)
zsh

/tmp/sh-thd-955082504 (deleted)
bash

/tmp/sh-thd-955082612 (deleted)
sh

Entende? Para cada shell acima do shell, cria um arquivo e o mapeia para um descritor de arquivo. No zsh, (ba)shshell, cria um arquivo regular /tmp, despeja a saída, mapeia-o para um descritor e exclui o /tmparquivo para que a cópia do descritor do kernel seja o que resta. dashevita todo esse absurdo e simplesmente coloca seu processamento de saída em um |pipearquivo anônimo destinado ao <<destino de redirecionamento .

Isso faz com que dash:

cmd <<HEREDOC
    $(cmd)
HEREDOC

funcionalmente equivalente a bash's:

cmd <(cmd)

enquanto dasha implementação é pelo menos POSIXly portátil.

QUE FAZ DIVERSOS ARQUIVOS

Então, na resposta abaixo quando eu faço:

{    cat >|./file
     chmod +x ./file
     ./file
} <<\FILE
#!/usr/bin/sh
_fn() { printf '#!' ; command -v zsh ; cat 
} <<SCRIPT >$0
    [SCRIPT BODY]
SCRIPT    

_fn ; exec $0
FILE

O seguinte acontece:

  1. A primeira vez que cato conteúdo de qualquer arquivo do shell criado para FILEdentro ./file, torná-lo executável, em seguida, executá-lo.

  2. O kernel interpreta #!e chama /usr/bin/shcom um <read descritor de arquivo atribuído ./file.

  3. shmapeia uma string na memória que consiste no comando composto começando em _fn()e terminando em SCRIPT.

  4. Quando _fné chamado, shdeve primeiro interpretar, em seguida, mapear para um descritor de arquivo definido no <<SCRIPT...SCRIPT antes invocando _fncomo um especial built-in utilitário porque SCRIPTé _fn's<input.

  5. A saída de cordas por printfe commandestão escritas para _fn's padrão-out >&1 - que é redirecionada para o atual shell de ARGV0- ou $0.

  6. catconcatena seu descritor de arquivo de <&0 entrada padrão - SCRIPT- sobre o argumento >do shell atual truncado ARGV0, ou $0.

  7. A conclusão do comando composto atual já lido , sh execé o $0argumento executável - e recentemente reescrito - .

Desde o momento em que ./fileé chamado até que suas instruções contidas especifiquem que ele deve ser execd novamente, o shlê em um único comando composto de cada vez enquanto os executa, enquanto ./fileele próprio não faz nada, exceto aceitar alegremente seu novo conteúdo. Os arquivos que estão realmente no trabalho são/usr/bin/sh, /usr/bin/cat, /tmp/sh-something-or-another.

OBRIGADO, DEPOIS DE TUDO

Então, quando @ jw013 especifica que:

Um arquivo que recebe entrada e produz saída é bom ...

... entre suas críticas errôneas a essa resposta, ele está, na verdade, sem querer, desculpando o único método usado aqui, que basicamente funciona apenas para:

cat <new_file >old_file

RESPONDA

Todas as respostas aqui são boas, mas nenhuma delas está totalmente correta. Todo mundo parece reivindicar que você não pode seguir seu caminho de forma dinâmica e permanente #!bang. Aqui está uma demonstração de como configurar um caminho independente de caminho:

DEMO

{   cat >|./file
    chmod +x ./file
    ./file
} <<\FILE 
#!/usr/bin/sh
_rewrite_me() { printf '#!' ; command -v zsh
        ${out+cat} ; ${out+:} . /dev/fd/0 >&2
} <<\SCRIPT >|${out-/dev/null}
        printf "
        \$0    :\t$0
        lines :\t$((c=$(wc -l <$0)))
        !bang :\t$(sed 1q "$0")
        shell :\t"$(printf `ps -o args= -p $$`)\\n\\n
        sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
                sed -e 'N;s/\n/ >\t/' -e 4a\\...
SCRIPT
_rewrite_me ; out=$0 _rewrite_me ; exec $0
FILE

SAÍDA

        $0    : ./file
        lines : 13
        !bang : #!/usr/bin/sh
        shell : /usr/bin/sh

1 >     #!/usr/bin/sh
2 >     _rewrite_me() { printf '#!' ; command -v zsh
...
12 >    SCRIPT
13 >    _rewrite_me ; out=$0 _rewrite_me ; exec $0

        $0    : /home/mikeserv/file
        lines : 8
        !bang : #!/usr/bin/zsh
        shell : /usr/bin/zsh

1 >     #!/usr/bin/zsh
2 >             printf "
...
7 >             sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
8 >                     sed -e 'N;s/\n/ >\t/' -e 4a\\...

Entende? Acabamos de fazer o script se sobrescrever. E isso só acontece uma vez após uma gitsincronização. A partir desse ponto, ele tem o caminho certo na linha #! Bang.

Agora, quase tudo isso lá em cima é apenas fofo. Para fazer isso com segurança, você precisa:

  1. Uma função definida na parte superior e chamada na parte inferior que faz a escrita. Dessa forma, armazenamos tudo o que precisamos na memória e garantimos que todo o arquivo seja lido antes de começarmos a escrevê-lo.

  2. Alguma maneira de determinar qual deve ser o caminho. command -vé muito bom para isso.

  3. Heredocs realmente ajudam porque são arquivos reais. Enquanto isso, eles armazenam seu script. Você pode usar cordas também, mas ...

  4. Você precisa garantir que o shell leia o comando que sobrescreve seu script na mesma lista de comandos que a executada.

Veja:

{   cat >|./file
    chmod +x ./file
    ./file
} <<\FILE 
#!/usr/bin/sh
_rewrite_me() { printf '#!' ; command -v zsh
        ${out+cat} ; ${out+:} . /dev/fd/0 >&2
} <<\SCRIPT >|${out-/dev/null}
        printf "
        \$0    :\t$0
        lines :\t$((c=$(wc -l <$0)))
        !bang :\t$(sed 1q "$0")
        shell :\t"$(printf `ps -o args= -p $$`)\\n\\n
        sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
                sed -e 'N;s/\n/ >\t/' -e 4a\\...
SCRIPT
_rewrite_me ; out=$0 _rewrite_me
exec $0
FILE

Observe que eu mudei o execcomando apenas uma linha. Agora:

#OUTPUT
        $0    : ./file
        lines : 14
        !bang : #!/usr/bin/sh
        shell : /usr/bin/sh

1 >     #!/usr/bin/sh
2 >     _rewrite_me() { printf '#!' ; command -v zsh
...
13 >    _rewrite_me ; out=$0 _rewrite_me
14 >    exec $0

Não recebo a segunda metade da saída porque o script não pode ler no próximo comando. Ainda assim, porque o único comando que faltava era o último:

cat ./file

#!/usr/bin/zsh
        printf "
        \$0    :\t$0
        lines :\t$((c=$(wc -l <$0)))
        !bang :\t$(sed 1q "$0")
        shell :\t"$(printf `ps -o args= -p $$`)\\n\\n
        sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
                sed -e 'N;s/\n/ >\t/' -e 4a\\...

O script surgiu como deveria - principalmente porque estava no heredoc - mas se você não planejar corretamente, poderá interromper o fluxo de arquivos, o que aconteceu comigo acima.


O voto negativo é porque o código de modificação automática é geralmente considerado uma prática ruim. Nos velhos tempos de pequenos programas de montagem, era uma maneira inteligente de reduzir filiais condicionais e melhorar o desempenho, mas hoje em dia os riscos de segurança superam as vantagens. Sua abordagem não funcionaria se o usuário que executou o script não tivesse privilégios de gravação no script.
jw013

@ jw013 Obviamente, minha abordagem para instalar ou atualizar um script executável não funcionaria se a pessoa que tentasse instalar ou atualizar o script não tivesse permissões para instalar ou atualizar o script. de fato, é exatamente isso que torna essa resposta melhor do que qualquer outra resposta aqui - ela pode fornecer uma linha #! bang precisa conforme necessário e só precisa de permissões especiais para fazê-lo na primeira chamada - durante a instalação. E, novamente, não vou simplesmente aceitar sua palavra de que o código auto-modificado é uma prática ruim - por favor, veja man commanduma opinião contraditória.
mikeserv

consulte man commanduma opinião contraditória - não a encontre. Você pode me direcionar para a seção / parágrafo específico de que estava falando?
Jw013

@ jw013 - meu erro, está em man sh- procure por 'command -v'. Eu sabia que estava em uma das manpáginas que eu estava olhando no outro dia.
mikeserv

Presumo que este seja o command -vexemplo do qual você estava falando man sh. Esse é um script de instalação normal e não modificado automaticamente . Até os instaladores independentes contêm apenas entradas de pré-modificação e as produzem em outro lugar. Eles não se reescrevem da maneira que você está recomendando.
Jw013

1

Aqui está uma maneira de ter um script auto-modificado que corrige seu shebang. Este código deve ser anexado ao seu script real.

#!/bin/sh
# unpatched

PATH=`PATH=/bin:/usr/bin:$PATH getconf PATH`
if [ "`awk 'NR==2 {print $2;exit;}' $0`" = unpatched ]; then
  [ -z "`PATH=\`getconf PATH\`:/usr/local/bin:/some/long/path/to/the/right:$PATH command -v zsh`" ] && { echo "zsh not found"; exit 1; }
  cp -- "$0" "$0.org" || exit 1
  mv -- "$0" "$0.old" || exit 1
  (
    echo "#!`PATH=\`getconf PATH\`:$PATH command -v zsh`" 
    sed -n '/^##/,$p' $0.old
  ) > $0 || exit
  chmod +x $0
  rm $0.old
  sync
  exit
fi
## Original script starts here

Alguns comentários:

  • Deve ser executado uma vez por alguém que tenha o direito de criar e excluir arquivos no diretório em que o script está localizado.

  • Ele usa apenas a sintaxe herdada do shell bourne, pois, apesar da crença popular, /bin/shnão é garantido que seja um shell POSIX, mesmo em sistemas operacionais compatíveis com POSIX.

  • Ele configurou o PATH para um compatível com POSIX, seguido por uma lista de possíveis locais zsh para evitar escolher um zsh "falso".

  • Se, por algum motivo, um script auto-modificável não for bem-vindo, seria trivial distribuir dois scripts em vez de um, o primeiro sendo o que você deseja corrigir e o segundo, o que sugeri que foi modificado levemente para processar o primeiro.


O /bin/shponto é bom - mas nesse caso você precisa de um pré #!- modificado ? E não é awktão provável que seja falso como zshé?
mikeserv

@mikeserv Resposta atualizada para chamar o POSIX awk. O shebang pré-modificado existe para impedir que o script seja interpretado por um shell não compatível com o Bourne, caso seja o seu shell de login.
Jlliagre 5/05

Faz sentido. Eu o votei porque ele funciona, segue o livro e demonstra uma boa compreensão do possível ambiente de shell / manipulação de arquivos - especialmente os arquivos de backup que você usa, que é o que todos os GNUs sed -ifazem de qualquer maneira. Pessoalmente, acho que o $PATHproblema observado nos comentários de outra resposta e que você aborda com a maior segurança possível, em algumas linhas, é melhor tratado com definições simples e explícitas de dependências e / ou testes rigorosos e explícitos - por exemplo, agora getconfpode ser falso, mas as chances são perto de zero, mesmo que eles foram para zsheawk.
mikeserv

@mikeserv, script modificado para reduzir o risco de chamar um getconf falso.
jlliagre

$(getconf PATH)não é Bourne. cp $0 $0.oldé a sintaxe zsh. O equivalente Bourne seria cp "$0" "$0.old"que você gostariacp -- "$0" "$0.old"
Stéphane Chazelas
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.