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 ★★★★★
()
Вы не можете добавлять комментарии в эту тему: топик перемещен в архив.