sed удаляет строки, начинающиеся с #, но не с #! (shell скрипты)

Окончательный ответ на вопрос "как запускаются программы" в Linux - это пара статей на LWN.net, озаглавленных, как ни странно, Как запускаются программы и Как запускаются программы: Двоичные файлы ELF. В первой статье скрипты рассматриваются кратко. (Строго говоря, окончательный ответ находится в исходном коде, но эти статьи легче читать и они содержат ссылки на исходный код.)

Небольшие эксперименты показывают, что вы почти все правильно поняли, и что выполнение файла, содержащего простой список команд, без шебанга, должно обрабатываться оболочкой. В execve(2) manpage содержится исходный код тестовой программы execve; мы воспользуемся ею, чтобы посмотреть, что происходит без оболочки. Сначала напишите тестовый скрипт, testscr1, содержащий

#!/bin/sh

pstree

и другой, testscr2, содержащий только

pstree

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

chmod u+x testscr[12]
./testscr1 | less
./testscr2 | less

Теперь попробуйте снова, используя execve (предполагая, что вы создали его в текущем каталоге):

./execve ./testscr1
./execve ./testscr2

testscr1 по-прежнему запускается, но testscr2 производит

execve: Exec format error

Это показывает, что оболочка обрабатывает testscr2 по-другому. Однако он не обрабатывает сам сценарий, для этого по-прежнему используется /bin/sh; это можно проверить, переведя testscr2 в less:

./testscr2 | less -ppstree

На моей системе я получаю

    |-gnome-terminal--+-4*[zsh]
    |                 |-zsh-+-less
    |                 |     `-sh---pstree

Как вы можете видеть, есть оболочка, которую я использовал, zsh, которая запустила less, и вторая оболочка, обычная sh (dash на моей системе), для запуска сценария, который запустил pstree. В zsh этим занимается zexecve в Src/exec. c: оболочка использует execve(2), чтобы попытаться выполнить команду, и если это не удается, она читает файл на предмет наличия в нем shebang, обрабатывая его соответствующим образом (что также будет сделано ядром), и если это не удается, она пытается запустить файл с помощью sh, если из файла не был прочитан ни один нулевой байт:

        for (t0 = 0; t0 != ct; t0++)
            if (!execvebuf[t0])
                break;
        if (t0 == ct) {
            argv[-1] = "sh";
            winch_unblock();
            execve("/bin/sh", argv - 1, newenvp);
        }

bash имеет такое же поведение, реализованное в execute_cmd. c с полезным комментарием (на который указал taliezin):

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

  1. fork ()
  2. connect pipes
  3. look up the command
  4. do redirections
  5. execve ()
  6. Если execve не удалось, посмотрите, установлен ли в файле исполняемый режим. Если да, и это не каталог, то выполните его содержимое как сценарий оболочки.

POSIX определяет набор функций, известных как функции exec(3), которые обертывают execve(2) и обеспечивают эту функциональность; подробности см. в ответе muru. В Linux, по крайней мере, эти функции реализуются библиотекой C, а не ядром.

4
09.07.2017, 02:15
7 ответов
sed -i -e '/^#!/p' -e '/^#/d' file

Это будет проходить через файл построчно и находит строку, начинающуюся с #! будет напечатано первым выражением. Затем он будет удален из пространства шаблонов вторым выражением (т. е. он не будет напечатан через секундураз командой по умолчанию p, которая действует, когда не используется sed -n).

Строка, начинающаяся только с #, будет игнорироваться первым выражением, но удаляться вторым выражением.

Любая другая строка будет напечатана командой по умолчанию p.

Чтобы разрешить пробелы перед #(а также удалить эти строки):

sed -i -e '/^[[:blank:]]*#!/p' -e '/^[[:blank:]]*#/d' file

Выражение [[:blank:]]будет соответствовать пробелу или табуляции. персонаж.


Как упомянул Стефан, это комментарии, изменение pна bв первом выражении позволит сценарию sedпродолжить работу со следующей строкой ввода без учета второе выражение, если первое выражение совпадает. Команда bпереходит к предопределенной метке или к концу сценария sed, если метка не указана. Это будет оптимизация.

10
27.01.2020, 20:44

Для "и"двух адресов нужна группа команд ({...;}):

sed '/^[[:space:]]*#/{/^#!/!d;}' < file

С GNU sed, вы можете использовать -iвместо inplace, замените [[:space:]]на \s(при условии, что это последняя версия) и опустите ;:

sed -i '/^\s*#/{/^#!/!d}' file

Вы можете вложить несколько, но имейте в виду, что при переносе у вас не может быть ничего после }. Таким образом, для A и B, а не C и не Dэто будет:

sed '/A/{/B/{/C/!{/D/!d;}' -e '}' -e '}' < file

Или:

sed '
  /A/{
    /B/{
      /C/!{
        /D/!d
      }
    }
  }' < file
6
27.01.2020, 20:44

Вывод: удалить все комментарии, не относящиеся к шебангу.

sed -e '1{/^\s*\#/{/^\#!/!d}}' -e '1!{/^\s*\#/d}' file

Ваша команда изменена для использования одинарных кавычек (без двойных \\):

sed '/^\(\s\)*\#/d'

будет работать почти правильно, просто добавив деталь, что (после #) должно быть что-то, что не является звездочкой [^!]sed '/^\(\s\)*\#[^!]/d'. Но это не удастся с пустой строкой после символа комментария (#`).

