Melewati argumen ke shell yang disediakan

8

man su mengatakan:

You can use the -- argument to separate su options from the arguments
supplied to the shell.

man bash mengatakan:

--        A  --  signals  the  end of options and disables further option
          processing.  Any arguments after the -- are treated as filenames
          and arguments.  An argument of - is equivalent to --.

Baiklah, mari kita lihat:

[root ~] su - yuri -c 'echo "$*"' -- 1 2 3
2 3
[root ~] su - yuri -c 'echo "$*"' -- -- 1 2 3                                                       
2 3
[root ~] su - yuri -c 'echo "$*"' -- - 1 2 3                                                        
1 2 3
[root ~] su - yuri -c 'echo "$*"' - 1 2 3                                                           
1 2 3

Apa yang saya harapkan (output dari perintah kedua berbeda):

[root ~] su - yuri -c 'echo "$*"' -- 1 2 3
2 3
[root ~] su - yuri -c 'echo "$*"' -- -- 1 2 3                                                       
1 2 3
[root ~] su - yuri -c 'echo "$*"' -- - 1 2 3                                                        
1 2 3
[root ~] su - yuri -c 'echo "$*"' - 1 2 3                                                           
1 2 3

Mungkin tidak banyak masalah. Tetapi apa yang terjadi di sana? Varian kedua dan ketiga sepertinya cara untuk pergi, tetapi salah satu dari mereka tidak bekerja. Yang keempat sepertinya tidak -bisa diandalkan, bisa diperlakukan sebagai supilihan.

x-yuri
sumber
Itu aneh. Saya mendapatkan hasil yang Anda harapkan (dan saya juga setuju dengan harapan itu). Maksudku, dalam kasus doa kedua, saya mendapatkan "1 2 3" sebagai output. Saya menggunakan bash 4.2.45akun sumber dan tujuan.
Krzysztof Adamski

Jawaban:

9

Apa yang terjadi adalah bahwa argumen pertama yang Anda berikan ke shell adalah $0parameter, (biasanya ini akan menjadi nama shell). Itu tidak termasuk ketika Anda melakukannya echo $*karena $*setiap argumen terpisah $0.

Contoh:

# su - graeme -c 'echo "\$0 - $0"; echo "\$* - $*"' -- sh 1 2 3
$0 - sh
$* - 1 2 3

Memperbarui

Melakukan perintah berikut:

strace -f su graeme -c 'echo $0; echo "$*"' -- -- 1 2 3

menghasilkan garis strace:

