Shebang'da çoklu argümanlar


32

Shebang satırı ( #!) üzerinden birden çok seçeneği çalıştırılabilir bir dosyaya geçirmenin genel bir yolu olup olmadığını merak ediyorum .

NixOS kullanıyorum ve yazdığım herhangi bir senaryoda shebang'ın ilk kısmı genellikle /usr/bin/env. O zaman karşılaştığım sorun, sonra gelen her şeyin sistem tarafından tek bir dosya veya dizin olarak yorumlanması.

Örneğin, bashposix modunda yürütülecek bir komut dosyası yazmak istediğimi varsayalım . Shebang'ı yazmanın saf yolu şudur:

#!/usr/bin/env bash --posix

ancak sonuçtaki betiği çalıştırmaya çalışmak şu hatayı üretir:

/usr/bin/env: ‘bash --posix’: No such file or directory

Ben farkındayım bu yazı ama daha genel ve daha temiz bir çözüm olup olmadığını merak ediyorum.


EDIT : Guile senaryoları için , kılavuzun 4.3.4 . Bölümünde belgelenen istediğimi elde etmenin bir yolu olduğunu biliyorum :

 #!/usr/bin/env sh
 exec guile -l fact -e '(@ (fac) main)' -s "$0" "$@"
 !#

Buradaki hile, ikinci satırın (baştan itibaren exec) kod olarak yorumlanması shama #!... !#bloğunda olmanın, yorum olarak ve dolayısıyla Guile yorumlayıcısının ihmal etmesidir.

Bu metodu herhangi bir tercümana genelleştirmek mümkün olmaz mıydı?


İkinci EDIT : Biraz oynadıktan sonra, girdilerini okuyabilen tercümanlar stdiniçin aşağıdaki yöntem işe yarayacak gibi görünüyor:

#!/usr/bin/env sh
sed '1,2d' "$0" | bash --verbose --posix /dev/stdin; exit;

shTercüman işini bitirene kadar süreç sürdüğü için muhtemelen en uygun değil . Herhangi bir geri bildirim veya öneri takdir edilecektir.



Yanıtlar:


27

Genel bir çözüm yoktur, en azından Linux'u desteklemeniz gerekmiyorsa, çünkü Linux çekirdeği shebang hattındaki ilk “sözcüğü” izleyen her şeyi tek bir argüman olarak ele alır .

NixOS'ın kısıtlamalarının ne olduğundan emin değilim, ama tipik olarak sadece sizin dilinizi yazın

#!/bin/bash --posix

veya, mümkünse, kodda seçenekleri ayarlayın :

set -o posix

Alternatif olarak, komut dosyasının uygun kabuk çalıştırmasıyla kendisini yeniden başlatmasını sağlayabilirsiniz:

#!/bin/sh -

if [ "$1" != "--really" ]; then exec bash --posix -- "$0" --really "$@"; fi

shift

# Processing continues

İlk birkaç satırın (kabuk tarafından yorumlanan) hedef dil tarafından göz ardı edilmesinin bir yolunu bulduğunuz sürece, bu yaklaşım diğer dillere genelleştirilebilir.

GNU coreutils' envbkz sürümü 8.30 yana bir geçici çözüm sağlanır unode s’ cevabını detayları için. (Bu Debian 10 ve sonrasında, RHEL 8 ve sonrasında, Ubuntu 19.04 ve sonrasında, vb. Mevcuttur.)


18

Tamamen taşınabilir olmamasına rağmen, 8.30 ve coreutils ile başlayarak , belgelerine göre kullanabileceksiniz:

#!/usr/bin/env -S command arg1 arg2 ...

Yani verilen:

$ cat test.sh
#!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too

Alacaksın:

% ./test.sh 
$0 is '/usr/local/bin/showargs'
$1 is 'here'
$2 is 'is another'
$3 is 'long'
$4 is 'arg'
$5 is '-e'
$6 is 'this and that '
$7 is 'too'
$8 is './test.sh'

ve merak ediyorsanız showargs:

#!/usr/bin/env sh
echo "\$0 is '$0'"

i=1
for arg in "$@"; do
    echo "\$$i is '$arg'"
    i=$((i+1))
done

Bu gelecekteki referans için bilmek çok iyidir.
John McGehee

Bu seçenek FreeBSD’lerin 2005’te eklendikleri envyerden kopyalandı -S. Bkz. Lists.gnu.org/r/coreutils/2018-04/msg00011.html
Stéphane Chazelas

Fedora 29
Eric

Bazı iyileştirmeler @unode showargs: pastebin.com/q9m6xr8H ve pastebin.com/gS8AQ5WA (one-liner)
Eric

Bilginize: Çekirdek 8.31'den itibaren, envkendi içeriğini içerir showargs: -v seçeneği, örneğin#!/usr/bin/env -vS --option1 --option2 ...
chocolateboy,

9

POSIX standardı, aşağıdakileri açıklamaya oldukça açıktır #!:

Gönderen ait belgelerin mantığı bölümünde exec()sistem arayüzleri ailesi :

Bazı tarihsel uygulamaların kabuk komut dosyalarını işlemesinin başka bir yolu, dosyanın ilk iki baytını karakter dizesi olarak tanımak ve dosyanın #!ilk satırının kalanını çalıştırılacak komut yorumlayıcısının adı olarak kullanmaktır.

Gönderen Shell Giriş bölümünde :

Kabuk bir dosyaya (bkz onun girişini okur shitibaren) -cseçenek veya gelen system()ve popen()işlevleri POSIX.1-2008 Sistem Arabirimleri hacminde tanımladı. Bir kabuk komut dosyasının ilk satırı karakterlerle başlıyorsa #!, sonuçlar belirtilmez .

Bu, temel olarak, herhangi bir uygulamanın (kullandığınız Unix), shebang hattının ayrıştırma özelliklerini, istediği gibi yapma konusunda özgür olduğu anlamına gelir.

MacOS gibi bazı Unices (ATM'yi test edemez), tercüman için tercümana verilen argümanları bağımsız argümanlara böler, Linux ve diğer birçok Unices argümanları tercümana tek seçenek olarak verir.

Bu nedenle, Shebang hattına, tek bir argümandan fazlasını alabilmeye güvenmek akıllıca değildir.

Ayrıca, Wikipedia'daki Shebang makalesinin Taşınabilirlik bölümüne de bakın .


Herhangi bir yardımcı programa veya dile genelleştirilebilir olan kolay bir çözüm, gerçek komut dosyasını uygun komut satırı argümanlarıyla yürüten bir sarmalayıcı komut dosyası oluşturmaktır:

#!/bin/sh
exec /bin/bash --posix /some/path/realscript "$@"

Ben şahsen o yeniden yürütmek yapmaya sanmıyorum kendisini o biraz kırılgan hissediyor olarak.


7

Shebang execve(2) man sayfasında şöyle açıklanmaktadır :

#! interpreter [optional-arg]

Bu sözdiziminde iki alan kabul edilir:

  1. Tercüman yolundan bir boşluk önce , ancak bu boşluk isteğe bağlıdır.
  2. Tercüman yolunu ve isteğe bağlı argümanını ayıran bir boşluk .

İsteğe bağlı bir argümandan bahsederken çoğul kullanmadığımı [optional-arg ...], en fazla bir argüman sağlayabileceğiniz için yukarıdaki sözdizimini de kullanmadığına dikkat edin .

Kabuk komut dosyası kodlama söz konusu olduğunda, setkomut satırının başlangıcına yakın bir yerde tercüman parametrelerini ayarlamaya izin verecek şekilde komut komutunun argümanlarını kullandığınız aynı komutu kullanarak yerleşik komutu kullanabilirsiniz .

Senin durumunda:

set -o posix

Bash komut isteminde, help setkullanılabilir tüm seçenekleri almak için çıktısını kontrol edin .


1
İkiden fazla alana sahip olmanıza izin verilir, bunlar sadece isteğe bağlı argümanın bir parçası olarak kabul edilir.
Stephen Kitt

@StephenKitt: Aslında, buradaki beyaz boşluk, asıl boşluk karakterinden daha kategori olarak alınmalıdır. Sekmeler gibi diğer beyaz alanların da yaygın olarak kabul edilmesi gerektiğini düşünüyorum.
WhiteWinterWolf

3

Linux'ta, shebang çok esnek değildir; Birden fazla cevaba göre ( Stephen Kitt'in cevabı ve Jörg W Mittag'ın ), bir Shebang hattında birden fazla argüman iletmenin belirlenmiş bir yolu yoktur.

Kimseye faydası olacak mı emin değilim, ama eksik özelliğini uygulamak için kısa bir senaryo yazdım. Bkz. Https://gist.github.com/loxaxs/7cbe84aed1c38cf18f70d8427bed1efa .

Gömülü geçici çözümler yazmak da mümkündür. Bu arada , aynı test betiğine uygulanan dört dilde-agnostik geçici çözüm sunuyorum ve sonuçların her biri yazdırılıyor. Komut dosyasının çalıştırılabilir olduğunu ve içinde olduğunu varsayalım /tmp/shebang.


Senaryoyu proses değiştirme içerisinde bir bash heredocuna sarma

Bildiğim kadarıyla, bunu yapmanın en güvenilir dil-agnostik yolu. Argümanların geçmesine izin verir ve stdin'i korur. Dezavantajı, yorumlayıcının okuduğu dosyanın (gerçek) konumunu bilmemesidir.

#!/bin/bash
exec python3 -O <(cat << 'EOWRAPPER'
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv
try:
    print("input() 0 ::", input())
    print("input() 1 ::", input())
except EOFError:
    print("input() caused EOFError")
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")
EOWRAPPER
) "$@"

Çağrı echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'baskıları:

PYTHON_SCRIPT_BEGINNING
input() 0 :: aa
input() 1 :: bb
argv[0]   :: /dev/fd/62
argv[1:]  :: ['arg1', 'arg2 contains spaces', 'arg3\\ uses\\ \\\\escapes\\\\']
__debug__ :: False
PYTHON_SCRIPT_END

İşlem değişikliğinin özel bir dosya oluşturduğunu unutmayın. Bu tüm çalıştırılabilirlere uygun olmayabilir. Örneğin, #!/usr/bin/lessşikayet eder:/dev/fd/63 is not a regular file (use -f to see it)

Süreç içerisinde heredoksanın çizgi içinde olması mümkün mü bilmiyorum.


Senaryoyu basit bir esrarla sarmala

Daha kısa ve basittir, ancak stdinbetiğinizden erişemezsiniz ve tercümanın bir betiği okuyabilmesi ve çalıştırabilmesi gerekir stdin.

#!/bin/sh
exec python3 - "$@" << 'EOWRAPPER'
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv

try:
    print("input() 0 ::", input())
    print("input() 1 ::", input())
except EOFError:
    print("input() caused EOFError")
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")
EOWRAPPER

Çağrı echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'baskıları:

PYTHON_SCRIPT_BEGINNING
input() caused EOFError
argv[0]   :: -
argv[1:]  :: ['arg1', 'arg2 contains spaces', 'arg3\\ uses\\ \\\\escapes\\\\']
__debug__ :: True
PYTHON_SCRIPT_END

Awk system()çağrısını kullanın, ancak bağımsız değişkenler olmadan

Yürütülen dosyanın adını doğru şekilde iletir, ancak komut dosyanız verdiğiniz bağımsız değişkenleri almaz. Awk'nin, tercümanı varsayılan olarak linux'da yüklü olduğunu bildiğim tek dil olduğunu ve talimatlarını varsayılan olarak komut satırından okuduğunu unutmayın.

#!/usr/bin/gawk BEGIN {system("python3 -O " ARGV[1])}
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv

print("input() 0 ::", input())
print("input() 1 ::", input())
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")

Çağrı echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'baskıları:

PYTHON_SCRIPT_BEGINNING
input() 0 :: aa
input() 1 :: bb
argv[0]   :: /tmp/shebang
argv[1:]  :: []
__debug__ :: False
PYTHON_SCRIPT_END

Bağımsız system()değişkenlerinizi boşluk içermemesi koşuluyla awk 4.1+ çağrısı kullanın

Güzel, ancak yalnızca komut dosyanızın boşluk içeren argümanlarla çağrılmayacağından eminseniz. Gördüğünüz gibi, boşluklar dışında bırakılmadıkça boşluk içeren değişkenleriniz bölünmüş olur.

#!/usr/bin/gawk @include "join"; BEGIN {system("python3 -O " join(ARGV, 1, ARGC, " "))}
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv

print("input() 0 ::", input())
print("input() 1 ::", input())
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")

Çağrı echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'baskıları:

PYTHON_SCRIPT_BEGINNING
input() 0 :: aa
input() 1 :: bb
argv[0]   :: /tmp/shebang
argv[1:]  :: ['arg1', 'arg2', 'contains', 'spaces', 'arg3 uses \\escapes\\']
__debug__ :: False
PYTHON_SCRIPT_END

4.1'in altındaki awk sürümleri için, for döngüsü içinde string birleştirme kullanmanız gerekir, bkz. Örnek fonksiyon https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html .


1
Engellemek $variableveya `command`ikame etmek için buraya belge sonlandırıcısını verin :exec python3 -O <(cat <<'EOWRAPPER'
John McGehee

2

(Shebang) hattında LD_LIBRARY_PATHpython ile kullanılabilecek bir püf noktası #!, kabuktan başka hiçbir şeye bağlı değildir ve bir muamele görür:

#!/bin/sh
'''' 2>/dev/null; exec /usr/bin/env LD_LIBRARY_PATH=. python -x "$0" "$@" #'''

__doc__ = 'A great module docstring'

Bu sayfada başka bir yerde açıklandığı gibi sh, bazı mermiler standart girdilerinde bir komut dosyası alabilir.

Verdiğimiz komut dosyası (boş dize) tarafından basitleştirilmiş bir shkomutu çalıştırmayı dener ve elbette herhangi bir komut olmadığı için yürütülemez , bu nedenle normal olarak standart hata tanımlayıcısında çıkar, ancak bu mesajı kullanarak yeniden yönlendiririz . En yakın kara delik çünkü kullanıcının görüntülemesine izin vermek dağınık ve kafa karıştırıcı olurdu .''''''sh''line 2: command not found2>/dev/nullsh

Daha sonra bize ilgi emriyle devam ediyoruz: execbu, şu anki kabuk sürecini aşağıdaki şekilde değiştirir, bizim durumumuzda: /usr/bin/env pythonyeterli parametrelerle:

  • "$0" python'a hangi betiği açması ve yorumlaması gerektiğini ve sys.argv[0]
  • "$@"python'ları sys.argv[1:]script komut satırında iletilen argümanlara ayarlamak için.

Ayrıca , hack'in tek noktası olan çevre değişkenini de envayarlamayı istiyoruz LD_LIBRARY_PATH.

Kabuk komutu, yorumun sonunda başlar, #böylece kabuk takip eden üçlü tırnak işaretlerini yok sayar '''.

shdaha sonra, ilk argüman olarak verilen python kaynak betiğini açan ve okuyan yeni bir python yorumlayıcısının örneği verilir "$0".

Python dosyayı açar ve -xargüman sayesinde kaynağın 1. satırını atlar . Not: Aynı zamanda çalışır -xçünkü Python için bir shebang sadece bir yorumdur .

Daha sonra Python, 2. satırı geçerli modül dosyası için docstring olarak yorumlar, böylece geçerli bir modül docstring'ine ihtiyacınız varsa __doc__, python programınızdaki ilk şeyi yukarıdaki örnekte olduğu gibi ayarlayın.



Boş bir dize… um… boş olduğu için, maymun işi bulunmayan emrinizi bırakabilmelisiniz: ''''exec ...işi yapmalısınız. Yürütmeden önce boşluk kalmayacağını unutmayın, yoksa boş komutu aramasını sağlar. Boş $0olanı ilk argümana eklemek istersiniz, öyleyse öyle exec.
Caleb

1

Bir betiği tek bir argüman olarak hariç tutan bir çalıştırılabilir dosya ararken oldukça aptalca bir geçici çözüm buldum:

#!/usr/bin/awk BEGIN{system("bash --posix "ARGV[1])}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.