Shebang indipendenti dal percorso


20

Ho uno script che voglio poter eseguire su due macchine. Queste due macchine ottengono copie dello script dallo stesso repository git. Lo script deve essere eseguito con l'interprete giusto (ad es zsh.).

Purtroppo, sia env e zshvivere in luoghi diversi nelle macchine locali e remote:

Macchina remota

$ which env
/bin/env

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

Macchina locale

$ which env
/usr/bin/env

$which zsh
/usr/local/bin/zsh

Come posso impostare lo shebang in modo che l'esecuzione dello script /path/to/script.shusi sempre il Zshdisponibile in PATH?


8
Sei sicuro che envnon sia in / bin e / usr / bin? Prova which -a enva confermare.
Grawity,

Risposte:


22

Non puoi risolverlo direttamente attraverso shebang, poiché shebang è puramente statico. Quello che potresti fare è avere un »moltiplicatore meno comune« (dal punto di vista della shell) nello shebang e rieseguire lo script con la shell giusta, se questo LCM non è zsh. In altre parole: Avere il vostro script eseguito da un guscio trovato su tutti i sistemi, prova per una zshfunzione -Solo e se i giri di prova fuori falso, hanno lo script execcon zsh, in cui il test avrà successo e basta continuare.

Una caratteristica unica in zsh, ad esempio, è la presenza della $ZSH_VERSIONvariabile:

#!/bin/sh -

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

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

In questo semplice caso, lo script viene prima eseguito da /bin/sh(tutti i sistemi di tipo Unix post-80 capiscono #!e hanno un /bin/sh, o Bourne o POSIX ma la nostra sintassi è compatibile con entrambi). Se non$ZSH_VERSION è impostato, lo script è in sé . Se impostato (risp. Lo script è già in esecuzione ), il test viene semplicemente ignorato. Ecco.execzsh$ZSH_VERSIONzsh

Questo fallisce solo se zshnon lo è $PATHaffatto.

Edit: Per essere sicuri, è solo execuna zshnei soliti posti, si potrebbe usare qualcosa di simile

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

Questo potrebbe salvarti dal prendere accidentalmente execqualcosa nel tuo $PATHche non è quello zshche ti aspetti.


Ho valutato questo per eleganza, ma in linea di principio ha problemi di sicurezza / compatibilità, se il primo zshin $PATHnon è quello che ti aspetti.
Ryan Reich,

Ho cercato di affrontarlo. La domanda è se puoi sempre essere sicuro che un zshbinario nelle posizioni standard sia davvero un zsh.
Andreas Wiese,

Puoi tracciare dinamicamente la linea! Bang. Puoi anche chiederti zshdove si trova zsh -c 'whence zsh'. Più semplicemente puoi command -v zsh. Vedi la mia risposta per come seguire dinamicamente il #!bang.
Mikeserv,

1
Chiamare il zshbinario da $PATHper ottenere il percorso dizsh binario non risolve del tutto il problema sottolineato da @RyanReich, vero? :)
Andreas Wiese,

Non se si esegue zsh solo, no, credo di no. Ma se incorpori la stringa risultante nel tuo hash bang e poi esegui il tuo script, almeno sai cosa stai ricevendo. Tuttavia, renderebbe un test più semplice rispetto al looping.
Mikeserv,

7

Per anni ho usato qualcosa di simile per gestire le varie posizioni di Bash sui sistemi di cui avevo bisogno per eseguire i miei script.

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...

Quanto sopra può essere facilmente adattato per una varietà di interpreti. Il pezzo chiave è che questo script inizialmente viene eseguito come shell Bourne. Quindi si chiama ricorsivamente da solo una seconda volta, ma analizza tutto sopra il commento ### BEGINusando sed.

Perl

Ecco un trucco simile per 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...

Questo metodo sfrutta l'abilità di Perl quando viene eseguito un file per analizzare il file saltando tutte le righe che si trovano prima della riga #! perl.


Un certo numero di problemi lì: virgolette mancanti, uso $*invece di "$@", uso inutile di eval, stato di uscita non segnalato (non usato execper il primo), mancante -/ --, messaggi di errore non su stderr, 0 stato di uscita per condizioni di errore , usando / bin / sh per LIN_BASH, punto e virgola inutile (cosmetico), usando tutte le maiuscole per le variabili non env. uname -sè come uname(uname è per il nome Unix). Hai dimenticato di dire che il salto è attivato -xdall'opzione perl.
Stéphane Chazelas,

4

NOTA: @ jw013 formula la seguente obiezione non supportata nei commenti seguenti:

