1

Тема: JScript: Проблема с WshScriptExec.Exec

Хотел сделать расширенный аналог функции поиск файлов по расширению. Однако столкнулся с проблемой. Следующий скрипт не завершает выполнение на определенном каталоге (см. пример ниже). "Уничтожение" процесса завершает скрипт. Скрипт нормально работает если ввести уточнение шаблона, например, x* (около 20 файлов). Это проблема только у меня (Windows Vista)?


var cmd = 'cmd /c dir /b /a-d "C:\\Windows\\System32\\*"';

var sh = new ActiveXObject('WScript.Shell');
var ex = sh.Exec(cmd);

while ( ex.Status == 0 ) {
	WScript.Sleep(100);
}

WScript.Echo(ex.StdOut.ReadAll());
( 2 * b ) || ! ( 2 * b )

2

Re: JScript: Проблема с WshScriptExec.Exec

Попробовал. У меня так же повисает и ждёт закрытия окна (Windows 7 Professional x32). Помогла только добавка Terminate. Но скорее всего это оборвёт перебор файлов в каталоге... Так что не вариант.

var cmd = 'cmd /c dir /b /a-d "C:\\Windows\\System32\\*"';
 
var sh = new ActiveXObject('WScript.Shell');
var ex = sh.Exec(cmd);
 
ex.Terminate();

while ( ex.Status == 0 ) {
    WScript.Sleep(100);
}
 
WScript.Echo(ex.StdOut.ReadAll());
Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

3

Re: JScript: Проблема с WshScriptExec.Exec

Да. Terminate может прервать выполнения команды и, соответственно, перебор файлов. Если убрать цикл проверки статуса, то скрипт работает. Но это не гарантирует правильность его работы. Печально. А ведь это самый быстрый способ получения листинга каталога в отличие от скриптового перебора коллекции файлов/каталогов.

( 2 * b ) || ! ( 2 * b )

4

Re: JScript: Проблема с WshScriptExec.Exec

Сделал тест - вставил в цикле запись в файл ex.StdOut.ReadLine-а. Выяснил, что просто перебор идёт очень медленно. В итоге у меня это жевалось где то минут 5. Но потом штатно закрылось окно cmd и выдалось сообщение со списком файлов. Делаю вывод - просто надо дождаться окончания работы приложения. И видимо, как я и предположил - Terminate просто не даёт перебору завершиться нормально.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

5

Re: JScript: Проблема с WshScriptExec.Exec

Вот так работает. Быстро работает. Но надежно ли это?


var cmd = 'cmd /c dir /b /a-d "C:\\Windows\\System32\\*"';

var sh = new ActiveXObject('WScript.Shell');
var ex = sh.Exec(cmd);

var result = [];
while ( ! ex.StdOut.AtEndOfStream ) {
	result.push(ex.StdOut.ReadLine());
}
WScript.Echo(result.join('\n'));
WScript.Echo(result.length);
( 2 * b ) || ! ( 2 * b )

6

Re: JScript: Проблема с WshScriptExec.Exec

Боюсь, что не совсем надёжно. Дело в том, что StdOut.AtEndOfStream может принимать значение True, если Вы успели считать из него (буфера) все данные, а новые ещё не были положены. Такое может происходить в процессе перебора в любой момент. Поэтому мне кажется, что это равноценно обрыву по Terminate. Хотя могу ошибаться.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

7

Re: JScript: Проблема с WshScriptExec.Exec

Боюсь, что не совсем надёжно.

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

( 2 * b ) || ! ( 2 * b )

8

Re: JScript: Проблема с WshScriptExec.Exec

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

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

9

Re: JScript: Проблема с WshScriptExec.Exec

Говоря о промежуточном файле я имел в виду следующее


sh.Run(cmd + '>tmpfile', 0, true);

Я согласен, что первый код рабочий. Ведь на других каталогах он работает. Вот только почему он не работает на каталогах с большим количеством объектов (например, C:\Windows\System32 содержит более 2500 файлов).