Для этого нужно утверждать, что линия закончилась ($).

Для этого нам потребуется использование расширенного синтаксиса ([^!]|$):

sed -E '/^\s*\#([^!]|$)/d'

Или, более переносимого:

sed -E '/^[ \t]*\#([^!]|$)/d'

Однако для сценария только первая строка, которая начните как #! имеет значение.
Все остальные строки, начинающиеся с необязательного пробела и #, являются комментариями:

sed -e '1{/^#!/!d}' -e '1!{/^[ \t]*#/d}' file

Что означает:

Первая -e

, если первая строка начинается с комментария (пробел и #), но не начинается точно с участием #! он стирается.

Вторая -e

другие строки (1! ), начинающиеся с необязательного пробела и #, удаляются.

2
27.01.2020, 20:44

Используйте негативный взгляд -вперед в Perl:

perl -ne 'print unless /#(?!!)/'

Это удаляет строки, содержащие #, за которыми не следует !. Если вы хотите сопоставить #только в начале строки, возможно, с предшествующим пробелом, используйте

perl -ne 'print unless /^\s*#(?!!)/'
4
27.01.2020, 20:44

sed -i -e '/^\s*#\([^!]\|$\)/d'

Где:

  • ^начало строки
  • \s*ноль или более пробелов
  • #одна решетка
  • \([^!]\|$\)с последующим символом, который не является !или конец строки.
14
27.01.2020, 20:44

Мне помогло:

$ cat test.sed 
  # delete this one

  #! don't delete this one

        # delete this too

Команда:

$ sed -i -e '/^[[:space:]]*#[^!].*/d' test.sed

Результат:

$ cat test.sed 

  #! don't delete this one

Что ж, эта команда не удалит строк только с одиноким #. Итак, я сохраняю этот ответ, чтобы показать, почему вам нужно ссылаться на другие решения.

0
27.01.2020, 20:44

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

Вы можете использовать ту же самую команду подстановки, если вы ограничите ее рассмотрением только всего, начиная со строки 2; в нотации sedэто диапазон 2,$. Вы ограничиваете команды sedдиапазоном, добавляя к ним префикс этого диапазона :

.
sed "2,${/^\(\\s\)*\\#/d}"

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

sed -e '2,${/^\s*#/d}'

Пример

Вход:

#!/path/to/a/shebang-command
# This is the first comment

command 1
  # another comment
  command 2
# Final comment

Выход:

#!/path/to/a/shebang-command

command 1
  command 2
0
27.01.2020, 20:44

Теги

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