Почему "-" в "#! /bin/sh -"?

Попробуйте это

awk '
  NR==1 {
    print;
    next;
  }
  NR==FNR {
   split($2,a,","); 
   for(i in a) b[a[i]] = b[a[i]]==""? $1 : b[a[i]] "," $1;
   next;
  }
  {
    if ($1 in b) print $1 "\t" b[$1];
  }
  ' File1 File2

Если у вас нет / не нужна строка заголовка, вы можете упростить, отбросив первую пару правило / действие.

28
16.03.2017, 11:11
1 ответ

Это по той же причине, что и вам нужно написать:

rm -- *.txt

А не

rm *.txt

Если вы не можете гарантировать, что ни один из файлов .txt в текущем каталоге не имеет имени, начинающегося с - .

В:

rm <arg>

считается вариантом, если он начинается с - или файла, который нужно удалить в противном случае. В

rm -- <arg>

arg всегда рассматривается как файл для удаления, независимо от того, начинается он с - или нет.

То же самое для sh .

Когда выполняется сценарий, который начинается с

#! /bin/sh

Обычно с:

execve("path/to/the-script", ["the-script", "arg"], [environ])

Система преобразует его в:

execve("/bin/sh", ["/bin/sh", "path/to/the-script", "arg"], [environ])

Путь / к / сценарию обычно не находится под контроль автора сценария. Автор не может предсказать, где и под каким именем будут храниться копии сценария. В частности, они не могут гарантировать, что путь / к / скрипту , с которым он вызывается, не будет начинаться с - (или + , который также проблема с sh ). Вот почему нам нужны , - здесь, чтобы отметить конец параметров.

Например, в моей системе zcat (как и большинство других скриптов на самом деле) является одним из примеров скрипта, который не следовал этой рекомендации:

$ head -n1 /bin/zcat
#!/bin/sh
$ mkdir +
$ ln -s /bin/zcat +/
$ +/zcat
/bin/sh: +/: invalid option
[...]

Теперь вы можете спросить, почему #! / bin / sh - , а не #! / bin / sh - ?

Хотя #! / bin / sh - будет работать с оболочками POSIX, #! / bin / sh - более портативен; в частности к древним версиям sh . sh обработка - как предшественников конца опции getopt () и общее использование - для обозначения конца варианты на долгое время. Так как оболочка Bourne (с конца 70-х) анализировала свои аргументы, только первый аргумент рассматривался для параметров, если он начинался с - . Все символы после - будут рассматриваться как имена параметров; если после - не было символа, вариантов не было. Это застряло, и все более поздние оболочки, подобные Борну, распознают - как способ отметить конец параметров.

В оболочке Борна (но не в современных оболочках типа Борна) #! / bin / sh -euf также поможет обойти проблему, поскольку для опций учитывался только первый аргумент.

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

  1. никто в здравом уме не стал бы называть сценарий с чем-то, начинающимся с - или + или поместите их в каталог, имя которого начинается с - или + .
  2. даже если бы они это сделали, сначала можно было бы возразить, что они могут винить только себя, но также, когда вы вызываете сценарий, чаще всего, это из оболочки или из execvp () / функции типа execlp () .И в этом случае обычно вы вызываете их либо как the-script , чтобы его можно было найти в $ PATH , и в этом случае аргумент пути к execve () системный вызов обычно начинается с / (не - и + ) или как ./ the-script , если вы хотите сценарий в текущем каталоге, который будет запущен (а затем путь начинается с ./ , а не с - или + ).

Теперь, помимо проблемы теоретической правильности , есть еще одна причина, по которой #! / bin / sh - был рекомендован в качестве хорошей практики . И это восходит к тому времени, когда несколько систем все еще поддерживали сценарии setuid.

Если у вас есть сценарий, содержащий:

#! /bin/sh
/bin/echo "I'm running as root"

И этот сценарий был установлен с идентификатором root (например, с разрешениями -r-sr-xr-x root bin ) в этих системах при выполнении обычный пользователь,

execve("/bin/sh", ["/bin/sh", "path/to/the-script"], [environ])

будет выполнен как root !

Если пользователь создал символическую ссылку / tmp / -i -> путь / к / сценарию и выполнил ее как -i , то он запустил бы интерактивную оболочку ( / bin / sh -i ) как корень .

- может обойти это (это не будет работать с проблемой состояния гонки или того факта, что некоторые реализации sh , такие как некоторые на основе] ksh88 будут искать аргументы скрипта без / в $ PATH ).

В настоящее время практически никакая система не поддерживает сценарий setuid, а некоторые из тех, которые все еще поддерживают (обычно не по умолчанию), в конечном итоге выполняют execve ("/ bin / sh", ["/ bin / sh "," / dev / fd / ", arg]) (где - дескриптор файла, открытый для чтения в скрипте), который работает как с этой проблемой, так и с гонкой состояние.

Обратите внимание, что аналогичные проблемы возникают с большинством интерпретаторов, а не только с оболочками типа Борна. Не-Борн-подобные оболочки обычно не поддерживают - в качестве маркера конца опции, но обычно поддерживают вместо него - (по крайней мере, для современных версий).

Также

#! /usr/bin/awk -f
#! /usr/bin/sed -f

Нет проблемы, так как следующий аргумент в любом случае рассматривается как аргумент для параметра -f , но он все равно не работает, если path / to / script - это - (в этом случае с большинством реализаций sed / awk , sed / awk считывает код не из файла - , а из стандартного ввода).

Также обратите внимание, что в большинстве систем нельзя использовать:

#! /usr/bin/env sh -
#! /usr/bin/perl -w --

Как и в большинстве систем, механизм shebang допускает только один аргумент после пути интерпретатора.

Относительно использования #! / bin / sh - vs #! / bin / sh - , это дело вкуса. Я предпочитаю первый вариант, так как он делает путь интерпретатора более заметным и упрощает выбор мышью. Существует легенда , в которой говорится, что пространство необходимо в некоторых древних версиях Unix , но AFAIK, это никогда не проверялось.

Очень хорошую ссылку на Unix shebang можно найти на https://www.in-ulm.de/~mascheck/various/shebang

55
27.01.2020, 19:39

Теги

Похожие вопросы