( 2 * b ) || ! ( 2 * b )

10

Re: JScript: Проблема с WshScriptExec.Exec

Xameleon пишет:

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

15 минут ожидания... И скрипт не завершился.

( 2 * b ) || ! ( 2 * b )

11

Re: JScript: Проблема с WshScriptExec.Exec

Решил ещё раз потестировать. Убедился, что если из цикла убрать ex.StdOut.ReadAll или ex.StdOut.ReadLine, то окошко продолжает висеть. Стоит вернуть обратно - код срабатывает молниеносно.....


var fso = new ActiveXObject('Scripting.FileSystemObject');
var ts = fso.OpenTextFile("log.txt",2,1);
var cmd = 'cmd /c dir /b /a-d "C:\\Windows\\System32\\*"';
 
var sh = new ActiveXObject('WScript.Shell');
var ex = sh.Exec(cmd);
 
while ( ex.Status == 0 ) {
    ts.WriteLine(ex.StdOut.ReadAll());
    WScript.Sleep(100);
}
 
WScript.Echo('END');
Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

12

Re: JScript: Проблема с WshScriptExec.Exec

Без промежуточного файла я бы сделал так:


Dim sh, ex, tmp
Set sh = CreateObject("WScript.Shell")
Set ex = sh.Exec("cmd /c dir /b /a-d ""C:\windows\system32""")
Do while ex.Status = 0
	tmp = tmp & ex.StdOut.ReadAll
	WScript.Sleep 100
Loop

MsgBox tmp

Написал на VBS, т.к тоже самое на JS у меня падало на строке WScript.Echo(tmp);. Видимо текст длиной 45294 для Echo перебор. А замену MsgBox в JS я не знаю.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

13

Re: JScript: Проблема с WshScriptExec.Exec

Код #5 рабочий. Проверил на очень большом списке (более 19 тыс. файлов) и маленьком (2 файла). Вот его модификация:


var cmd = 'cmd /c dir /b /a-d /s "C:\\Windows\\System32\\*"';

var sh = new ActiveXObject('WScript.Shell');
var ex = sh.Exec(cmd);

var result = [];
while ( ex.Status == 0 ) {
	if ( ex.StdOut.AtEndOfStream ) {
		break;
	}
	result.push(ex.StdOut.ReadLine());
}

//WScript.Echo(result.join('\n'));
WScript.Echo(result.length);
( 2 * b ) || ! ( 2 * b )

14

Re: JScript: Проблема с WshScriptExec.Exec

Приветствую. Rumata, а чем не устраивает промежуточный файл?

Кстати, консоль можно прятать.

15

Re: JScript: Проблема с WshScriptExec.Exec

JSman, ничего не имею против временных файлов, но из "идеологических" соображений считаю неприемлемым одно действие дробить между разными процессами (получение списка в файл - один процесс, удаление файла - другой процесс).

( 2 * b ) || ! ( 2 * b )

16

Re: JScript: Проблема с WshScriptExec.Exec

Rumata, если решение готово, то предлагаю добавить в коллекцию :-)

17

Re: JScript: Проблема с WshScriptExec.Exec

JSman, имеется в виду поиск файлов? Пока еще нет. Решение пока сыровато и требует некоторой доработки. Добавлю, как только приду к выводу, что решение может быть опубликовано.

( 2 * b ) || ! ( 2 * b )

18 (изменено: shiz, 2011-06-02 01:38:03)

Re: JScript: Проблема с WshScriptExec.Exec

Удивительно, что такой простой вопрос вызвал такое мощное обсуждение!
Просто уберите из исходного примера ненужное ожидание

while ( ex.Status == 0 ) {
    WScript.Sleep(100);
}

- WshScriptExec.StdOut.ReadAll() всё равно будет ждать пока запущенное приложение не закроет свой выходной поток (во многих случаях это крайне неудобно, но не в этом).

Забыл пароль и потерял e-mail.

19

