Спосіб використання `/ usr / bin / env sed -f` у shebang?


25

Введення /usr/bin/env sed -fтермінальних робіт.

Але якщо використовувати його як шебанг,

#!/usr/bin/env sed -f 
s/a/b/

Сценарій не вдасться виконати:

/usr/bin/env: sed -f: No such file or directory

Я начебто вважаю, що це пов'язано з -f. Але як це вирішити?


Відповіді:


33

Не можна, переносно, розміщувати більше одного аргументу на #!рядку . Це означає лише повний шлях і один аргумент (наприклад, #!/bin/sed -fабо #!/usr/bin/sed -f), або #!/usr/bin/envніякий аргумент для перекладача.

Обхід для отримання портативного скрипту полягає у використанні #!/bin/shта оболонці оболонки, передаючи скрипт sed як аргумент командного рядка. Зауважте, що це не санкціонується POSIX (мультиінструкційні сценарії повинні писатися з окремим -eаргументом для кожної інструкції щодо переносимості), але це працює з багатьма реалізаціями.

#!/bin/sh
exec sed '
s/a/b/
' "$@"

Для довгого сценарію може бути зручніше використовувати гередок. Перевагою гередока є те, що вам не потрібно цитувати єдині цитати всередині, якщо такі є. Основним недоліком є ​​те, що сценарій подається на стандартний вхід з двома прикрими наслідками. Деякі версії sed потребують -f /dev/stdinзамість -f -, що є проблемою для портативності. Гірше, що сценарій не може діяти як фільтр, тому що стандартним вводом є сценарій і не може бути даними.

#!/bin/sh
exec sed -f - -- "$@" <<'EOF'
s/a/b/
EOF

Мінус гередока можна виправити корисним використанням cat. Оскільки це знову ставить весь скрипт у командному рядку, він не сумісний з POSIX, але багато в чому портативний на практиці.

#!/bin/sh
exec sed "$(cat <<'EOF')" -- "$@"
s/a/b/
EOF

Іншим вирішенням є написання сценарію, який можна проаналізувати як sh, так і sed. Це портативний, розумно ефективний, просто трохи некрасивий.

#! /bin/sh
b ()
{
x
}
i\
f true; then exec sed -f "$0" "$@"; fi
: ()
# sed script starts here
s/a/b/

Пояснення:

  • Під sh: визначте функцію, яку називають b; вміст не має значення, поки функція синтаксично сформована (зокрема, ви не можете мати порожню функцію). Тоді, якщо це правда (тобто завжди), виконуйте sedсценарій.
  • Під sed: гілка на ()етикетку, потім якийсь добре сформований ввід. Потім iкоманда, яка не має ефекту, тому що вона завжди пропускається. Нарешті ()мітка з наступною корисною частиною сценарію.
  • Тестували під GNU sed, BusyBox та OpenBSD. (На GNU sed можна втекти чимось простішим, але OpenBSD sed вимогливий щодо частин, які він пропускає.)

1
"або #!/usr/bin/envі немає аргументу." не фразується дуже добре. Можливо, "чи #!/usr/bin/env sedні аргумент до sed".
cjm

+1 за "просто трохи некрасиво" та
двомовний

6

Існують різні несумісні реалізації shebang (#!) Залежно від ОС. Деякі будують повний список аргументів, деякі зберігають командний шлях і ставлять усі аргументи, що залишилися, як єдиний, деякі ігнорують усі аргументи і передають лише командний шлях, і, нарешті, деякі передають всю рядок як єдину командування. Ви, здається, в останньому випадку.


2

env намагається знайти файл із назвою "sed -f". Ви можете спробувати "#! / Usr / bin / sed -f" в якості рядка shebang.


2

Станом на GNU coreutils v8.30 , ви можете:

#!/usr/bin/env -S sed -f

Ця функція була додана в недавньому (2018-04-20) зробити , щоб env.cв ГНУ Coreutils пакет, який додав до -Sабо --split-stringваріант.

На envчоловіковій сторінці:

OPTIONS
-S/--split-string usage in scripts
    The  -S  option allows specifing multiple parameters in a script.
    Running a script named 1.pl containing the following first line:

            #!/usr/bin/env -S perl -w -T

    Will execute perl -w -T 1.pl .

    Without the '-S' parameter the script will likely fail with:

            /usr/bin/env: 'perl -w -T': No such file or directory

    See the full documentation for more details.

Більше прикладів можна знайти в посібнику з GNU coreutils .

Якщо ви також використовуєте -vпараметр для багатослівного виведення, ви можете точно бачити, як envрозбивається рядок аргументів:

В my_sed_script.sed:

#!/usr/bin/env -vS sed -f
s/a/b/

Виконання:

$ ./my_sed_script.sed
split -S:  ‘sed -f’
 into:    ‘sed’
     &    ‘-f’
executing: sed
   arg[0]= ‘sed’
   arg[1]= ‘-f’
   arg[2]= ‘./my_sed_script.sed’

Примітка. Це стосується лише shebangs, які використовують /usr/bin/env, як --split-stringце особливо характерно для GNU env.


1

Ця відповідь забезпечує шлях до елегантного рішення: /programming//a/1655389/642372

  1. read гередок в змінну оболонки.
  2. Передайте цю змінну як позиційний аргумент sed.

Зразок:

#!/usr/bin/env bash

read -rd '' SED_SCRIPT <<EOD      
# Your sed script goes here.
s/a/b/
EOD

exec sed "$SED_SCRIPT" "$@"

1

Мені подобається рішення Уокера, але його можна вдосконалити (поставивши це окремою відповіддю, оскільки коментарі не приймають попередньо відформатованого тексту). Це може не працювати у всіх версіях Linux або Bash, але на Ubuntu 17.10 ви можете зменшити це до наступного:

#!/usr/bin/env bash
read SED_SCRIPT <<EOD
s/a/b/
EOD
sed "$SED_SCRIPT" "$@"

Ви можете видалити evalта спростити readкоманду, але ви повинні позбутися коментаря всередині гередока.

Крім того, для всього, що ви коли-небудь хотіли знати про sed, але боялися запитати, є дуже корисний підручник на веб- сайті http://www.grymoire.com/unix/sed.html . Це говорить про ще краще рішення, за рахунок втрати /usr/bin/envта жорсткого кодування шляху до sed:

#!/bin/sed -f
s/a/b/
s/c/d/

Це має додаткову перевагу в підтримці більше однієї s///заміни.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.