LINUX.ORG.RU

Как из C++ вызвать shell-скрипт и получить экспортированные им переменные?

 ,


0

1

Нужно из программы на C++/Qt вызвать скрипт, подождать, пока он выполнится, и после этого заполучить себе все переменные окружения, которые он выставил через export. Если есть способ сделать это красиво с помощью QProcess, QProcessEnvironment и прочих кьютишных плюшек, то будет вообще супер. Если нет — то нужен любой другой способ как-нибудь такое реализовать.

Нет, ну почитайте уж доку по упомянутым вами QProcess и QProcessEnvironment. У QProcessEnvironment есть метод value, который возвращает значение переменной окружения по её имени. С помощью QProcess можно дождаться окончания выполнения задачи.

Y ★★
()

Создать враппер-скрипт, который сам вызовет целевой скрипт, только в этом же интерпретаторе (вроде бы через точку или source, щас подскажут, если чо не так). После выполнения целевого враппер должен вызвать env, а ты с его stdout'а это дело забрать.

wrapper.sh:

#!/bin/sh

. script.sh
env
script.sh:
#!/bin/sh

ASD=QWE
export ASD

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

Да, естественно, есть, но возможности получить изменённую копию после запуска что-то не видно.

QProcessEnvironment QProcess::processEnvironment() const

Returns the environment that QProcess will use when starting a process, or an empty object if no environment has been set using setEnvironment() or setProcessEnvironment(). If no environment has been set, the environment of the calling process will be used.

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

Если честно, хочется сделать в своём приложении возможность скриптования на произвольных языках, поддерживаемых в системе. Если бы я хотел ограничиться только шеллом, то, конечно, можно приписывать в конце printenv и парсить, но ведь вместо шелла может оказаться, скажем, питон.

Но в качестве запасного варианта пока держу именно такую идею: заточить всё на поддержку только шелла.

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

Вот меня как нуба это и удивляет: тот же интерпретатор может «вернуть» переменную в окружение родителя через export, а между двумя разными процессами такое обычно не реализуемо? То есть export —— это особый костыль внутри sh-подобных интерпретаторов, а не общая фича для взаимодействия между родителями и детьми? Если так, то печальненько.

А ещё, кстати, кто-нибудь посоветует что-то почитать про то, как этот export реализован? Хотя бы в нужный файл в исходниках баша ткните, но лучше, конечно, в хорошую статью. Сам не нашёл пока.

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

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

как бы не получится обновить env текущего процесса, не?

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

Если честно, хочется сделать в своём приложении возможность скриптования на произвольных языках, поддерживаемых в системе.

Вообще, какой-то кривой путь. Почему именно переменные окружения?

Лучше dbus какой-нибудь заюзай: оно специально для этого сделано, и биндинги есть к куче языков

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

тот же интерпретатор может «вернуть» переменную в окружение родителя через export

какета может?

[user@submeow ~]$ echo $VAR

[user@submeow ~]$ bash -c 'export VAR=lol'
[user@submeow ~]$ echo $VAR

[user@submeow ~]$ 
derlafff ★★★★★
()
Ответ на: комментарий от greatperson

процесс A создает процесс B

процесс B (интерпритатор bash/sh/etc.) выполняя скприт делает export MY_VAR

експорт дальше, чем на процесс B не распространяется вообще

процесс A должен подчитать переменную окружения MY_VAR процесса B? так емним нету для этого средств как таковых.

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

Через точку/source не запускается второй интерпретатор, это тот же sh засасывает сорец и выполняет его в себе. Окружение вещь условная, во-первых, на низком уровне окружение передается процессу явно от родителя (execve). Дочерний процесс хоть и использует getenv()/setenv(), это все равно массив где-то в его памяти, поэтому получить к нему доступ извне нельзя (если ошибаюсь, а это вряд-ли, кто-нить да поправит). Локальные переменные шелла это просто другой массив (как и обычные переменные питона или с++, например). Когда ты говоришь export, шелл просто удаляет переменную из своего массива и делает setenv(), который потом естественным образом передастся в execve, и дочерние получат экспортированные значения. А не-экспортированные — не получат.

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

Мне кажется, так пользователю проще без лишних запар встроить скриптики.

  • Моя программа делает что-то
  • Вызывается юзерскрипт prepare.sh (или prepare.py, да хоть prepare.bin), если нужно, выставляет переменные, типа «я создал временную директорию вон там» и т.п.)
  • Моя программа делает основную работу
  • Вызывается юзерскрипт cleanup.sh (cleanup.py, cleanup.bin), которому могут понадобиться переменные из первого скрипта