Re: JScript: Проблема с WshScriptExec.Exec

shiz, не делайте скоропалительных выводов. "Ненужное" ожидание необходимо, чтобы скрипт дождался окончания вызванного процесса. Из моих экспериментов я увидел, что скрипт не может дождаться окончания выполнения процесса. Простым удалением фрагмента кода ожидания проблема не решается. Хотя я и писал в #13, что код рабочий, но иногда он не прочитывает весь выходной поток процесса. То есть я столкнулся с ситуацией о которой говорил коллега Xameleon:

Xameleon пишет:

... StdOut.AtEndOfStream может принимать значение True, если Вы успели считать из него (буфера) все данные, а новые ещё не были положены. Такое может происходить в процессе перебора в любой момент...

( 2 * b ) || ! ( 2 * b )

20 (изменено: shiz, 2011-06-02 02:08:51)

Re: JScript: Проблема с WshScriptExec.Exec

Rumata пишет:

То есть я столкнулся с ситуацией о которой говорил коллега Xameleon:

Xameleon пишет:

... StdOut.AtEndOfStream может принимать значение True, если Вы успели считать из него (буфера) все данные, а новые ещё не были положены. Такое может происходить в процессе перебора в любой момент...

Коллега Xameleon был бы прав, если бы мы запустили приложение перенаправив его выходной поток в физический файл и одновременно производили бы чтение из этого файла.
В данном же случае это не так. Для проверки предлагаю создать 2-3 простейших скрипта:

- ведомый (test1.js)

var n=10;   //число повторов
var t=1000; //задержка между повторами, мс

for (var i=0; i<n; i++) {WScript.Sleep(t); WScript.Echo(i);}

- и запускающий его ведущий (в 2-х вариантах):

var WshShell=new ActiveXObject("WScript.Shell");
var WshExec=WshShell.Exec("CScript.exe //nologo test1.js");
WScript.Echo(WshExec.StdOut.ReadAll());
var WshShell=new ActiveXObject("WScript.Shell");
var WshExec=WshShell.Exec("CScript.exe //nologo test1.js");
var result="";
while (!WshExec.StdOut.AtEndOfStream) result+=WshExec.StdOut.ReadLine()+"\n";
WScript.Echo(result);

Так что ваши трудности, вероятно, связаны с чем-то другим.

Забыл пароль и потерял e-mail.

21

Re: JScript: Проблема с WshScriptExec.Exec

2 shiz: Посмотрел примеры. Благодарю. Видимо и правда я ошибался. Значит всё таки AtEndOfStream срабатывает один раз.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

22

Re: JScript: Проблема с WshScriptExec.Exec

Коллеги, спасибо за обсуждение. Кажется общими усилиями мы нашли причину проблемы.

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

Как это работает
1. Скрипт запускается из командной строки с или без ключа /GOOD или /BAD
2. Скрипт запускает самого себя с ключом /INIT чтобы вывести в выходной поток n-ное количество символов
3. Скрипт из п.1 читает выходной поток скрипта из п.2 и "собирает" его вывод в массив
4. По окончании выводит количество считанных байт.

Количество выводимых символов задается переменной n в первой строке скрипта. Примеры вызова:

// проверка "стандартного" алгоритма
cscript 1.js

// поверка "расширенного" алгоритма
cscript 1.js /ADV

необходимо варьировать значение переменной n. Мои результаты утверждают, что стандартный алгоритм "вешается" при размере выходного потока превышающем 4 КБ.

"Стандартный" алгоритм запускает внешний скрипт и ожидает его завершение (while WshScriptExec.Exec.Status == 0). После этого считывает выходной поток.

"Расширенный" алгоритм запускает внешний скрипт и ожидая его завершения считывает выходной поток. Так как чтение потока теоретически может завершиться раньше полной записи процессом в поток, выполняется повторное чтение потока до его конца. Такая схема гарантирует а) корректное завершение работы внешнего процесса; б) полное чтение его выходного потока.


