Care este diferența dintre „#!/usr/bin/env bash” și „#!/usr/bin/bash”? (Programare, Linux, Bash, Shell, Unix, Shebang)

Salah Eddine Taouririt a intrebat.

În antetul unui script Bash, care este diferența dintre aceste două declarații:

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

Când am consultat env pagina de manual, , obțin această definiție:

 env - run a program in a modified environment

Ce înseamnă aceasta?

Comentarii

  • Vedeți această întrebare și răspunsul meu. –  > Por Keith Thompson.
  • Cine poate să-mi spună de ce această întrebare este închisă, iar „legat de programare sau dezvoltare de software” nu este ? –  > Por Salah Eddine Taouririt.
  • 20

  • Această întrebare nu ar fi trebuit să fie marcată ca fiind off-topic. Este nevoie doar ca 5 persoane cu un punctaj de peste 3000 să o marcheze ca „on-topic” și poate fi redeschisă. Este o întrebare – în special despre programare. –  > Por Danijel-James W.
  • Sunt șocat. Șocat să constat că documentația Linux este plină de tautologii. xkcd.com/703 git-man-page-generator.lokaltog.net –  > Por allyourcode.
  • O diferență majoră este că, în funcție de sistem, #!/usr/bin/bash nu va funcționa. Pe sistemul meu (Ubuntu 17.04), bash este /bin/bash, , și nu există /usr/bin/bash. Distincția dintre /bin și /usr/bin este în mare măsură arbitrară. Și, pentru a adăuga frivolitate, majoritatea sistemelor pun env în /usr/bin, , dar acest lucru nu este garantat. –  > Por Keith Thompson.
5 răspunsuri
Alec Bennett

Rularea unei comenzi prin /usr/bin/env are avantajul de a căuta oricare ar fi versiunea implicită a programului în versiunea dvs. curentă de environment.

În acest fel, nu trebuie să o căutați într-un anumit loc din sistem, deoarece aceste căi de acces pot fi în locații diferite pe sisteme diferite. Atâta timp cât se află în calea dvs., îl va găsi.

Un dezavantaj este că nu veți putea trece mai mult de un argument (de exemplu, nu veți putea scrie /usr/bin/env awk -f) dacă doriți să sprijiniți Linux, deoarece POSIX este vag în ceea ce privește modul în care trebuie interpretată linia, iar Linux interpretează tot ceea ce urmează după primul spațiu pentru a indica un singur argument. Puteți utiliza /usr/bin/env -S pe unele versiuni de env pentru a ocoli acest lucru, dar atunci scriptul va deveni și mai puțin portabil și se va întrerupe pe sisteme destul de recente (de exemplu, chiar și pe Ubuntu 16.04, dacă nu mai târziu).

Un alt dezavantaj este că, din moment ce nu apelați un executabil explicit, există potențialul de a face greșeli și, pe sistemele multiuser, probleme de securitate (dacă cineva a reușit să facă ca executabilul său să fie numit bash în calea dumneavoastră, de exemplu).

#!/usr/bin/env bash #lends you some flexibility on different systems
#!/usr/bin/bash     #gives you explicit control on a given system of what executable is called

În unele situații, prima variantă poate fi preferată (cum ar fi rularea scripturilor python cu mai multe versiuni de python, fără a fi nevoie să refaceți linia executabilă). Dar în situațiile în care se pune accentul pe securitate, ar fi de preferat cea de-a doua variantă, deoarece limitează posibilitățile de injectare a codului.

Comentarii

    24

  • Un alt dezavantaj este faptul că nu puteți trece un argument suplimentar către interpretor. –  > Por Keith Thompson.
  • @KeithThompson : Informație incorectă… Puteți trece opțiuni către interpretul de bază folosind /usr/bin/env! –  > Por Gaurav Agarwal.
  • @GauravAgarwal: Nu și pe sistemul meu. Un script care conține doar această singură linie: #!/usr/bin/env echo Hello se plânge: /usr/bin/env: echo Hello: No such file or directory. Aparent, se pare că tratează echo Hello ca un singur argument pentru /usr/bin/env. –  > Por Keith Thompson.
  • @AndréLaszlo: The env permite cu siguranță transmiterea de argumente către comandă. Problema este legată de semantica #! linie, iar aceasta depinde de kernel. Kernelurile Linux recente permit lucruri precum #!/usr/bin/env command args, , dar nucleele Linux mai vechi și alte sisteme nu permit acest lucru. –  > Por Keith Thompson.
  • De ce există ghilimele în blocul de cod? Nu ar trebui să fie eliminate? –  > Por Benjamin W..
