Принцип составления однострочных команд для метода Shell

Тема в разделе "Изучение основ языка", создана пользователем Dragokas, 27 окт 2013.

  1. Dragokas
    Оффлайн

    Dragokas Very kind Developer Команда форума Супер-Модератор Разработчик Клуб переводчиков

    Сообщения:
    4.478
    Симпатии:
    4.306
    Статья от 17.01.2013
    Автор: Dragokas

    Принцип составления однострочных команд Batch


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

    Область применения:
    1) Языки программирования высокого уровня
    2) Реестр Windows (reg-файл)

    Пример логики связи:
    1) Команды "удалить файлы a и b"
    Код (DOS):
    del a
    del b
    можно выполнить отдельно:
    Код (vb.net):
    shell "cmd /c del a"
    shell "cmd /c del b"
    2) Команды "вывести на экран сумму 1 + 1"
    Код (DOS):
    Set /A n= 1 + 1
    Echo %n%
    разрывать нельзя, так как значение переменной "n" будет утеряно.

    Чтобы составить сложный набор однострочных команд для выполнения в методе (процедуре, объекте) Shell другого ЯП
    нужно сначала добиться успешной отработки Вашей конструкции в среде командного интерпретатора.

    Открываем консоль - Win + R (выполнить), CMD {Enter}
    Формируем однострочные команды, подставляя первой командой запуск самого EXE-файла интерпретатора:
    Код (DOS):
    CMD.exe /C Echo Привет
    Если нужно выполнить несколько команд, строку команд обрамляем кавычками (согласно синтаксиса ключей CMD /?)
    а сами команды разделяются знаком "амперсанд" (&)
    Код (DOS):
    ::если файл "а" существует, скопировать его под именем "b". Удалить файл "c".
    cmd /c "if exist a copy a b& del c"
    Все кавычки, которые будут заключены внутрь считаются как единая строка и не бьются на отдельные токены. Об этом беспокоится не нужно.
    Код (DOS):
    ::Например, копирование файла с пробелами в пути и/или имени:
    cmd /c "copy "c:\my folder\a.txt" "c:\my folder\b.txt""
    Если участвует цикл FOR, в отличие от текста bat-файла, здесь спецсимволы не обрабатываются, поэтому % в цикле удваивать не нужно.
    Если команду нужно указать вне цикла, тогда весь блок цикла помещается в скобки.
    Код (DOS):
    ::Подсчет кол-ва файлов *.txt
    cmd /c "(for %a in (*.txt) do set /A n+=1)& Echo !n!"
    Если нужно выполнить несколько команд в цикле, обрамляем их скобками после ключевого слова do:
    Код (DOS):
    cmd /c "for %a in (*.txt) do (set /A n+=1& Echo !n!)"
    В однострочной команде переменная сразу не изменяет значения. Получить его можно, влючив "отложенное расширение переменных", а переменную окружив знаками (!):
    Код (DOS):
    cmd /c "SetLocal EnableDelayedExpansion& Set /A n= 1 + 1& Echo !n!"
    чтобы не писать такую длинную команду добавьте к CMD ключ /V:ON обязательно перед ключем /C. Все, что после него считается выполняемыми командами.
    Код (DOS):
    cmd /v:on /c "Set /A n= 1 + 1& Echo !n!"
    .

    Построение кавычек внутри переменных других ЯП

    Кавычка (") - является специальным (служебным) символом для языка Visual Basic,
    и если в реестре, или в языках C++, C# ... для задания кавычки (") как значение переменной нужно указать экран в виде бекслеша (\)
    [csharp=-1]st = "\""[/csharp]
    в VB кавычка удваивается.

    Есть 2 способа присвоения значений с кавычкой переменной:
    1) Удвоение кавычки
    Код (vb.net):
    Shell = "cmd /c ""copy ""c:\my folder\a.txt"" ""c:\my folder\b.txt"""""
    2) Через функцию Chr(34), которая возвращает символ под номером 34 ASCII-таблицы кодов (").
    В VB результат выполнения функции и строковой тип данных может конкатенироваться знаком амперсанд (&).
    Код (vb.net):
    Shell = "cmd /c " & Chr(34) & "copy " & Chr(34) & "c:\my folder\a.txt" & Chr(34) & " " & Chr(34) & "c:\my folder\b.txt" & string(2, Chr(34))
    Но я предпочитаю вариант № 1.

    Общий принцип разбора чужих запросов с кавычками таков:

    Вариант 1) Автоматически.
    Поставьте Stop после команды присвоения переменной сложного запроса.
    выполните ?variable {ENTER} в окне Immediate (Вид - окно неотложного (View - Immediate (Ctrl+G)))
    Immediate_Answer.png

    Вариант 2) Вручную.
    Кавычка в CMD - это двойная кавычка здесь в переменной ""), т.е. если видите запись вида:
    Код (vb.net):
    param = """ """
    Чтобы "расшифровать", отбрасываете визуально левую и правую кавычку, остальные парные кавычки меняете на одиночные и получаете то, что увидит командная строка:
    Код (DOS):
    " "
    Также в реестре, C++, C# ... спецсимволом является сам знак бекслеш (\), который разделяет папки, путь к файлу.
    Здесь приняты 2 способа:
    1) Удвоение бекслеша (\\)
    2) Замена бекслеша (\) на обычный слеш (/)


    Альтернативой для ЯП при использовании Shell является выгрузка кода в несколько строк во внешний файл. Запуск его отдельно.
    При этом такой код может быть заранее:
    1) записан в переменную по указанным выше правилам.
    Код от INV.DS

    Код (vb.net):

    'Запись:
    'Для записи текста служит функция Print(и Write)
    Open "c:\1.txt" For Append As #1 'Открываем файл для добавления записи, с номером канала Print #1, "Твой ТЕКСТ"' Записываем в файл 1.txt текст
    Close #1 'Закрываем файл
    'Есть еще функция для определения свободного канала - FreeFile. Например:
    f = FreeFile 'Возвращает номер свободного канала
    Open "c:\1.txt" For Append As f 'Открываем файл для добавления записи
    Print #f, "Твой ТЕКСТ"'Записываем в файл 1.txt текст
    Close #f 'Закрываем файл
    'Чтение:
    'Считать данные из файла сложнее чем записать. Есть два способа чтения данных из файла. Первый:
    f = FreeFile
    Open "c:\1.txt" For Input As f' Открываем файл 1.txt для чтения
    Text1.Text = Input(LOF(f), f) 'Считываем текст из открытого файла в текстовое поле(Оператор LOF(Len Of File) определяет длину файла)
    Close f
    'Второй:
    Dim txt as String
    Open "c:\1.txt" For Input As #1' Открываем файл 1.txt для чтения
    Do While Not EOF(1) ' Функция EOF(End Of File) проверяет, достигнут ли конец файла Line Input #1, txt ' Читаем строку данных Text1.Text = txt
    Loop
    Close #1
    2) подключен к проекту в виде файла Custom Resource
    Руководство от Alex77755

    32.jpg
    33.jpg
    34.jpg
    35.jpg
    36.jpg

    3) Присоединён в конец уже скомпилированного EXE-файла в открытом виде
    Код VB6 от Catstail

    Вот комментированный код запуска файла, хранящегося в теле другого файла:

    Код (vb.net):

    Private Sub Command1_Click()
      '::: Определяем домашнюю директорию
      HomeDir$ = App.Path
      '::: Берем свободный номер файла
      fi% = FreeFile
      '::: Открываем сами себя на чтение в двоичном доступе
      Open HomeDir$ + "\" + App.EXEName + ".exe" For Binary Access Read As #fi%
      '::: Определяем собственную длину
      LF& = LOF(fi%)
      '::: Позиционируем файловый указатель на начало последних
      '::: 4-x байтов EXE
      Seek #fi%, LF& - 3
      '::: Читаем длину тела запускаемого файла
      Get #fi%, , Offset&
      '::: Позиционируем файловый указатель на начало
      '::: тела запускаемого файла
      Seek #fi%, LF& - Offset& - 3
      '::: Создаем буфер для тела запускаемого файла
      Buf$ = Space$(Offset&)
      '::: Читаем тело
      Get #fi%, , Buf$
      '::: Закрываем входной файл
      Close #fi%
      '::: Берем свободный номер файла
      fo% = FreeFile
      '::: Открываем в тек. директории файл с каким-либо именем
      Open HomeDir$ + "\notepad.sss" For Binary Access Write As #fo%
      '::: Сбрасываем в файл содержимое
      Put #fo%, , Buf$
      '::: Закрываем
      Close #fo%
      '::: Скрываем форму
      Me.Hide
      '::: Запускаем... и ждем завершения
      Processn.ExecPrg HomeDir$ + "\notepad.sss", "", RC%, vbNormalFocus
      '::: Дождались - покажем форму
      Me.Show
      '::: Удалим запускаемый файл
      Kill HomeDir$ + "\notepad.sss"
      '::: Покажем код завершения
      MsgBox "RC=" + CStr(RC%)
    End Sub

     
    А вот комментированный код exe-мэйкера:

    Код (vb.net):

    Sub Main()
      '::: Домашняя директория
      HomeDir$ = App.Path
      '::: Командная строка
      CMD$ = Command$
      '::: положение пробела в ком. строке
      k% = InStr(CMD$, " ")
      '::: нет пробела - ошибка
      If k% = 0 Then
      MsgBox "Запуск: maker Главный Запускаемый"
      Exit Sub
      End If
      '::: первый параметр - имя главного файла
      master$ = Left$(CMD$, k% - 1)
      '::: второй параметр - имя запускаемого из
      slave$ = Mid$(CMD$, k% + 1)
      '::: Свободный номер файла
      fi% = FreeFile
      '::: Открываем главный (для чтения и записи!)
      Open HomeDir$ + "\" + master$ For Binary Access Read Write As #fi%
      '::: Получаем длину
      LFM& = LOF(fi%)
      '::: Свободный номер файла
      ffi% = FreeFile
      '::: открываем подчиненный (только для чтения)
      Open HomeDir$ + "\" + slave$ For Binary Access Read As #ffi%
      '::: Получаем длину
      LFS& = LOF(ffi%)
      '::: создаем буфер для запускаемого файла
      BufS$ = Space$(LFS&)
      '::: Читаем весь запускаемый файл в буфер
      Get #ffi%, , BufS$
      '::: Позиционируем главный файл в конец
      Seek #fi%, LFM& + 1
      '::: Выводим буфер
      Put #fi%, , BufS$
      '::: выводим длину (чтобы главный мог узнать, сколько байтов от конца нужно взять)
      Put #fi%, , LFS&
      '::: Все закрываем
      Close
      '::: OK!
      MsgBox "OK!"
    End Sub
     
    Некоторые примеры:
    1) Рекурсивный подсчет кол-ва файлов, заданных маской
    Код (vb.net):
    Sub CMD_Seek_Recursive()
    Dim RetCode, Query As String, StartFolder As String, SeekFile As String, Flags As String
    Dim isRecursive As Boolean
    Dim inclSystem As Boolean
    Dim inclHidden As Boolean
    Dim inclReadOnly As Boolean

    'Папка, начиная с которой ведется поиск
    StartFolder = "l:\bash\test"
    'Имя искомого файла (допускается подстановочный знак *)
    SeekFile = "*.txt"

    'Options
    'Включить рекурсию
    isRecursive = True
    'Включить в поиск системные, скрытые файлы, только для чтения
    inclSystem = True
    inclHidden = True
    inclReadOnly = True

    Flags = IIf(isRecursive, "/s", "") + "/a:-D" + _
      IIf(inclSystem, "", "-S") + IIf(inclHidden, "", "-H") + IIf(inclReadOnly, "", "-R")

    Query = "cmd.exe /Q /V:on /C ""(for /f ""delims="" %x in ('dir /b " & Flags & " " & _
      """" & StartFolder & "\" & SeekFile & """') do (set /a n+=1))& Exit !n!"""

    RetCode = CreateObject("WScript.Shell").Run(Query, 0, True)

    Debug.Print "Количество найденных файлов = " & RetCode
    End Sub
    И чуть по-меньше буков:

    Код (vb.net):
    Sub CheckCount()
    Debug.Print Get_Count_Files("l:\bash\test", "*.txt")
    End Sub

    'Выдает кол-во файлов SeekFile в папке StartFolder рекурсивно без учета скрытых файлов
    Function Get_Count_Files(StartFolder As String, SeekFile As String) As Long
    Get_Count_Files = CreateObject("WScript.Shell").Run("cmd /V:on /C ""(for /R """ & _
      StartFolder & """ %x in (""" & SeekFile & """) do set /a n+=1)& Exit !n!""", 0, True)
    End Function
    2) Еще в VB можно получить код возврата, сгенерированного командами CMD или вручную через Exit
    Метод от Казанский

    Получение ErrorLevel из команды CMD в переменную VBS-скрипта
    (на примере команды сравнения файлов)

    Код (vb.net):
    Function FileCompare(path1, path2)
      'возвращает результат двоичного сравнения двух файлов
      'с помощью системной утилиты fc.exe. Возвращаемое значение:
      '0 - файлы одинаковы;
      '1 - файлы различаются;
      '2 - файл не найден
    FileCompare = CreateObject("wscript.shell").Run( _
      "cmd /c fc /b """ & path1 & """ """ & path2 & """", 0, True)
    End Function

     
    Комментарий Dragokas:
    Добавлю от себя: чтобы симитировать свой произвольный код возврата, достаточно указать команду Exit <code>
    Например,
    Код (vb.net):
    'Двоичный сдвиг 0110(6) -> 0011(3)
    FileCompare = CreateObject("wscript.shell").Run( _
      "cmd /v:on /c set /A x=6"">>""1& exit !x!", 0, True)
     
    Последнее редактирование: 2 ноя 2013
    FraidZZ, orderman и Kиpилл нравится это.
  2. Kиpилл
    Оффлайн

    Kиpилл Команда форума Администратор

    Лучший автор месяца

    Сообщения:
    12.208
    Симпатии:
    4.977
    Картинка потерялась))
    А аргументы типа %1 ?
     
  3. Dragokas
    Оффлайн

    Dragokas Very kind Developer Команда форума Супер-Модератор Разработчик Клуб переводчиков

    Сообщения:
    4.478
    Симпатии:
    4.306
    В языке VB, VBScript %1 передаются без удвоения знака процента.
    Знак % - является особым спецсимволом только в пакетных файлах.
    Например, из-под командной строки CMD его удваивать тоже не нужно.

    Например, получить перечень имен файлов в текущей папке через цикл будет:
    • из пакетного файла .bat/.cmd:
    Код (Text):
    for %%a in (*) do @echo %%a
    • из CMD.exe:
    Код (Text):
    for %a in (*) do @echo %a
     
    Kиpилл нравится это.

Поделиться этой страницей