// размер буфера в байтах
var n = Math.floor(1024 * 4);

// вывод заданного количества символов
// количество опеделяется переменной n
if ( WScript.Arguments.Named.Exists('INIT') ) {
	var s = new Array(n + 1).join('0');
	WScript.StdOut.Write(s);
	WScript.Quit();
}

// внутренняя команда вывода
var cmd = 'cscript //nologo ' + WScript.ScriptName + ' /INIT';

var sh = new ActiveXObject('WScript.Shell');
var ex = sh.Exec(cmd);

// массив для заполнения данными внутреннего скрипта
var result = [];
if ( WScript.Arguments.Named.Exists('ADV') ) {
	// "расширенный алгоритм
	while ( ex.Status == 0 ) {
		if ( ex.StdOut.AtEndOfStream ) {
			break;
		}
		result.push(ex.StdOut.ReadLine());
	}
	while ( ! ex.StdOut.AtEndOfStream ) {
		result.push(ex.StdOut.ReadLine());
	}
} else {
	// стандартный алгоритм (взят из документации MSDN)
	while ( ex.Status == 0 ) {
		WScript.Sleep(100);
	}
	while ( ! ex.StdOut.AtEndOfStream ) {
		result.push(ex.StdOut.ReadLine());
	}
}

// Вывод статуса внутреннего процесса и размера буфера
WScript.Echo(ex.Status);
WScript.Echo(result.join('').length);
( 2 * b ) || ! ( 2 * b )

23 (изменено: jite, 2011-06-06 03:51:37)

Re: JScript: Проблема с WshScriptExec.Exec

Win XP - то же поведение. До 4KB способ MSDN читает, после - виснет. Продвинутый способ работает хорошо, тестировались размеры буфера до 3 MB (42 сек. на Intel E7200)

Реального использования "дочитывания" буфера в продвинутом способе замечено не было.

Решил помониторить объемы считывания с хронометражом. Одна строка нулей читалась, как можно догадаться, все время одним куском. Для приближения к реальности дописал генератор строк случайной длины из случайных символов. Вот код:

// размер буфера в байтах
var n = Math.floor(1024 * 3);
var sNL = "\n"; //Символ(ы) переноса строки
// вывод заданного количества символов
// количество опеделяется переменной n
if (WScript.Arguments.Named.Exists('INIT')) {
  //var s = new Array(n + 1).join('0'); //Было
  var nLinesCount = 0;
  var nNewLineLen;
  var s = "";
  while (s.length < n) {
    nNewLineLen = Math.floor(Math.random() * 400); //Случайная длина строки 0...399 символов
    var ss = ""; //Строчка, заканчивающаяся переносом
    for ( var i = 0; i < nNewLineLen; i++) {
      var nCode = Math.floor(Math.random() * (123 - 33)) + 33; //Латинские + точки-звездочки-и-тд.
      ss += String.fromCharCode(nCode);
    }
    s += ss + sNL;
    nLinesCount++;
  }
  s = s.substring(0, n);
  WScript.StdOut.Write(s);
  WScript.Quit(nLinesCount);
}

// внутренняя команда вывода
var cmd = 'cscript //nologo ' + WScript.ScriptName + ' /INIT';

var sh = new ActiveXObject('WScript.Shell');
var ex = sh.Exec(cmd);

// массив для заполнения данными внутреннего скрипта
var result = [], times = [];
var t = new Timer();
if (WScript.Arguments.Named.Exists('ADV')) {
  // "расширенный алгоритм
  while (ex.Status == 0) {
    if (ex.StdOut.AtEndOfStream) {
      break;
    }
    result.push(ex.StdOut.ReadLine());
    t.Stop();
    times.push(t + "  " + result[result.length - 1].length + " символов");
  }
  while (!ex.StdOut.AtEndOfStream) {
    result.push(ex.StdOut.ReadLine());
    t.Stop();
    times.push("дочитывание " + t + "  " + result[result.length - 1].length + " символов");
  }
}
else {
  // стандартный алгоритм (взят из документации MSDN)
  while (ex.Status == 0) {
    WScript.Sleep(100);
  }
  while (!ex.StdOut.AtEndOfStream) {
    result.push(ex.StdOut.ReadLine());
    t.Stop();
    times.push(t + "  " + result[result.length - 1].length + " символов");
  }
}

