[devel] #! shebang fixer

Alexey Tourbin =?iso-8859-1?q?at_=CE=C1_altlinux=2Eru?=
Пн Сен 10 03:48:27 MSD 2007


On Mon, Sep 10, 2007 at 02:27:11AM +0300, Led wrote:
> > Этот пакет содержит некоторое количество tcl скриптов.
> > $ rpmfile /ALT/Sisyphus/files/noarch/RPMS/TclTutor-2.0-alt0.2.noarch.rpm |grep tcl
> > /usr/bin/TclTutor.tcl   100755  Bourne shell script text executable
> > /usr/bin/tcltutor       120777  symbolic link to `TclTutor.tcl'
> > /usr/share/TclTutor/htmllib.tcl 100644  Bourne shell script text executable
> > /usr/share/TclTutor/scaler.tcl  100644  ASCII English text
> > $
> >
> > Эти скрипты, по-видимому, не требуют никаких tcl пакетов.
> 
> Там вот такое:
> $ head -3 /usr/bin/TclTutor.tcl
> #!/bin/sh
> # \
> exec wish "$0" "$@"
> 
> Наветное, имеет смысл заменить на явное
> #!/usr/bin/wish
> ?

Здесь есть два подхода.

Во-первых, можно написать shebang fixer.  То есть чтобы на стадии fixup,
перед поиском зависимостей, совершенно автоматически выправлять все
кривые шебанги.  Но этот фиксер будет эвристическим, и если он
облажается, то это будет пшик и автору фиксера дадут подзатыльник.
Подумайте: на самом деле придётся писать full-fledged шелл-парсер.
Ведь "$0" "$@" это простейший вариант, а любая менее тривиальная
манипуляция с аргументами может ввести фиксер в глубокий ступор.
В лучшем случае он просто ничего не зафиксит.  А в худшем случае
зафиксит так что мало не покажется.

Кроме того, есть такие случаи, которые зафиксить принципиально нельзя,
и которые, вместе с тем, имеют свой глубокий смысл.

$ head -3 /usr/lib/rpm/tcl.req
#!/bin/sh
# -*- tcl -*- \
exec ${RPM_TCLSH:-/usr/bin/tclsh} "$0" "$@"
$

Поэтому я решил, что можно реализовать менее навязчивые варианты.
В частности, в новом rpm-build уже реализовано распознавание
/usr/bin/env для всех типичных случаев.  В частности,
релевантная часть анализатора шебанга (shebang.req) выглядит так:

	local f="$1" line=; shift
	line=$(sed -n '1s|^#![[:space:]]*/|/|p' "$f")
	[ -n "$line" ] || return 0
	set -- $line
	case "$#,$1" in
		2,/usr/bin/env)
			FindPackage "$f" "$1" "$2" ;;
		*)
			FindPackage "$f" "$1" ;;
	esac

То есть скрыть зависимость на интерпретатор через /usr/bin/env
в типичном случае (если всего один аргумент у env) уже нельзя.

Теперь только остаётся придумать, что делать с re-exec'ом.
В принципе, подход тоже есть, он реализован в shell.req:

	if ! reqs="$($sh --rpm-requires "$f")"; then
		# sh --rpm-requires failed, and stderr is already there.
		# We are almost dead.  The last chance to escape is to see
		# if the shell is used only to re-exec another interpreter, e.g.
		#	exec tclsh "$0" "$@"
		if line1=$(egrep -m1 -v '^[[:space:]]*(#|$)' "$f"); then
			set -- $line1
			if [ $# -gt 1 ] && [ "$1" = exec ]; then
				Info "$f is $2 script!"
				FindPackage "$f" "$2"
				return 0
			fi
		fi
		Fatal "$f: $sh --rpm-requires failed"
	fi

Проблема тут в том, что этот код нельзя сделать вполне универсальным,
то есть реализовать на его основе диспетчерезацию на другой тип
зависимостей.  Но можно просто вставить аналогичный код в tcl.req.files.
Кажется, это единственный язык, который в котором любят делать
шебанг-хак через re-exec в первой значащей строчке.
----------- следующая часть -----------
Было удалено вложение не в текстовом формате...
Имя     : =?iso-8859-1?q?=CF=D4=D3=D5=D4=D3=D4=D7=D5=C5=D4?=
Тип     : application/pgp-signature
Размер  : 189 байтов
Описание: =?iso-8859-1?q?=CF=D4=D3=D5=D4=D3=D4=D7=D5=C5=D4?=
Url     : <http://lists.altlinux.org/pipermail/devel/attachments/20070910/3989cdb2/attachment-0002.bin>


Подробная информация о списке рассылки Devel