LINUX.ORG.RU

Помоги перенести скрипт bash -> sh

 , ,


2

1

Здравствуй, мой дорогой, уважаемый друг!

У меня возникили трудности при написании скрипта на shell, я так сильно привык к bash, что немного вошел в ступор и надеюсь на твою помощь.

Задача

Имеем:

export APP_NAME='test'
export APP_DESCRIPTION='Test app'
export APP_VERSION='beta'
export APP_COMMIT_HASH='000000000'
# и черт пойми еще какие APP_* могут быть
export PROJECT_DIR='./src'

На bash-е сделал так:

#!/bin/bash
for k in ${!APP_*}; do
  grep -q "^$k" "$PROJECT_DIR/.env" && \
    sed -i "s|^$k=.*|$k=${!k}|" "$PROJECT_DIR/.env" || \
    printf '%s=%s\n' "$k" "${!k}" >> "$PROJECT_DIR/.env"
done

Очень нужно перенести на shell, там где это будет работать - товарищ майор запрещает использовать bash. На скорую руку сделал так:

#!/bin/sh
vars=$(env | grep -o '^APP_.*=' | tr -d '=')
for k in $vars; do
  eval "v=$k"
  grep -q "^$k" "$PROJECT_DIR/.env" && \
    sed -i "s|^$k=.*|$k=$v|" "$PROJECT_DIR/.env" || \
    printf '%s=%s\n' "$k" "$v" >> "$PROJECT_DIR/.env"
done

Проблема

  • eval == зло
  • сложное получение списка названий переменных

Изящный способ то существует?

Скорее всего env и eval можно заменить на set, но решил спросить у тебя, может быть подскажешь что-то более практически и эстетически верное.

Решение

Всем спасибо за помощь!

Пока оставил в таком виде:

#!/bin/sh
cat /proc/self/environ | tr -d '\n' | tr '\0' '\n' | \
grep '^APP_[a-zA-Z0-9_-]*=.*$' | \
while IFS='=' read -r k v; do
  grep -q "^$k" "$PROJECT_DIR/.env" && \
    sed -i "s|^$k=.*|$k=\"$v\"|" "$PROJECT_DIR/.env" || \
    printf '%s="%s"\n' "$k" "$v" >> "$PROJECT_DIR/.env"
done



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

У тебя скрипт на баше нерабочий. Что тебе перенести?

Ты пиши, что ты хочешь сделать, а нерабочие скрипты, неизвестно что делающие.

anonymous
()

Между прочим, аноним даже прав, работает ли скрипт я даже не проверял, но брать первые попавшиеся переменные, начинающиеся на APP_ и на этом делать какие-то телодвижения и катить бочку на eval — двойные стандарты.

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

Он хочет обновить содержание $project_dir/.env в соответствии с текущим окружением. Но он не может это написать словами, потому что сам не знает что хочет.

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

Аноним прав, я не указал для sed файл с которым ему нужно работать, исправил. Извините, допустил ошибку.

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


Хорошо, давайте отбросим всё остальное. И подскажите как конкретно эту часть лучше всего перенести на shell.

sed -i "s|^$k=.*|$k=${!k}|" "$PROJECT_DIR/.env"
WoozyMasta
() автор топика

env | grep -o '^APP_.*='

Для начала, переменные могут содержать переводы строки. И ты получишь не то, что ожидалось:

$ export APP_V1="xxx"
$ export FAKE='
> APP_V2=yyy'
$ env | grep -o '^APP_.*='
APP_V1=
APP_V2=
$

Поэтому что-то типа:

$ env -0 | tr '\0\n' '\n#' | grep -o '^APP_.*='
APP_V1=
$
wandrien ★★
()

eval == зло

Почему?

Вместо '^APP_.*=' напиши '^APP_[a-zA-Z0-9_-]*=', и eval будет предсказуемый.

printf '%s=%s\n'

Здесь проблема с экранированием, точнее, с его отсутствием. И что-то я сходу не подскажу, как её решить самым простым обазом. Видимо, на sed сделать экранизатор.

Посмотрим, что подскажут. Самому на днях надо примерно такой же править.

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

Не, я сразу от этого отказался, там чет еще все костыльнее в реализации получается.

  • Хз сколько будет на входе переменных APP_*
  • Есть куча других переменных которые могут быть в .env файле, и мне они не нужны, нужны только APP_*
  • Если переменной нет, её нужно дописать в файл
  • Нельзя плодить дубли переменных

Текущее решение куда проще смотрится.

WoozyMasta
() автор топика

Самое непонятное здесь то, откуда берется пачка export'ов и почему ее нельзя сразу писать в project/.env, а потом (если надо) тащить их оттуда скриптом в переменные среды.

В остальном

#!/bin/sh

VNAMEPAT="^APP_"

for S in `env | grep "$VNAMEPAT"`; do
        VNAME=`echo "$S" | cut -d= -f1`
        VVALUE=`echo "$S" | cut -d= -f2`
done;
Временные файлы, промежуточные переменные и сед-магия по вкусу.

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

Зачем там eval, если тебе env уже отдал и имя переменной, и ее значение? Не думаю, что там что-то хитрожопое со спецсимволами, поэтому cut в помощь.

anonymous
()
Ответ на: комментарий от anonymous
VVALUE=`echo "$S" | cut -d= -f2-`

так правильнее.

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

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

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

нужны только APP_*

К сожалению, в отличии от bash в более простых sh-ах зачастую нет команды declare, а export -p всё равно отдаёт полный список всех переменных. Это лучше, чем env — и нет нового процесса и есть безопасное экранирование. Но есть та же проблема, что тут указали с непечатными и в особенности с переводами строк.

Так что, по-моему, правильнее список конкретных переменных, какой-нибудь APP_VAR_LIST по типу PATH, это правильнее с точки зрения и безопасности и мусора. Уж сами имена не будут содержать кривые символы, а содержимое отправлять как есть, не парся. Надеюсь вам позволительно писать /bin/printf «%s=%q».

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

/bin/printf "%s=%q"

Ептать!

Спасибо, что напомнили. Я почему-то думал, что %q — частная фича bash. Совсем забыл, как оно на самом деле.

Это всё упрощает.

wandrien ★★
()

Одно не понЯл: нафик эспортировать переменные в среду ? Из какогото сраного скрипта?

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

Надеюсь вам позволительно писать /bin/printf «%s=%q».

Увы нет printf: %q: invalid directive

Касаемо списка переменных я согласен, но данный кейс будет работать в chroot на системе без systemd и пользовательских сессий, и так сойдет, ведь весь набор APP_* создается мною подконтрольными скриптами и сторонних появиться не должно, а вот итоговое количество APP_* переменных мне не известно :)

Пока оставил в таком виде:

#!/bin/sh
env -0 | tr '\0\n' '\n ' | grep '^APP_[a-zA-Z0-9_-]*=.*$' | \
while IFS='=' read -r k v; do
  grep -q "^$k" "$PROJECT_DIR/.env" && \
    sed -i "s|^$k=.*|$k=\"$v\"|" "$PROJECT_DIR/.env" || \
    printf '%s="%s"\n' "$k" "$v" >> "$PROJECT_DIR/.env"
done

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