// Вывод статуса внутреннего процесса и размера буфера
//vard(result, "Считанные блоки"); //Посмотреть генерируемый текст :)
vard(times, "Хронология чтений");
WScript.Echo("Скрипт-генератор " + (ex.Status ? ("завершен, сгенерировано строк: " + ex.ExitCode) : "не завершен"));
WScript.Echo("Символов прочтено " + result.join(sNL).length);
t.Stop();
WScript.Echo("Конец работы " + t);

//=== DEBUG_STUFF ==========================================================

/** Возвращает строку рекурсивного описания содержимого объекта oBj
 * @param sParentName - имя объекта, произвольное, по сути комментарий (можно опустить)
 * @param nEsting - максимальный уровень вложенности объектов/значений (можно опустить)
 */
function vardump(oBj, sParentName, nEsting) {
  var sNL = "\r\n"; // Перенос строки 
  var nEsting_max = 12; //Максимальный исследуемый уровень вложенности, против бесконечных ссылок на самого себя
  var nPadding = 2; //Структурный отступ сдвига потомков относительно родителей, в пробелах
  //Ограничивать ли слишком длинное отображение объекта в поле >значения<
  var bRestrictObjectVal = true, nRestrictWidth = 80, sCutSign = "...";
  var bShowOnlyOwnProps = false; //Показывать только собственные (уникальные) свойства объекта или все приобретенные
  var bShowFunctionsCode = false; //Показывать ли код функций
  if (sParentName == undefined) sParentName = "";
  if (isNaN(parseInt(nEsting))) nEsting = nEsting_max;
  var sRes = "";
  for ( var i = 0; i < nPadding * (nEsting_max - nEsting); i++)
    sRes += " ";
  if (nEsting >= 0) {
    var sPrototype = Object.prototype.toString.call(oBj); //[object НекийТип], вызов через ***
    sRes += sParentName + "  ";
    switch (typeof oBj) { //Описание элемента toString() и обрамление в соответствии с его типом 
    case "string":
      sRes += "\"" + oBj + "\"";
      break;
    case "function":
      if (sPrototype == "[object RegExp]") sRes += oBj + "  as function"; //В Chrome typeof => function
      else {
        var s = "" + oBj;
        var nIdx = s.indexOf(")") + 1;
        if (nIdx) s = s.substring(0, nIdx);
        else
          s = "function"; //Не должно произойти
        sRes += s;
      }
      break;
    case "object":
      if (Object.prototype.toString.call(oBj) == "[object Array]") { //Для массива - еще длину
        sRes += "Array[" + oBj.length + "]  ";
      }
      if (Object.prototype.toString.call(oBj) == "[object Date]" //Объекты с простым и информативным содерж. toString() 
        || Object.prototype.toString.call(oBj) == "[object RegExp]") {
        sRes += oBj;
      }
      else { //Вывод прочих объектов и массивов
        sRes += ">";
        var sOTS = ("" + oBj).replace(/\r/g, "\\r").replace(/\n/g, "\\n");
        if (bRestrictObjectVal) { //Если надо, подрезаем слишком длинные описания в строку
          var nMaxOTSLength = nRestrictWidth - sRes.length - 1; //-1 под "<"
          var nCutOSTLength = nMaxOTSLength - sCutSign.length;
          if (nCutOSTLength < 0) nCutOSTLength = 0; //Ну да, вылезет...
          if (sOTS.length > nMaxOTSLength) sOTS = sOTS.substr(0, nCutOSTLength) + sCutSign;
        }
        sRes += sOTS + "<";
      }
      break;
    default: //числа, спецзначения, логические
      sRes += oBj;
    }
    sRes += sNL;
    //Вывод содержимого объекта: строками следующего уровня иерархии
    var sEl;
    var sGetChildName =
      function(oParent, sParentName, sChildName) { //Формирование имени потомка
        var sFullChildName = sParentName;
        var bIsIndex = false;
        if (Object.prototype.toString.call(oParent) == "[object Array]") {
          var nIdx = parseInt(sChildName, 10);
          if (!isNaN(nIdx) && nIdx.toString() === sChildName.toString() && nIdx >= 0 && nIdx < oParent.length)
            bIsIndex = true;
        }
        if (bIsIndex) sFullChildName += "[" + sChildName + "]";
        else {
          if (sFullChildName != "") sFullChildName += "."; //Если есть что отделять, отделяем точкой
          sFullChildName += sChildName;
        }
        return sFullChildName;
      }; //sGetChildName

    if (Object.prototype.toString.call(oBj) == "[object RegExp]") { //В завис. от браузера typeof то object, то function
      sEl = "lastIndex";
      sRes += arguments.callee(oBj[sEl], sGetChildName(oBj, sParentName, sEl), nEsting - 1);
    }
    else { //Осмотрим внутренности
      switch (typeof oBj) {
      case "object": //Объекты и массивы
        if (!oBj) break; //null
        for (sEl in oBj) {
          if (bShowOnlyOwnProps && !oBj.hasOwnProperty(sEl)) break;
          sRes += arguments.callee(oBj[sEl], sGetChildName(oBj, sParentName, sEl), nEsting - 1);
        }
        break;
      case "function":
        if (bShowFunctionsCode) sRes += ">>>" + sNL + oBj + "<<<" + sNL;
        break;
      //Остальное содержимого не имеет
      }
    }
  }
  else
    sRes += sParentName + " ! Nesting limit !" + sNL;
  return(sRes);
}

