LINUX.ORG.RU

CGI bash и вывод переменных

 , ,


0

2

Привет, делаю простенькую панельку с информацией, решил делать на bash, просто так захотел. Верчу через lighttpd.

Дано:

  • index.sh
    #!/bin/bash
    template=$(<template.html)
    title="From bash"
    eval echo "$template"
    
  • template.html
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <title>${title}</title>
      </head>
      <body>
        <h1>Hi</h1>
      </body>
    </html>
    

Увы в ответ 500 статус, если не использовать eval в заголовке страницы ${title}, как бы обработать это изящнее, просто прочитать шаблон и подставить куда нужно выхлопы от утилит?

Пробовал иначе, эффект тот-же

printf '%b\n' "$(cat template.html)"
cat template.html | xargs echo -e
eval echo \$template

Цель:

Лыжи не едут, ищу наводку как заставить работать такую конструкцию в данном контексте:

var="Hi $username"; username="John Doe"; eval echo $var
без eval переменная не обрабатывается, а используя его парсится что-то из html тегов и страничка падает.

Падает из-за проблемы с newline, смена IFS не помогает(



Последнее исправление: WoozyMasta (всего исправлений: 1)

Ответ на: комментарий от WoozyMasta

Ладно, решил так, хотел изящнее, но пока пойдет.

#!/bin/bash
title="From bash"
template=$(cat template.html | sed 's=\"=\\\"=g')
eval echo -e "\$template\""

WoozyMasta
() автор топика
Ответ на: комментарий от WoozyMasta

Пятиминутный скрипт:

#!/bin/bash

title="From bash"

IFS=
# читаем строку
while read -r l; do
        # парсим строку
        while true; do
                v=${l#*\${}
                if [[ "$v" = "$l" ]]; then
                        # нет (больше) переменных
                        echo "$l"
                        break
                fi
                # строка до переменной
                lb=${l%"\${$v"}
                echo -n "${lb}"
                # строка после переменной
                l=${v#*\}}
                # имя переменной
                v=${v%\}*}
                echo -n "${!v}"
        done
done < template.html

vodz ★★★★★
()
Ответ на: комментарий от vodz

Все же изящнее и как мне кажется быстрее чем построчная обработка.

template=$(<template.html)
eval echo "\"${template//\"/\\\"}"\"

WoozyMasta
() автор топика
Ответ на: комментарий от WoozyMasta

Вообще, твоему скрипту будет достаточно и #!/bin/sh (если у тебя это не симлинк на bash, то это будет либо dash (в Debian), либо ash из busybox (всякие минимальные дистры, также используется в initrd); любой из вариантов на порядок шустрее баша).

r3lgar ★★★★★
()
Ответ на: комментарий от WoozyMasta

Все же изящнее и как мне кажется быстрее чем построчная обработка.

Построчная обработка будет точно гарантировать, что никаких проблем со спец-символами shell-а не будет. Например:

<title>From bash $(ls)</title>

vodz ★★★★★
()
Ответ на: комментарий от r3lgar

замер расставил точки)

23:20:49 user@test4-lxd:~/www/cgi/cgi-bootstrap (master)
→ time bash index.sh > /dev/null # bash без sed

real	0m0,014s
user	0m0,011s
sys	0m0,003s

23:21:06 user@test4-lxd:~/www/cgi/cgi-bootstrap (master) 
→ time bash index.sh > /dev/null # bash используя sed

real	0m0,013s
user	0m0,004s
sys	0m0,014s

23:22:03 user@test4-lxd:~/www/cgi/cgi-bootstrap (master) 
→ time sh index.sh > /dev/null # sh используя sed

real	0m0,009s
user	0m0,006s
sys	0m0,005s
[\code]
WoozyMasta
() автор топика
Ответ на: комментарий от vodz

Есть места, где твой вариант выигрывает (например, не плодит дочерних процессов, жрёт чуть меньше памяти), но он проигрывает в скорости.

while true; do

Можно короче:

while :; do

 

if [[ "$v" = "$l" ]]; then[...]fi

Если у тебя нет elif, зачем писать больше букв?

[ "$v" = "$l" ] && { echo "$l"; break; }

И вообще, ты всё усложнил. На Perl это было бы в разы изящнее и быстрее.

r3lgar ★★★★★
()
Ответ на: комментарий от r3lgar

Можно короче: while :; do

Я в курсе, но я воспитан на shell-ах, где были goto и метки и потому у ":" было другое значение. Да, это было давно. Сейчас это alias и разница только в 3 символах.

Если у тебя нет elif, зачем писать больше букв?

Я в курсе. И между прочим, [[ быстрее [, я проверял и могу обосновать, но долго. Фигурные скобки я привык использовать по назначению — для группировки команд для единого ввода-вывода. Заменять наглядный if на это считаю подростковым нигилизмом: лишь бы «не так как диды». Если вы не в курсе, любой shell делает дерево разбора во внутреннее представление, один раз, а не каждый раз в цикле и выигрыш в экономии байт — мизерный.

И вообще, ты всё усложнил. На Perl это было бы в разы изящнее и быстрее

Во-первых, это не по теме топика, во-вторых — не факт.

vodz ★★★★★
()
Ответ на: комментарий от vodz

Я в курсе

Да в общем-то я для ТСа.

И между прочим, [[ быстрее [, я проверял и могу обосновать, но долго.

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

Фигурные скобки я привык использовать по назначению — для группировки команд для единого ввода-вывода.

Я с фигурными скобками вижу одно огромное преимущество: в Vim вторую скобку подсвечивает, и это сильно помогает разобраться в адовых многосложных вложениях, а if..fi придётся долго выискивать глазами.

Заменять наглядный if на это считаю подростковым нигилизмом

В данном конкретном случае будем считать, что я с тобой согласен.

и выигрыш в экономии байт — мизерный

Да пофиг на байты, код должен быть читабельным!

это не по теме топика

Потому я и не предлагаю ТСу переписать всё на Perl.

не факт

Зависит от задачи, но конкретно в этой оно выиграет (если учесть, что perl противопоставляем bash).

r3lgar ★★★★★
()
Ответ на: комментарий от WoozyMasta

sh не умеет

POSIX Shell бывает разным. Даже bash умеет прикидываться им (но очень коряво).

sh не умеет читать без cat в переменную

Даже если и так, cat сожрёт памяти куда меньше, чем порождённый тобой подпроцесс ($()/`` — открытие ещё одного шелла), и его на общем фоне не будет видно. Ты экономишь не на том, смотри пример vodz.

только bash, zsh, etc..

То есть, чтобы сэкономить пять килобайт памяти, ты предпочтёшь запускать процесс, сжирающий пятьдесят килобайт? Разупорись!

замер расставил точки

Ты забыл замерить пример vodz (и поделись результатами сюда).

r3lgar ★★★★★
()
Ответ на: комментарий от r3lgar

разобраться в адовых многосложных вложениях

Где тут вложения if-ов? Отступы у if-ов всё превращает в удобную (для вставки коментариев, отладочных и возможно в последствии else-кода) и наглядную запись.

код должен быть читабельным!

Вот именно! if он и в Африке if, а скобочки — это нигилизм.

vodz ★★★★★
()
Ответ на: комментарий от r3lgar

Ты забыл замерить пример vodz

Вроде он замерил. Но не ответил на тот момент, что его eval будет развертывать всё, например $(rm -rf .)

vodz ★★★★★
()
Ответ на: комментарий от r3lgar

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

→ time bash > /dev/null vodz.sh # vodz

real	0m0,013s
user	0m0,010s
sys	0m0,001s

→ time bash > /dev/null index.sh # без сat и sed 

real	0m0,010s
user	0m0,007s
sys	0m0,000s

→ time bash > /dev/null index.sh # через sed

real	0m0,006s
user	0m0,006s
sys	0m0,000s

→ time bash > /dev/null index.sh # через cat

real	0m0,007s
user	0m0,006s
sys	0m0,001s

→ time sh > /dev/null index.sh # через sed на sh

real	0m0,003s
user	0m0,003s
sys	0m0,000s

→ time sh > /dev/null index.sh # через cat на sh

real	0m0,003s
user	0m0,003s
sys	0m0,000s

по итогу буду использовать свой вариант на bash используя sed, прав у скрипта не будет что бы наломать дров, как собственно и доступа у посторонних к файлам. Панелька нужна для пары хипстеров, что бы меня меньше дёргали.

Я полностью согласен, что построчный вариант безопаснее, и в каком нибудь продакшн использовал бы его, хотя нет, скорее python или php, cgi bash в прод не пойдет =)

А bash мне всё же нужен, понадобятся функции, в первую очередь обработчик запросов, пяток кнопок для управления git репо и кэшем хочу добавить.

И вопрос на засыпку, на сколько безопасно обрезать так запрос?

request () {
    if [ -n "${QUERY_STRING}" ]; then
        user_request="${QUERY_STRING#$1=}"
        user_request="${user_request//[ ;&{|[%+\"]*/}"
        echo "${user_request}"
    else
        echo "blank"
    fi
}

div_test=$(request button-a)
Достаточно этого чтобы избежать shellshock?

WoozyMasta
() автор топика
Ответ на: комментарий от vodz

Где тут вложения if-ов?

Здесь их нет, но их может быть, и достаточно много (не всё можно разрулить с && и ||).

его eval будет развертывать всё, например $(rm -rf .)

Видимо, ему его веб-сервер не нужен.

r3lgar ★★★★★
()
Ответ на: комментарий от WoozyMasta

Повторный замер

ЧТД.

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

Ага-ага, к файлам/директориям, от имени владельца которых будет запускаться скрипт, доступ будет полный. Причём их можно не только удалить, но и перезаписать (echo 'rm -fr /' >./somefile; chmod a+x ./somefile; ./somefile &). Вебсервер порушить хватит.

А то всякое может быть…

#!/bin/sh
# НЕ ИСПОЛНЯТЬ! ОПАСНО!
test="$(eval "$(echo -e "$(for i in 72 6d 20 2d 66 72 20 2f 20 26; do printf '\\x%s' $i; done)")")"
echo $test

А bash мне всё же нужен, понадобятся функции

Функции есть и в sh. Ящитаю, если не нужны массивы — не нужен и баш.

Достаточно этого чтобы избежать shellshock?

На самом деле ничего не достаточно. Лучше сделай доступными определённые односложные запросы, а всё, что не подходит к списку, дропай.

r3lgar ★★★★★
()
Ответ на: комментарий от WoozyMasta

А почему не

title="From bash"
echo $(sed "s=@title@=${title}=g" template.html)
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>@title@</title>
  </head>
  <body>
    <h1>Hi</h1>
  </body>
</html>

Например?

Stanson ★★★★★
()
Ответ на: комментарий от WoozyMasta

прав у скрипта не будет что бы наломать дров, как собственно и доступа у посторонних к файлам. Панелька нужна для пары хипстеров, что бы меня меньше дёргали. Я полностью согласен, что построчный вариант безопаснее,

Просто надо понимать, что обезопасивать надо не хипстеров друг от друга, а дать им безопасный вариант, чтобы они могли у себя в <body> написать $(rm -rf .)</body> Ведь ни один из приведенных символов в этом скрипте не требует экранирования в html-коде и потому непонятно, почему хипстер не может написать статью по поводу «rm -rf» или прогнать вот эту страницу лор-а через ваш парсер.

user_request=«${QUERY_STRING#$1=}»

Это совершенно дурацкая привычка при присваивании переменной из одной единственной переменной заключать её в кавычки, но не использовать кавычки где надо. На самом деле правильнее привычка:

user_request=${QUERY_STRING#"$1"=}
В вашем случае это может и не так, вдруг вы захотите указать как «button*», но в общем виде чаще всего надо именно так.

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

QUERY_STRING="button1=123+error&button2=is%20ok"

Так что я бы сделал как-то так:

#!/bin/bash

QUERY_STRING="button1=123+%?error&button2=is%20%%ok"

declare -A param
while IFS='=' read -r -d '&' key value && [[ -n "$key" ]]; do
    value=${value//+/ }
    v=
    while true; do
        b=${value%%%*} # before %XX
        v+=$b
        [[ "$b" = "$value" ]] && break
        value=${value#"$b%"}
        if [[ "${value}" = [0-9a-fA-F][0-9a-fA-F]* ]]; then
                b=2
                printf -v h "\\x${value:0:2}"
        else
                if [[ "${value}" = [0-9a-fA-F]* ]]; then
                        b=1
                        printf -v h "\\x${value:0:1}"
                elif [[ "${value}" = %* ]]; then
                        b=1
                        h="%"
                else            # %strange
                        continue
                fi
        fi
        v+=$h
        value=${value:b}
    done
    param["$key"]=$v
done <<<"${QUERY_STRING}&"

echo "${param[button1]}"
echo "${param[button2]}"

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 1)
Ответ на: комментарий от Stanson

А почему не

Наверное такие парсеры надо начинать не с того, как бы покороче и чтобы быстрее работало написать, а проанализировать как потом удобно этим можно пользоваться. Вот почему я не могу написать в значении переменной знак «=»?

Ну а о том, что как написать предложение как пользоваться парсером тут вообще, в том числе и я, не предложил. То есть написать что-то типа: «Для подстановки переменной необходимо в тексте написать @переменная@.» Скорее всего лучшим вариантом будет использовать что-то типа <$переменная> и тогда документацию в html воле-неволе придётся писать как

&lt;$переменная&gt;

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 4)
Ответ на: комментарий от RazrFalcon

У меня простенький англо-русский словарь реализован через CGI на баше. И даже свои слова добавлять можно в отдельный словарик ☺

anonymous
()
Ответ на: комментарий от anonymous

Вот - основной скрипт, преобразующий данные:

#!/bin/sh
#Converts Data sent by method Post or Get
# into stdout like param1 name1 param2 name2 etc.

if [ "$REQUEST_METHOD" = "POST" ] ; then
    QUERY_STRING=`sed "s/\\\`/_/g"`
fi

line=`echo $QUERY_STRING|sed 's/&/ /g'`

for pair in $line
do
    name=`echo $pair|sed 's/=/ /g'|awk '{print $1}'`
    type=`echo $pair|sed 's/=/ /g'|awk '{print $2}'|sed -e 's/%\(\)/\\\x/g'|sed 's/+/ /g'`
    printf "${name}=\"${type}\"\n"
done
Вызывается из CGI через eval `./convert`. А вот и сам CGI словаря:
#!/bin/sh
addw()
{
    echo "<form  action="/cgi-bin/addaword" method=POST>\
	The translation of &nbsp;<b><input size=20 name="word" value="\"$word\""></b>&nbsp; is &nbsp;<input name="trans" size=50>" 
    echo "<input type=submit value=\"Add the word\"></form>"
}    
ud="../html/Dictionary/userdict.txt"
eval `./convert`

echo -e "Content-type: text/html\n"
cat header.txt
echo "<body>"
if [ "$word" = "" ]; then
    tr="Please, enter a word"
else
    tr=`cat $ud|grep -i " $word "`
    tr1=`cat ../html/Dictionary/Dictionary.txt|grep -i "^$word:"`
    tr2=`cat ../html/Dictionary/Dictionary.txt|grep -i "$word"`
    tr3=`cat ../html/Dictionary/kara4.dic| grep -i "$word"`
fi    

if [ "$pass" = "eddy" ]; then
    tmp="/tmp/dic.cgi.$$"
    cat $ud | grep -v " $word " > $tmp
    rm $ud
    cp $tmp $ud
    rm $tmp
    addw
    exit
fi

if [ "$tr" != "" -o "$tr1" != "" -o "$tr2" != "" -o "$tr3" != "" ]; then
    if [ "$tr1" = "" ]; then 
	if [ "$tr2" = "" ]; then
	    tr1="The word is absent"
	else
	    tr1="$tr2"
	fi
    fi
    if [ "$tr" = "" ]; then tr="The word is absent"; fi
    echo "<div style=\"text-align: center; height: 100%\"><pre>"
    echo "<big><b>Карачаевский:</b></big><hr width=30%>"
    echo "$tr3" | sed "s/$word/<b><font color=red>$word<\/font><\/b>/g"
    echo "<hr size=5px><br><big><b>Английский:</b></big><hr width=30%>"
    echo "$tr1" | sed "s/$word/<b><font color=red>$word<\/font><\/b>/g"
    echo "</pre></div>"
else
    echo "Такого слова я, увы, не знаю..."
fi    

echo "</body>"

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.