Передача аргументов в предоставленную оболочку

8

man su говорит:

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

man bash говорит:

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

Ну что ж, посмотрим:

[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

Что я ожидал (вывод второй команды отличается):

[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

Вероятно, не большая проблема. Но что там происходит? Второй и третий варианты кажутся подходящими, но один из них не работает. Четвертый кажется ненадежным, -может рассматриваться как suвариант.

х-юри
источник
Это странно. Я получаю именно те результаты, которые вы ожидаете (и я также согласен с этими ожиданиями). Я имею в виду, что в случае второго вызова я получаю «1 2 3» в качестве вывода. Я использую bash 4.2.45как на исходном, так и на конечном счете.
Кшиштоф Адамски

Ответы:

9

Происходит следующее: первый аргумент, который вы указываете оболочке, - это $0параметр (обычно это имя оболочки). Это не входит, когда вы делаете, echo $*так $*как каждый аргумент кроме $0.

Пример:

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

Обновить

Делаем следующую команду:

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

дает прямую линию:

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

Так что почему-то кажется, что в этом случае suмы поглощаем лишнее, --не передавая его bash, возможно, из-за ошибки (или, по крайней мере, недокументированного поведения). Однако это не съест больше двух --аргументов:

# su graeme -c 'echo $0; echo "$*"' -- -- -- 1 2 3
--
1 2 3
Graeme
источник
Это я понимаю. Я делаю: su - yuri -c 'echo "$*"' -- -- 1 2 3оболочка предположительно получает -- 1 2 3, но выводит только 2 3. Это вообще имеет какой-то смысл?
x-yuri
И когда я делаю bash -c 'echo $*' -- 1 2 3, это выводит, 1 2 3как ожидалось.
x-yuri
@ х-юри, обновлено. Это похоже на suошибку.
Грэм
5

На самом деле ответ @ Graeme - и ваш вопрос - просто ссылаются на побочные эффекты того, как обрабатывает оболочка. "$@positional $*parameters".Они назначаются оболочкой своим аргументам при вызове и в любое время позже с помощью встроенной setутилиты. Они могут быть вызваны в любое время с помощью либо того, "$*"который разделяет каждый позиционный элемент с первым символом, "$IFS"либо "$@"который цитирует каждый позиционный элемент и разделяет их со всеми"$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

Если у вас уже есть значения, которые вы вводите в оболочку, вам не нужно делать это --три раза. Параметры оболочки могут set- всегда, в любое время, а не только при вызове (исключая $ 0 и -i):

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

7 8 9
4 5 6

И все эти цитаты из оболочки могут сбивать с толку. Это немного упрощает вещи:

( 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

Аргументы родительской оболочки равны set4, 5 и 6 и затем передаются в подоболочку, вызываемую suчерез позиционныйparameter "$@array".

Обратите внимание на то, как я ( subshell )выполняю приведенную выше команду - я делаю это, потому что я не хочу возиться с моей текущей средой оболочки - потому что я могу непреднамеренно изменить то, что я предпочел бы не делать, если бы я сделал сset.

О НАПРАВЛЕНИИ:

Прежде всего, ваша система Unix работает с файлами - разрешениями на файлы, содержимым файлов, атрибутами файлов. Так или иначе, каждый используемый вами объект данных может (и, по крайней мере, на мой взгляд, должен) рассматриваться как файл. Перенаправление указывает на файл - и все. A <<HERE-DOCUMENTопишет файл в строке, а затем перенаправит его. Либо расширения оболочки интерпретируются, либо нет.

Аскер отмечает в комментариях ниже, что когда он пытается использовать этот метод в качестве rootпользователя, ему выдается ошибка прав доступа. Когда я ответил , я предложил , чтобы он chownили chgrpв /dev/fd/${num}специальный файл, но это, вероятно , не лучший метод. Причиной, по которой он сталкивается с этой проблемой, rootявляется предоставление readразрешений, но не execute разрешений. Вы можете легко справиться с этим, просто избегая execвызова. Вместо того, чтобы вызывать /dev/fd/${num}файл непосредственно из командной строки, выполните:

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

Использование двух heredocs может помочь с побегом. Вот что происходит в каждом случае:

НЕТ УСТАНОВЛЕНО <<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

ВЫВОД

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

ВЫВОД

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             $@
"$@"            $@              \$@             $@
"\$@"  

УСТАНОВИТЬ "$@"И БОЛЬШЕ В<<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

ВЫВОД

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
"$@"            $@              \$@             $@
"\$@"  
Микесерв
источник
Проблема в том, что ваш первый сценарий дает мне "8 9\n4 5 6\n". Я бегу debian 6, bash-4.1.5и su.
x-yuri
@ х-юри - и второе, что позволяет избежать всего беспорядка цитирования?
mikeserv
Если бежать от rootнего , говорит: -su: /dev/fd/4: Permission denied. Кстати, вы знаете, что это значит? В противном случае он выводит, как вы говорите, но не решает вопрос. Вопрос об использовании --и -.
x-yuri
@ x-yuri Я думаю, это значит, что ты должен chown /dev/fd/4это делать на время, или просто chgrp. У меня не так много времени для тестирования прямо сейчас. Но это немного не относится к делу, как и в случае с другими, вам вообще не нужно передавать аргументы в конце - просто поработайте над цитатой. Видишь это сейчас?
mikeserv
Если мы опускаем проблему с suневозможностью работать с перенаправленным stdin, передача аргументов еще лучше, чем внедрение их в команду. Потому что в последнем случае вам нужно избежать их.
x-yuri