/** Echo-обертка
 */
function vard() {
  WScript.Echo(vardump.apply(null, arguments));
}

/** Класс замеров временных промежутков ------------------------------------
 */
function Timer() {
  this.StartTime = this.StopTime = new Date(); //Сразу начинает отсчет

  //--- Методы

  /** Начинает отсчет, возвращает дату начала
   */
  this.Start = function() {
    return this.StartTime = this.StopTime = new Date();
  };

  /** Отмечает конечную точку отсчета (при этом по сути отсчет не останавливается), возвращает дату конца
   */
  this.Stop = function() {
    return this.StopTime = new Date();
  };

  /** Возвращает время (мс) между StartTime и StopTime
   */
  this.Interval = function() {
    return this.StopTime - this.StartTime;
  };

  /** Возвращает текстовое представление интервала "чч:мм:сс.мсс"
   */
  this.toString = function() {
    var nInt = this.Interval();
    var aInt = [0, 0, 0, 0]; //Период поделенный на мс, с, м, ч 
    var aDivs = [1000, 60, 60, 10000000]; //Делители
    var aDelims = [".", ":", ":", ""]; //Разделители
    for ( var i = 0; i < 4; i++) {
      aInt[i] = nInt % aDivs[i];
      if (aInt[i] == nInt) break; //Все поделено
      nInt = Math.floor(nInt / aDivs[i]);
    }
    var sInt = "";
    for ( var i = 0; i < 4; i++)
      sInt = aDelims[i] + sLeadingSigns(aInt[i]) + sInt;
    return sInt;
  };

  /** Дополняет текстовое представление oBj знаками sSign (по ум. нулями) до длины nOutLength. 
   * Минус выносится вперед, если не выставленно bDoNotMinusMove
   */
  function sLeadingSigns(oBj, nOutLength, sSign, bDoNotMinusMove) {
    if (sSign == undefined) sSign = "0";
    sSign = "" + sSign;
    nOutLength = parseInt(nOutLength, 10);
    if (isNaN(nOutLength) || nOutLength < 1) nOutLength = 2; //По ум.
    var sObj = "" + oBj;
    if (sObj.length < nOutLength) { //Если надо удлинять
      var nZeros = nOutLength - sObj.length;
      var sZeros = "";
      for ( var i = 0; i < nZeros; i++)
        sZeros += sSign;
      return bDoNotMinusMove ? sZeros + sObj : sObj.replace(/(\-{0,1})(.+)/, "$1" + sZeros + "$2");
    }
    else
      return sObj;
  }

} //Timer()