Dolphiniac

Utilizarea #!/usr/bin/env NAME face ca shell-ul să caute prima potrivire a lui NAME în variabila de mediu $PATH. Poate fi utilă dacă nu cunoașteți calea absolută sau dacă nu doriți să o căutați.

Comentarii

  • Cel puțin trebuie să știți unde se află env :). –  > Por ᐅdevrimbaris.
  • Un răspuns excelent. Explică succint ceea ce face env shebang, în loc să spună „alege programul în funcție de configurația sistemului” – -.  > Por De Novo.
Mecki

În cazul în care scripturile de shell încep cu #!/bin/bash, , acestea vor rula întotdeauna cu bash de la /bin. Dacă însă încep cu #!/usr/bin/env bash, , acestea vor căuta bash în $PATH și apoi vor începe cu primul pe care îl vor găsi.

De ce ar fi util acest lucru? Să presupunem că doriți să executați bash scripturi, care necesită bash 4.x sau mai nou, dar sistemul dvs. are doar bash 3.x instalat, iar în prezent distribuția dvs. nu oferă o versiune mai nouă sau nu sunteți administrator și nu puteți schimba ceea ce este instalat pe acel sistem.

Bineînțeles, puteți descărca codul sursă bash și să vă construiți propriul bash de la zero, plasându-l la ~/bin de exemplu. Și puteți, de asemenea, să vă modificați $PATH în variabila .bash_profile pentru a include ~/bin ca primă intrare (PATH=$HOME/bin:$PATH ca ~ nu se va extinde în $PATH). Dacă acum apelați bash, shell-ul îl va căuta mai întâi în $PATH în ordine, astfel încât va începe cu ~/bin, , unde va găsi bash. Același lucru se întâmplă dacă scripturile caută bash folosind #!/usr/bin/env bash, , astfel încât aceste scripturi vor funcționa acum pe sistemul dvs. utilizând bash personalizat.

Un dezavantaj este că acest lucru poate duce la un comportament neașteptat, de exemplu, același script pe aceeași mașină poate rula cu interpreți diferiți pentru medii diferite sau utilizatori cu căi de căutare diferite, provocând tot felul de dureri de cap.

Cel mai mare dezavantaj al env este că unele sisteme permit doar un singur argument, astfel încât nu puteți face acest lucru #!/usr/bin/env <interpreter> <arg>, , deoarece sistemele vor vedea <interpreter> <arg> ca pe un singur argument (îl vor trata ca și cum expresia ar fi fost citată) și astfel env va căuta un interpretor numit <interpreter> <arg>. Rețineți că aceasta nu este o problemă a env în sine, care a permis întotdeauna trecerea mai multor parametri, ci cu parserul shebang al sistemului care analizează această linie înainte chiar de a apela env. Între timp, acest lucru a fost rezolvat pe majoritatea sistemelor, dar dacă scriptul dvs. dorește să fie ultraportabil, nu vă puteți baza pe faptul că acest lucru a fost rezolvat pe sistemul pe care îl veți utiliza.

Aceasta poate avea chiar implicații de securitate, de exemplu, dacă sudo nu a fost configurat pentru a curăța mediul sau $PATH a fost exclusă de la curățare. Permiteți-mi să vă demonstrez acest lucru:

