Почему моя программа под названием «set» не выполняется?

10

Я создал простую программу на C, например:

int main(int argc, char *argv[]) {

    if (argc != 5) {
       fputs("Not enough arguments!\n", stderr);
       exit(EXIT_FAILURE);
    }

И мой PATH был изменен в etc / bash.bashrc примерно так:

PATH=.:$PATH

Я сохранил эту программу как set.c и собираю ее

gcc -o set set.c

в папке

~/Programming/so

Тем не менее, когда я звоню

set 2 3

Ничего не произошло. Там нет текста, который появляется.

призвание

./set 2 3

дает ожидаемый результат

У меня никогда не было проблем с PATH раньше и

which set

возвращается ./set. Так что, кажется, ПУТЬ правильный. Что происходит?

Ганея Дан Андрей
источник
10
Относительно опасно добавлять «.» в ваш путь. Лучше просто использовать ./ при выполнении чего-либо из локального каталога или переместить исполняемый файл в хорошо известный каталог, такой как ~ / bin /
TREE
7
Также плохая идея вызывать вашу тестовую программу testпо той же причине; testэто встроенная оболочка тоже.
Джонатан Леффлер
@JonathanLeffler И все же для быстрых тестов вызов программы testимеет смысл. Конечно, к тому времени, когда вы положите это в свой, PATHвы действительно должны были придумать другое имя. И пока вы не поместите программу в свою, PATHвы должны будете вызывать ее так ./testили иначе. Поэтому вполне нормально использовать имя testдля программы, если это быстрый тест, который вы собираетесь удалить до конца дня.
kasperd
1
@kasperd: Насколько я знаю, обычное название для быстрой программы тестирования - foo.
Hmakholm оставил Монику
Если вы назовете его, lsто всякий раз, когда вы будете проверять, существует ли он, он будет запускаться (но только если вы измените свой путь, как вы указали в вопросе).
Ctrl-Alt-Delor

Ответы:

24

Вместо использования which, которое не работает, когда вам это нужно больше всего , используйте typeдля определения того, что будет работать при вводе команды:

$ which set
./set
$ type set
set is a shell builtin

Оболочка всегда ищет встроенные функции перед поиском $PATH, поэтому настройка $PATHздесь не поможет.

Лучше было бы переименовать ваш исполняемый файл во что-то другое, но если ваше назначение требует имени программы set, вы можете использовать функцию оболочки:

$ function set { ./set; }
$ type set
set is a function
set ()
{
    ./set
}

(Это работает bash, но другие оболочки вроде kshне могут этого позволить. См. Ответ mikeserv для более портативного решения.)

Теперь при наборе текста setбудет запущена функция с именем set ./set. GNU bashищет функции перед поиском встроенных функций и ищет встроенные функции перед поиском $PATH. Раздел «КОМАНДНОЕ ИСПОЛНЕНИЕ» на странице руководства bash дает более подробную информацию об этом.

Смотрите также документацию по builtinи command: help builtinи help command.

yellowantphil
источник
3
Вы рекомендуете typeболее which, но не дают никаких оснований , почему. ( Я знаю почему , но тот, кому нужна рекомендация, не стал бы.)
cjm
1
@cjm Вот целый трактат о том, почему нет : unix.stackexchange.com/questions/85249/…
Энтони
Очень информативно. Вы бы никогда не догадались, что вокруг такой, казалось бы, простой команды, которая выполняет простую задачу, так много противоречий
Ганея Дан Андрей
4
@GaneaDanAndrei В основном, используйте typeвместо which, не называйте вашу программу "set", и поймите, что function set { ./set; }это ужасный хак, которого вы, вероятно, должны избегать.
Йеллоантфил
11

setявляется встроенным в Bash (и, вероятно, большинство других оболочек). Это означает, что bash даже не будет искать путь при поиске функции.

В качестве дополнительного замечания я бы настоятельно рекомендовал не добавлять .путь в целях безопасности. Представьте, например cd, что /tmpпосле того, как любой другой пользователь добавил исполняемый файл /tmp/cd.

klimpergeist
источник
2
Да, это глупая идея, что мой учитель навязывает нам, чтобы «не выглядеть непрофессионально во время представления программы». Он оценивает вас, если это не сделано.
Ганея Дэн Андрей
1
Домовой указывает на великих учителей :(
klimpergeist
4
cdэто встроенная оболочка, поэтому пример не будет работать по той же причине, что и с set.
Эмиль Йержабек,
15
Использование ./fooдля вызова программы является профессиональным; это показывает, что вы понимаете, почему .не должно быть в $ PATH. Ваш учитель не прав, и вы можете сказать ему, что я так сказал.
Звол
10

setэто не просто встроенная функция, это специальная встроенная функция POSIX . Существует несколько встроенных команд, которые определены стандартами и которые можно найти в поиске команд раньше всего $PATH: поиск не выполняется, имена функций не ищутся и т. Д. Большинство встроенных команд, которые не являются специальными , фактически требуются стандартом POSIX для находится в вашем $PATH до того, как оболочка запустит любую из своих встроенных процедур. Это относится echoи большинство других (хотя ли стандарт почитаются в этом отношении был предметом спора в открытых списках рассылки в прошлом) , но не из set, trap, break,return, continue, ., :, times, eval, exit, export, readonly, unset, Или exec.

Все они являются зарезервированными именами оболочки и имеют специальные атрибуты, отличные от порядка их предпочтения для поиска команд. Например, вы не можете определить функцию оболочки с любым из этих имен в стандартизированной оболочке. Это хорошо - это позволяет людям безопасно писать переносимые скрипты . Это базовые команды, из которых опытный сценарист может установить безопасную и надежную точку опоры в своей среде. Вторжение в это пространство имен не рекомендуется.

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

Так что вы могли бы сделать:

alias set=./set

... который будет работать просто отлично.

mikeserv
источник
3

Проблема в том, что setэто встроенная оболочка, и лучшим решением будет использование другого имени для вашей исполняемой программы.

Кстати, на прошлой неделе я задал вопрос о том, как запускать системные команды вместо встроенных команд с тем же именем, и решение, которое я принял, состояло в том, чтобы выполнить команду через env:

env set 2 3

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

./set 2 3

Оба вышеупомянутых решения не зависят от оболочки, т. Е. Они будут работать независимо от того, какую оболочку вы используете.

Такие предложения, как использование commandвстроенной функции, не будут работать в Bash: это только предотвращает запуск функций оболочки . Хотя это не задокументировано, я также заметил, что использование commandтакже подавляет ключевые слова оболочки . Однако, это не будет делать то же самое для встроенных командных оболочек, таких как set. Насколько я понимаю, commandмогут работать другие оболочки, такие как zsh.

Кроме того , приемы , такие как \setили "set"или 'set'не работать на Bash встроенных команд - хотя они полезны для запуска исполняемых файлов вместо псевдонимов или оболочки ключевых слов .

Примечание. Этот ответ изначально начинался как комментарий к принятому Эрику ответу, но стал слишком большим, чтобы вписаться в комментарий. Другие ответы, рекомендующие использование, typeа не добавление .к PATH, являются хорошими.

Энтони Геогеган
источник