Как можно видеть, все читается четко построчно: сколько строк выдано, столько раз и ReadLine(). Собсно, как и ожидалось в теории.

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

Надеюсь, из приведенного примера вполне удастся такое соорудить. Странно: раньше пример делал, вроде бы пока стороннее приложение не отрабатывало полностью, оно StdOut.AtEndOfStream не включало. А здесь включает после каждой строки. Надо перепроверить...

24

Re: JScript: Проблема с WshScriptExec.Exec

Задачку принудительного завершения действительно легко сделал (внутри while перед чтением просто добавляем проверку по времени). Теперь даже непонятно, что же вызывало затруднения раньше? Возможно, что и те самые зависания из примера с MSDN...

Словом, спасибо за идею (ex.Status == 0)!

jite пишет:

Странно: раньше пример делал, вроде бы пока стороннее приложение не отрабатывало полностью, оно StdOut.AtEndOfStream не включало. А здесь включает после каждой строки.

Поправка. StdOut.AtEndOfStream всегда переходит в true после окончания всей отработки вывода приложения. На момент писания предыдущего поста просто не так понял алгоритм.

25

Re: JScript: Проблема с WshScriptExec.Exec

jite пишет:

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

А вот и не работает, если приложение "решает" всерьез зависнуть, а не просто делает большие паузы.
Попробую продолжить данные рассуждения в теме WSH: Работа с командной строкой

26

Re: JScript: Проблема с WshScriptExec.Exec

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



var sh = new ActiveXObject('WScript.Shell');
var ex = sh.Exec('%comspec% /c dire');

var stdout = [];
var stderr = [];

while ( true ) {
	if ( ! ex.StdOut.AtEndOfStream ) {
		stdout.push(ex.StdOut.ReadLine());
		continue;
	}
	if ( ! ex.StdErr.AtEndOfStream ) {
		stderr.push(ex.StdErr.ReadLine());
		continue;
	}
	if ( ex.Status == 1 ) {
		break;
	}
}

WScript.Echo('\nEXIT CODE :\n' + ex.ExitCode);
WScript.Echo('\nSTDERR    :\n' + stderr.join('\n'));
WScript.Echo('\nSTDOUT    :\n' + stdout.join('\n'));
WScript.Quit();
( 2 * b ) || ! ( 2 * b )

27 (изменено: Rumata, 2014-11-16 16:08:33)

Re: JScript: Проблема с WshScriptExec.Exec

Мои последние эксперименты на Win7 Enterprise SP 1 показывают, что не все гладко с этим решением 3-годичной давности. Иногда вышеприведенный код не работает. Например, в случае запуска приложения, которое в случае успеха ничего не пишет в консоль и уходит в фоновый режим. Тогда пример выше зависает на первом if.

Приложение, запущенное в фоне возвращает статус ex.Status == 0, но проблема не в этом. Проблема именно в том, что стандартные потоки открыты/закрыты/заблокированы/ожидают (что-то еще?). Схожий пример я наблюдал в случае проверки STDIN:


// z.js
WScript.Echo ( WScript.StdIn.AtEndOfStream );

Если запустить "голый" скрипт, то он подвиснет, пока принудительно не завершите скрипт по CTRL-C:

cscript //nologo z.js

Любое перенаправление - завершает скрипт


:: выведет -1 (конец файла достигнут)
cscript //nologo z.js<nul

:: выведет 0 (есть данные, конец файла не достигнут)
cscript //nologo z.js<z.js
( 2 * b ) || ! ( 2 * b )