[pid  9609] execve("/bin/bash", ["bash", "-c", "echo $0; echo \"$*\"", "1", "2", "3"], [/* 27 vars */] <unfinished ...>

Jadi entah bagaimana tampaknya dalam hal suini melahap ekstra --tanpa melewatkannya ke bash, mungkin karena bug (atau setidaknya perilaku tidak berdokumen). Namun itu tidak akan memakan lebih dari dua --argumen:

# su graeme -c 'echo $0; echo "$*"' -- -- -- 1 2 3
--
1 2 3
Graeme
sumber
Itu saya mengerti. Saya lakukan su - yuri -c 'echo "$*"' -- -- 1 2 3:, shell seharusnya mendapat -- 1 2 3, tetapi output saja 2 3. Apakah itu masuk akal?
x-yuri
Dan ketika saya melakukannya bash -c 'echo $*' -- 1 2 3, output 1 2 3seperti yang diharapkan.
x-yuri
@ x-yuri, diperbarui. Ini sepertinya sebuah subug.
Graeme
5

Sebenarnya jawaban @ Graeme - dan pertanyaan Anda - hanya referensi efek samping dari bagaimana shell menangani "$@positional $*parameters".ini ditugaskan oleh shell untuk argumen atas doa dan setiap saat kemudian dengan built-in setutilitas. Mereka dapat dipanggil kapan saja dengan "$*"yang membagi setiap posisi dengan karakter pertama "$IFS"atau "$@"yang mengutip setiap posisi dan membaginya dengan semua"$IFS."

man set

    NAME
       set  set or unset options and positional parameters

SYNOPSIS
       set [−abCefhmnuvx] [−o option] [argument...]

       set [+abCefhmnuvx] [+o option] [argument...]

       set −− [argument...]

       set o

       set +o

Jika Anda sudah memiliki nilai yang Anda makan shell, Anda tidak perlu --tiga kali. Parameter Shell setdapat - selalu, kapan saja, tidak hanya saat pemanggilan (kecuali $ 0 dan -i):

su - mikeserv -c 'set -- "$*" ; echo "$*" ; 
    set -- 4 5 6 ; echo "$*"' -- -- 7 8 9

7 8 9
4 5 6

Dan semua kutipan shell bisa membingungkan. Ini menyederhanakan sedikit hal:

( set -- 4 5 6
    su - mikeserv 4<<-\CMD /dev/fd/4 "$@"
    echo $0 "$*"
    set -- "$*"
    echo "$*"
    set -- 7 8 9
    echo "$*"
CMD
)

/dev/fd/4 4 5 6
4 5 6
7 8 9

Argumen shell induk adalah setuntuk 4, 5, dan 6 dan kemudian diteruskan ke subkulit yang dipanggil oleh suvia positionalparameter "$@array".

Perhatikan bagaimana saya ( subshell )perintah di atas - Saya melakukannya karena saya tidak ingin dipusingkan dengan lingkungan shell saya saat ini - karena saya secara tidak sengaja dapat mengubah sesuatu yang saya lebih suka tidak melakukannya jika saya melakukannya denganset.

TENTANG REDIRECTION:

Pertama-tama, sistem Unix Anda berfungsi dengan file - izin file, konten file, atribut file. Dalam satu atau lain cara, setiap objek data yang Anda gunakan dapat (dan, setidaknya menurut saya, harus) ditangani sebagai file. Pengalihan menunjuk ke file - itu saja. A <<HERE-DOCUMENTakan menjelaskan file in-line kemudian mengarahkan ulang. Entah ekspansi shell ditafsirkan atau tidak.

Penanya mencatat dalam komentar di bawah ini bahwa ketika ia mencoba menggunakan metode ini sebagai rootpengguna, ia akan diberikan kesalahan izin. Ketika saya menjawab saya menyarankan dia chownatau chgrpyang /dev/fd/${num}file khusus, tapi ini mungkin bukan metode terbaik. Alasan dia menemukan masalah ini rootadalah diberikan readizin tetapi bukan execute izin. Anda dapat dengan mudah menangani ini hanya dengan menghindari execpanggilan. Alih-alih memohon /dev/fd/${num}file secara langsung pada baris perintah, lakukan:

su -c '. /dev/fd/'${num} ${num}<<SCRIPT 

Menggunakan dua heredocs dapat membantu melarikan diri. Inilah yang terjadi dalam setiap kasus:

TANPA SETELAH <<HEREDOC

sh 3<<\CMD /dev/fd/3
    ( echo 'without set "$@" or \$@ in here-doc' ; echo
    set -- '1 "2" 3' 4 "5 6"
    su - mikeserv 4<<-UNQUOTED 5<<-\PREQUOTED /dev/fd/4
        echo UNQUOTED; echo $0 "$*"
        printf "%s\\t\\t%s\\t\\t%s\\t\\t%s\\n" $(printf "'%s' " "$@") \\
                $@ '$@' "$@" '"$@"' "'$@'" \$@ '\$@' "\$@" '"\$@"'
    . /dev/fd/5
    UNQUOTED
        echo PREQUOTED ; echo $0 "$*"
        printf "%s\t\t%s\t\t%s\t\t%s\n" $(printf "'%s' " "$@") \
                $@ '$@' "$@" '"$@"' \$@ '\$@' "\$@" '"\$@"'
    PREQUOTED
    )
CMD

KELUARAN

without set "$@" or \$@ in here-doc

UNQUOTED
/dev/fd/3 1 2 3 4 5 6
1 "2" 3         4               5 6             1
2               3               4               5
6               1 "2" 3 4 5 6           1 2 3 4 5 6             "1 "2" 3 4 5 6"
'1 2 3 4 5 6'           $@              "$@"
PREQUOTED
/dev/fd/5
''              $@              "$@"            $@
\$@             $@              "\$@"

SET "$@"IN<<HEREDOC

sh 3<<\CMD /dev/fd/3
    ( echo 'set "$@" and \$@ in here-doc' ; echo
    set -- '1 "2" 3' 4 "5 6"
    su - mikeserv 4<<-UNQUOTED 5<<-\PREQUOTED /dev/fd/4
        set -- "$@" "\$@"
        echo UNQUOTED; echo $0 "$*"
        printf "%s\\t\\t%s\\t\\t%s\\t\\t%s\\n" $(printf "'%s' " "$@") \\
                $@ '$@' "$@" '"$@"' "'$@'" \$@ '\$@' "\$@" '"\$@"'
        . /dev/fd/5
    UNQUOTED
        set -- "$@" "\$@"
        echo PREQUOTED ; echo $0 "$*"
        printf "%s\t\t%s\t\t%s\t\t%s\n" $(printf "'%s' " "$@") \
                $@ '$@' "$@" '"$@"' \$@ '\$@' "\$@" '"\$@"'
    PREQUOTED
)
CMD

KELUARAN

set "$@" and \$@ in here-doc

UNQUOTED
/dev/fd/3 1 2 3 4 5 6
1 "2" 3         4               5 6             1
2               3               4               5
6               1 "2" 3 4 5 6           1 2 3 4 5 6             "1 "2" 3 4 5 6"
'1 2 3 4 5 6'           1 2 3 4 5 6             $@              1 2 3 4 5 6
"$@"
PREQUOTED
/dev/fd/5 1 2 3 4 5 6 $@
'1              2               3               4
5               6'              '$@'            1 2 3 4 5 6
$@              $@              1 2 3 4 5 6             $@
"$@"            $@              \$@             $@
"\$@"  

SET "$@"DAN LEBIH DALAM<<HEREDOC

sh 3<<\CMD /dev/fd/3
    ( echo 'set "$@" and \$@ AND additional parameters in here-doc' ; echo
    set -- '1 "2" 3' 4 "5 6"
    su - mikeserv 4<<-UNQUOTED 5<<-\PREQUOTED /dev/fd/4
        set -- "$@" "\$@" '7 "8" 9' 10 "11 12"
        echo UNQUOTED; echo $0 "$*"
        printf "%s\\t\\t%s\\t\\t%s\\t\\t%s\\n" $(printf "'%s' " "$@") \\
                $@ '$@' "$@" '"$@"' "'$@'" \$@ '\$@' "\$@" '"\$@"'
        . /dev/fd/5
    UNQUOTED
        set -- "$@" "\$@" '13 "14" 15' 16 "17 18"
        echo PREQUOTED ; echo $0 "$*"
        printf "%s\t\t%s\t\t%s\t\t%s\n" $(printf "'%s' " "$@") \
                $@ '$@' "$@" '"$@"' \$@ '\$@' "\$@" '"\$@"'
    PREQUOTED
    )
CMD

KELUARAN

set "$@" and \$@ AND additional parameters in here-doc

UNQUOTED
/dev/fd/3 1 2 3 4 5 6
1 "2" 3         4               5 6             1
2               3               4               5
6               1 "2" 3 4 5 6           1 2 3 4 5 6             "1 "2" 3 4 5 6"
'1 2 3 4 5 6'           1 2 3 4 5 6             7 "8" 9         10
11 12           $@              1 2 3 4 5 6             7 "8" 9
10              11 12           "$@"
PREQUOTED
/dev/fd/5 1 2 3 4 5 6 7 "8" 9 10 11 12 $@ 13 "14" 15 16 17 18
'1              2               3               4
5               6'              '7              "8"
9'              '10'            '11             12'
'$@'            '13             "14"            15'
'16'            '17             18'             1 2 3 4 5 6
7 "8" 9         10              11 12           $@
13 "14" 15              16              17 18           $@
1 2 3 4 5 6             7 "8" 9         10              11 12
$@              13 "14" 15              16              17 18
"$@"            $@              \$@             $@
"\$@"  
mikeserv
sumber
Masalahnya adalah skrip pertama Anda memberi saya "8 9\n4 5 6\n". Saya sedang berlari debian 6, bash-4.1.5dan su.
x-yuri
@ x-yuri - dan yang kedua, yang menghindari semua kekacauan kutipan?
mikeserv
Jika lari dari rootitu mengatakan: -su: /dev/fd/4: Permission denied. Ngomong-ngomong, apa maksudnya? Kalau tidak, output seperti yang Anda katakan, tetapi tidak menjawab pertanyaan. Pertanyaannya adalah tentang menggunakan --dan -.
x-yuri
@ x-yuri Saya pikir itu berarti Anda harus chown /dev/fd/4selama Anda akan membutuhkannya, atau hanya chgrp. Saya tidak punya banyak waktu untuk menguji sekarang. Tapi itu sedikit di luar intinya, seperti yang lain Anda tidak perlu memberikan argumen di ujung ekor sama sekali - hanya bekerja kutipan Anda. Lihat sekarang?
mikeserv
Jika kita menghilangkan masalah sukarena tidak dapat bekerja dengan dialihkan stdin, melewati argumen masih lebih baik daripada memasukkannya ke dalam perintah. Karena dalam kasus terakhir Anda perlu melarikan diri dari mereka.
x-yuri