Il downvote è perché il codice auto-modificante è generalmente considerato una cattiva pratica. Ai vecchi tempi di piccoli programmi di assemblaggio era un modo intelligente per ridurre i rami condizionati e migliorare le prestazioni, ma oggigiorno i rischi per la sicurezza superano i vantaggi. Il tuo approccio non funzionerebbe se l'utente che eseguiva lo script non avesse i privilegi di scrittura sullo script.

Ho risposto le sue obiezioni di sicurezza sottolineando che eventuali permessi speciali sono richiesti solo una volta per installare / aggiornare azione al fine di installare / aggiornare l'autoinstallante sceneggiatura - che io personalmente chiamo abbastanza sicuro. Gli ho anche indicato un man shriferimento al raggiungimento di obiettivi simili con mezzi simili. A quel tempo non mi preoccupavo di sottolineare che qualsiasi difetto di sicurezza o altre pratiche generalmente sconsigliate che potevano o non potevano essere rappresentate nella mia risposta, erano più probabilmente radicati nella domanda stessa che nella mia risposta ad essa:

Come posso impostare shebang in modo che l'esecuzione dello script come /path/to/script.sh usi sempre lo Zsh disponibile in PATH?

Non soddisfatto, @ jw013 ha continuato a obiettare promuovendo il suo argomento non ancora supportato con almeno un paio di affermazioni errate:

Si utilizza un singolo file, non due file. Il pacchetto [ man shreferenziato] ha un file per modificare un altro file. Hai un file che si modifica da solo. C'è una netta differenza tra questi due casi. Un file che accetta input e produce output va bene. Un file eseguibile che cambia se stesso mentre viene eseguito è generalmente una cattiva idea. L'esempio che hai indicato non lo fa.

Innanzitutto:

L'UNICO CODICE ESEGUIBILE IN QUALSIASI SCRIPT DI SHELL ESECUTABILE È IL#! STESSO