28 (изменено: daap, 2016-02-14 21:05:34)

Re: JScript: Проблема с WshScriptExec.Exec

Rumata пишет:

Тогда пример выше зависает на первом if

Похоже та же картина. Пытаюсь найти с одним консольным движком общий язык.

AtEndOfStream  и
ReadLine()

работают до тех пор, пока из потока чего-нибудь читается, когда доходит дело до того, что
AtEndOfStream  должно вернуть конец - зависон глубокий пока не вырубишь консоль дочернего процесса

че делать не понятно. другие ж GUI работают с движком этим как-то ....

похоже что поток как-то не завершается как положено. Или это такой постоянно открытый поток ..

29

Re: JScript: Проблема с WshScriptExec.Exec

daap пишет:

AtEndOfStream  и
ReadLine()

работают до тех пор, пока из потока чего-нибудь читается, когда доходит дело до того, что
AtEndOfStream  должно вернуть конец - зависон глубокий пока не вырубишь консоль дочернего процесса

че делать не понятно. другие ж GUI работают с движком этим как-то ....

похоже что поток как-то не завершается как положено. Или это такой постоянно открытый поток

(Сейчас не перепроверял, пишу по воспоминаниям…)

Да, AtEndOfStream возвращает истинное значение,  а ReadLine — пустую строку не в том случае, если поток пуст в данный момент, а если он закрыт, иначе — ждут пока в нём что-нибудь появится или он всё-таки наконец закроется.

Для проверки попробуйте в качестве «движка» сценарий WSH, в котором перед долгим Sleep отсутствует/присутствует WScript.StdOut.Close.

Если такое поведение «движка» не устраивает — перенаправьте его вывод в файл, — в этом случае будет определятся конец файла, если считано всё его текущее содержимое, даже если он не закрыт и в него может быть дописано что-то ещё.

30

Re: JScript: Проблема с WshScriptExec.Exec

Господа, помню, что беда метода ReadLine именно в том, что он ждёт появления перевода строки в StdOut. Поэтому если ничего не вернулось, то он реально повисает на этом моменте. Предполагаю, что Вам поможет метод Read, который читает N-ое количество символов. Вроде бы на форуме эта тема уже поднималась.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

31 (изменено: daap, 2016-02-16 00:19:25)

Re: JScript: Проблема с WshScriptExec.Exec

Xameleon

Спасибо друзья!   не думал что тут живенько так.
С этим  ReadLine - хрен с ним, он то работает после   AtEndOfStream

Зависает и чего-то ждет именно AtEndOfStream
Т.е. я не могу узнать когда закончились строки в потоке, где же последняя.
Видать да, движок не завершает поток. Он как бы асинхронный, одновременно и принимает и выдает в обе дырки.

Пока выручила идея такая.
Я всегда знаю что движок должен ответить на каждый из запросов. Например я ему - "Эй",  он мне всегда - "тут"
Поэтому составил таблицу вопрос-ответ - и у меня последняя строка всегда ожидаемый ответ и я принудительно выхожу из цикла.

Но это все как-то неправильно.  Вдруг он не выдаст ожидаемый ответ, тогда зависон позорный. Не хорошо.

К сожалению я не очень продвинутый програмер, я по своему - в 1С ковыряю.
С этим только столкнулся по причине одного хобби, замутить интересное дело захотелось.

Поэтому такие вещи:  "попробуйте в качестве «движка» сценарий WSH, в котором перед долгим Sleep отсутствует/присутствует WScript.StdOut.Close."   
меня вводят в ступор   :-)))

Кста, подозреваю что в этом СЛИП чета кроется хорошее.

"поможет метод Read"
надо это попробовать, хорошая идея, поищу примеры скриптов....
Т.е. надежда на то что  Read  как-то остановится?  Т,е. он читает сплошным текстом а не построчно? Это было бы гуд.
а вдруг он после последнего символа также залипнет?