Но согласен, что есть и другие способы решения проблемы (например, взвалить на плечи пользователя заботу о наличии printenv в конце, или вообще писать во временный файл. Субъективно мне вариант с переменными казался самым интуитивным для скриптующего, поэтому и пытаюсь понять, возможен ли он.

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

Сорри за тупняк, значит, действительно я нереального хочу.

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

С питонами такое не прокатит. Разве что под gdb запускать и ловить на выходе из main().

arturpub ★★
()

Такой финт ушами возможен только при выставлении задержки перед выходом из дочернего процесса, чтобы было достаточно времени прочитать и распарсить его /proc/PID/env (или environment?).

anonymous
()

не эксперементировал, но по идее можно в обработчике сигнала SIGCHLD считать текущее окружение дочернего процесса (из /proc) - он как раз в этот момент zombie - то есть завершён, но не все ресурсы освобоженны.

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

Думаю, в итоге сделаю так: буду запускать скрипт пользователя, предварительно добавляя в PATH путь к паре своих команд, типа set_my_cool_var и read_my_cool_var. При этом я буду плевать на то, из какого языка пользователь как-то да вызовет эти бинарники, ну а пользователь будет плевать на то, как эти команды реализованы.

Пожалуй, они будут реализованы через свой файл в /tmp, ибо лень с этими вашими ди-басами возиться.

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

Почему бы скрипту prepare.* вместо установки переменной не сказать echo «did-mkdir $path», а тебе не поймать это с его stdout или другого дескриптора?

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

имхо, такие вещи на баше и писать, но не на qt

но

пусть твоя программа вызывает скрипт prepare.sh

в конце prepare.sh должен выполнять $NEXT из переменных окружения, передаваемый твоей программой

следующий бинарь, $NEXT — твой, пусть делает основную работу. общаться между qt-процессами — проще простого

дальше пусть он, сохраняя переменные вызывает cleanup.sh, и, если нужно, в конце последний твой бинарь

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

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

А через другой дескриптор — это да, это тоже можно попробовать. Спасибо!

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

Свои костыли через set_my_cool_var в темповый файл тебе дороже встанут, поверь. Если не хочешь возиться с IPC, хотя чего там возиться, возьми любой встраиваемый язык, тот же питон, перл, луа, лишп (шучу), в зависимости от того, что там за дела в скриптах, и экспортируй/забирай переменные/функции естественным образом. Шелл просто не встраиваемый.

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

Вот где-то тут уже и проходит тонкая грань между продвинутым пользователем и программистом. :D Я не хочу заставлять пользователей переходить её.

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

Открываешь новый пайп, дескриптор передаешь в скрипт как аргумент или переменную.

echo "did-mkdir $path" >> $1
#echo "did-mkdir $path" >> $RESULT_FD
arturpub ★★
()
Ответ на: комментарий от arturpub

Поддерживаю. Также ещё делают именованный канал либо сокет, а путь передают через переменную окружения. Можно посмотреть пример в документации по uzbl (секция «EXTERNAL SCRIPTS»).

xaizek ★★★★★
()

Скинуть из shell-скрипта экспортируемые переменные в stdout, не?

anonymous
()

в продолжении Как из C++ вызвать shell-скрипт и получить экспортированные им переменные? (комментарий) , может ещё кому пригодится

для собственных поделок показалось полезным и нашлось время поэксперементировать, вывод: чтение /proc/PID/environ не поможет считывать окружение. Во первых при прохождении сигнала SIGCHLD файл уже недоступен, во вторых сам по себе файл содержит НАЧАЛЬНОЕ окружение и вызов setenv его не меняет.

единственный несовсем корявый способ считывать окружение произвольного дочернего процесса - подменять ему библиотечные вызовы setenv (опционально putenv, clearenv) и exit. НЕ ВСЕ интерпретаторы используют setenv или модифицируют environ - в качестве костыля можно перехватывать ещё execve, хотя идеалогически это неправильно

собсно сурс :

#define _GNU_SOURCE
#include <dlfcn.h>

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

static int (*real_setenv)(const char *name,const char *value,int owerwrite);
static void (*real_exit)(int status);

static char *file_to_save_env=NULL;

__attribute__((constructor))
static void envsave_init() {
	file_to_save_env=getenv("ENVSAVE");	
	real_setenv=dlsym(RTLD_NEXT,"setenv");
	real_exit=dlsym(RTLD_NEXT,"exit");
}

int setenv(const char *name,const char *value,int overwrite) {
	int fd;
	if (file_to_save_env!=NULL && name!=NULL) {
		fd=open(file_to_save_env,O_WRONLY|O_CREAT|O_APPEND,0600);
		if (fd!=-1) {
			write(fd,name,strlen(name));
			write(fd,"=",1);
			if (value!=NULL) write(fd,value,strlen(value));
			write(fd,"\n",1);
			close(fd);
		}
	}
	return real_setenv(name,value,overwrite);
}

__attribute__((noreturn))
void exit(int status) {
	extern char **environ;
	if (file_to_save_env!=NULL && environ!=NULL) {
		int fd;
		fd=open(file_to_save_env,O_WRONLY|O_CREAT|O_TRUNC,0600);
		if (fd!=-1) {
			int t;
			for(t=0;environ[t];t++) {
				write(fd,environ[t],strlen(environ[t]));
				write(fd,"\n",1);
			}
			close(fd);	
		}		
	}
	real_exit(status);
}

собирать : gcc -shared envsave.c -ldl -o envsave.so

юзать: LD_PRELOAD=./envsave.so ENVSAVE=envsave.txt programm args

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