(anche se #!è ufficialmente non specificato )

{   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

Uno script di shell è solo un file di testo - affinché abbia qualche effetto, deve essere letto da un altro file eseguibile, le sue istruzioni vengono quindi interpretate da quell'altro file eseguibile, prima che l'altro file eseguibile esegua quindi la sua interpretazione del script di shell. Non è possibile che l'esecuzione di un file di script shell coinvolga meno di due file. C'è una possibile eccezione nel zshcompilatore, ma con questo ho poca esperienza e qui non è in alcun modo rappresentato.

L'hashbang di uno script di shell deve indicare l' interprete previsto o essere scartato come irrilevante.

RICONOSCIMENTO / ESECUZIONE DEI TOKEN DI SHELL COMPORTAMENTO E L' È DEFINITO NORME

La shell ha due modalità di base per analizzare e interpretare il suo input: o il suo input corrente sta definendo un <<here_documentoppure sta definendo un { ( command |&&|| list ) ; } &- in altre parole, la shell interpreta un token come delimitatore per un comando che dovrebbe eseguire dopo averlo letto in o come istruzioni per creare un file e mapparlo a un descrittore di file per un altro comando. Questo è tutto.

Quando si interpretano i comandi per eseguire la shell, delimita i token su una serie di parole riservate. Quando la shell incontra un token di apertura, deve continuare a leggere in un elenco di comandi fino a quando l'elenco non viene delimitato da un token di chiusura come una nuova riga - se applicabile - o dal token di chiusura come })per({ prima dell'esecuzione.

La shell distingue tra un comando semplice e un comando composto. Il comando composto è l'insieme di comandi che devono essere letti prima dell'esecuzione, ma la shell non esegue $expansionsu nessuno dei suoi comandi costituenti semplici fino a quando non esegue singolarmente ciascuno di essi.

Quindi, nell'esempio seguente, le ;semicolon parole riservate delimitano i singoli comandi semplici mentre il carattere senza escape \newlinedelimita tra i due comandi composti:

{   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

Questa è una semplificazione degli orientamenti. Diventa molto più complicato se si considerano i built-in della shell, i subshells, l'ambiente attuale e così via, ma, per i miei scopi qui, è abbastanza.

E parlando di built-in e liste di comandi, a function() { declaration ; }è semplicemente un mezzo per assegnare un comando composto a un comando semplice. La shell non deve eseguire alcuna $expansionsistruzione sulla dichiarazione stessa - per includere <<redirections>- ma deve invece memorizzare la definizione come singola stringa letterale ed eseguirla come shell speciale incorporata quando viene chiamata.

Quindi una funzione di shell dichiarata in uno script di shell eseguibile viene memorizzata nella memoria della shell di interpretazione nella sua forma letterale di stringa - non espansa per includere qui i documenti allegati come input - ed eseguita indipendentemente dal suo file sorgente ogni volta che viene chiamata come shell incorporata - fino a quando dura l'attuale ambiente della shell.

A <<HERE-DOCUMENTÈ UN FILE IN LINEA

Gli operatori di reindirizzamento <<ed <<-entrambi consentono il reindirizzamento delle righe contenute in un file di input della shell, noto come documento here, all'input di un comando.

Il documento qui deve essere trattato come una singola parola che inizia dopo la successiva \newlinee continua fino a quando non vi è una riga contenente solo il delimitatore e una \newline, senza [:blank:]s in mezzo. Quindi inizia il prossimo documento qui , se ce n'è uno. Il formato è il seguente:

[n]<<word
    here-document 
delimiter

... dove l'opzione facoltativa nrappresenta il numero del descrittore di file. Se il numero viene omesso, il documento qui si riferisce all'input standard (descrittore di file 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

Vedi? Per ogni shell sopra la shell crea un file e lo mappa su un descrittore di file. Nella zsh, (ba)shshell crea un file normale /tmp, scarica l'output, lo mappa su un descrittore, quindi elimina il /tmpfile in modo che la copia del descrittore del kernel sia tutto ciò che rimane. dashevita tutte queste sciocchezze e semplicemente elimina la sua elaborazione dell'output in un |pipefile anonimo finalizzato al <<target di reindirizzamento .

Questo rende dash:

cmd <<HEREDOC
    $(cmd)
HEREDOC

funzionalmente equivalente a bash's:

cmd <(cmd)

mentre dashl'implementazione è almeno POSIXly portabile.

CHE RENDE MOLTI FILE

Quindi nella risposta qui sotto quando lo faccio:

{    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

Succede quanto segue:

  1. I primi catil contenuto di qualsiasi file di guscio realizzato per FILEin ./file, renderlo eseguibile, quindi eseguirlo.

  2. Il kernel interpreta #!e chiama /usr/bin/shcon un <read descrittore di file assegnato a ./file.

  3. shmappa una stringa in memoria costituita dal comando composto che inizia _fn()e termina in SCRIPT.

  4. Quando _fnviene chiamato, shdeve prima interpretano poi mappare un descrittore di file definito nella <<SCRIPT...SCRIPT prima di invocare _fncome una speciale utilità incorporata perché SCRIPTè _fns'<input.

  5. L'uscita stringhe da printfe commandsono scritti fuori a _fn's standard di-out >&1 - che viene reindirizzato alla corrente shell ARGV0- o $0.

  6. catconcatena il suo descrittore di file di <&0 input standard - SCRIPT- sull'argomento >della shell corrente troncata ARGV0, oppure $0.

  7. Completando il comando composto corrente già letto , sh execè l' $0argomento eseguibile - e appena riscritto - .

Dal momento in cui ./fileviene chiamato fino a quando le sue istruzioni contenute specificano che dovrebbe essere execnuovamente d, lo shlegge in un singolo comando composto alla volta mentre li esegue, mentre ./filese stesso non fa nulla se non accetta felicemente il suo nuovo contenuto. I file che sono effettivamente al lavoro sono/usr/bin/sh, /usr/bin/cat, /tmp/sh-something-or-another.

GRAZIE, DOPO TUTTO

Quindi quando @ jw013 specifica che:

Un file che accetta input e produce output va bene ...

... tra le sue errate critiche a questa risposta, in realtà sta inconsapevolmente perdonando l'unico metodo usato qui, che in pratica risolve semplicemente:

cat <new_file >old_file

RISPOSTA

Tutte le risposte qui sono buone, ma nessuna di esse è completamente corretta. Tutti sembrano affermare che non puoi seguire il tuo percorso in modo dinamico e permanente #!bang. Ecco una dimostrazione di impostazione di un percorso indipendente shebang:

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

PRODUZIONE

        $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\\...

Vedi? Facciamo semplicemente sovrascrivere lo script. E succede sempre e solo una volta dopo una gitsincronizzazione. Da quel momento in poi ha preso la strada giusta nella linea #! Bang.

Adesso quasi tutto lassù c'è solo lanugine. Per farlo in sicurezza devi:

  1. Una funzione definita nella parte superiore e chiamata nella parte inferiore che esegue la scrittura. In questo modo archiviamo tutto ciò di cui abbiamo bisogno in memoria e assicuriamo che l'intero file venga letto prima di iniziare a sovrascriverlo.

  2. Qualche modo per determinare quale dovrebbe essere il percorso. command -vè abbastanza buono per questo.

  3. Heredocs aiuta davvero perché sono file reali. Nel frattempo memorizzeranno la tua sceneggiatura. Puoi usare anche le stringhe ma ...

  4. Devi assicurarti che la shell legga nel comando che sovrascrive il tuo script nella stessa lista di comandi di quello che lo esegue.

Guarda:

{   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

Notare che ho spostato il execcomando solo di una riga. Adesso:

#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

Non ottengo la seconda metà dell'output perché lo script non può leggere nel comando successivo. Tuttavia, poiché l'unico comando mancante era l'ultimo:

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\\...

La sceneggiatura è arrivata come avrebbe dovuto - principalmente perché era tutto in eredità - ma se non la pianifichi correttamente puoi troncare il tuo filestream, che è quello che è successo a me sopra.


Il downvote è perché il codice auto-modificante è generalmente considerato una cattiva pratica. Ai vecchi tempi di piccoli programmi di assemblaggio era un modo intelligente per ridurre i rami condizionati e migliorare le prestazioni, ma oggigiorno i rischi per la sicurezza superano i vantaggi. Il tuo approccio non funzionerebbe se l'utente che eseguiva lo script non avesse i privilegi di scrittura sullo script.
jw013,

@ jw013 Ovviamente il mio approccio all'installazione o all'aggiornamento di uno script eseguibile non funzionerebbe se la persona che tentava di installare o aggiornare lo script non disponesse delle autorizzazioni per installare o aggiornare lo script. in effetti, questo è specificamente ciò che rende questa risposta migliore di ogni altra risposta qui - può fornire una linea #! bang accurata se necessario e necessita solo di autorizzazioni speciali per farlo al primo richiamo - durante l' installazione. E, ancora una volta, non prenderò semplicemente in parola il fatto che il codice di auto-modifica sia una cattiva pratica - per favore, vedi man commandper un'opinione contraddittoria.
Mikeserv,

per favore, vedi man commandper un'opinione contraddittoria - non trovarne una. Mi puoi indirizzare alla specifica sezione / paragrafo di cui stavi parlando?
jw013,

@ jw013 - errore mio, è dentro man sh- cerca 'command -v'. Sapevo che era in una delle manpagine che stavo guardando l'altro giorno.
Mikeserv,

Presumo che questo sia l' command -vesempio di cui stavi parlando man sh. Questo è uno script di installazione dall'aspetto normale e non auto-modificante . Anche i programmi di installazione autonomi contengono solo input di pre-modifica e producono le loro modifiche altrove. Non si riscrivono come si sta raccomandando.
jw013,

1

Ecco un modo per avere uno script auto-modificante che risolva il suo shebang. Questo codice dovrebbe essere anteposto al tuo attuale script.

#!/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

Alcuni commenti:

  • Dovrebbe essere eseguito una volta da qualcuno che ha i diritti per creare ed eliminare i file nella directory in cui si trova lo script.

  • Utilizza solo la sintassi della shell bourne legacy poiché, nonostante la credenza popolare, /bin/shnon è garantito che sia una shell POSIX, anche in sistemi operativi POSIX compatibili.

  • Imposta il PERCORSO su POSIX compatibile seguito da un elenco di possibili posizioni zsh per evitare di scegliere un zsh "falso".

  • Se per qualche ragione uno script auto-modificante non viene accolto, sarebbe banale distribuire due script invece di uno, il primo è quello che si desidera correggere e il secondo, quello che ho suggerito leggermente modificato per elaborare il primo.


Il /bin/shpunto è buono, ma in quel caso hai bisogno di una premodificazione #!? E non è awkaltrettanto probabile che sia falso come lo zshè?
Mikeserv,

@mikeserv Risposta aggiornata per chiamare il awk POSIX. Lo shebang premodificato è lì per impedire che lo script venga interpretato da una shell non bourne compatibile nel caso in cui sia la shell di login.
jlliagre,

Ha senso. L'ho votato perché funziona, si attacca al libro e dimostra una solida comprensione del possibile ambiente shell / gestione dei file - in particolare i file di backup che usi, che comunque fanno tutti i GNU sed -i. Personalmente ritengo che il $PATHproblema riscontrato nei commenti su un'altra risposta e che affronti nel modo più sicuro che posso immaginare qui in poche righe sia gestito meglio definendo in modo semplice ed esplicito dipendenze e / o test rigorosi ed espliciti - ad esempio, ora getconfpotrebbe essere falso, ma le probabilità sono quasi nulle, come lo erano per zsheawk.
mikeserv

@mikeserv, script modificato per ridurre il rischio di chiamare un falso getconf.
jlliagre,

$(getconf PATH)non è Bourne. cp $0 $0.oldè la sintassi zsh. L'equivalente di Bourne sarebbe come cp "$0" "$0.old"vorraicp -- "$0" "$0.old"
Stéphane Chazelas
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.