De obicei, /bin este un loc bine protejat, doar root este capabil să schimbe ceva acolo. Directorul dvs. personal nu este, însă, orice program pe care îl executați este capabil să facă modificări în el. Asta înseamnă că un cod rău intenționat ar putea plasa un fișier fals bash în vreun director ascuns, să vă modifice .bash_profile pentru a include acel director în directorul dvs. $PATH, astfel încât toate scripturile care utilizează #!/usr/bin/env bash vor sfârși prin a rula cu acel fals bash. Dacă sudo continuă $PATH, , aveți mari probleme.

De exemplu, să considerăm că un instrument creează un fișier ~/.evil/bash cu următorul conținut:

#!/bin/bash

if [ $EUID -eq 0 ]; then
  echo "All your base are belong to us..."
  # We are root - do whatever you want to do
fi

/bin/bash "[email protected]"

Să facem un script simplu sample.sh:

#!/usr/bin/env bash

echo "Hello World"

Dovada de concept (pe un sistem în care sudo păstrează $PATH):

$ ./sample.sh
Hello World

$ sudo ./sample.sh
Hello World

$ export PATH="$HOME/.evil:$PATH"

$ ./sample.sh
Hello World

$ sudo ./sample.sh
All your base are belong to us...
Hello World

De obicei, shell-urile clasice ar trebui să fie toate localizate în /bin și dacă nu doriți să le plasați acolo din orice motiv, nu este o problemă să plasați un link simbolic în /bin care să indice locațiile lor reale (sau poate /bin este el însuși un symlink), așa că aș alege întotdeauna #!/bin/sh și #!/bin/bash. Sunt prea multe lucruri care s-ar strica dacă acestea nu ar mai funcționa. Nu că POSIX ar necesita aceste poziții (POSIX nu standardizează numele de căi și, prin urmare, nici măcar nu standardizează deloc funcția shebang), dar sunt atât de comune, încât chiar dacă un sistem nu ar oferi o poziție /bin/sh, , probabil că ar înțelege totuși #!/bin/sh și ar ști ce să facă cu ea și ar putea fi doar pentru compatibilitate cu codul existent.

Dar pentru interprete mai moderne, non-standard, opționale, cum ar fi Perl, PHP, Python sau Ruby, nu este specificat nicăieri unde ar trebui să fie localizate. Ele pot fi în /usr/bin dar ar putea la fel de bine să fie în /usr/local/bin sau într-o ramură ierarhică complet diferită (/opt/..., , /Applications/..., , etc.). Acesta este motivul pentru care acestea folosesc adesea #!/usr/bin/env xxx sintaxa shebang.

safay

În loc de a defini explicit calea către interpretor, ca în cazul lui /usr/bin/bash/, , prin utilizarea comenzii env, interpretul este căutat și lansat de oriunde este găsit prima dată. Acest lucru are atât avantaje și dezavantaje

Comentarii

  • Pe cele mai multe sisteme, acestea vor fi funcționale la fel, dar depinde de locația executabilelor bash și env. Totuși, nu sunt sigur de modul în care acest lucru va afecta variabilele de mediu. –  > Por safay.
  • „Este posibil să specificați interpretul fără a utiliza env, oferind calea completă către interpretor. O problemă este că, pe diferite sisteme de calculatoare, calea exactă poate fi diferită. Utilizând în schimb env, interpretul este căutat și localizat în momentul în care scriptul este rulat. Acest lucru face ca scriptul să fie mai portabil, dar crește, de asemenea, riscul ca interpretul greșit să fie selectat, deoarece se caută o potrivire în fiecare director de pe calea de căutare a executabilului. De asemenea, suferă de aceeași problemă prin faptul că calea către binarul env poate fi, de asemenea, diferită de la o mașină la alta.”-Wikipedia -.  > Por Mike Clark.
sudo97

Mi se pare util, deoarece atunci când nu știam despre env, înainte de a începe să scriu scripturi, făceam acest lucru:

type nodejs > scriptname.js #or any other environment

și apoi modificam acea linie din fișier în shebang.
Făceam asta, pentru că nu-mi aminteam întotdeauna unde se află nodejs pe calculatorul meu – /usr/bin/ sau /bin/, așa că pentru mine env este foarte util. Poate că există detalii în acest sens, dar acesta este motivul meu