Создание в powershell директорий и файлов с new-item их перенос и копирование

Другие возможности

Кроме хранения значений используя возможности Powershell можно использовать методы .Net и выражения. Все это мы рассмотрим ниже на примерах.

Сортировка

По умолчанию hashtable не отсортированы

Это не очень важно, так как по умолчанию значения мы получаем по ключу, но это можно сделать так:

Выражения

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

Как вы можете видеть выражение, которые переводит KB в GB, имеет вид хэш таблиц. Мы их можем использовать и другим способом:

Label — это имя колонки, а знак $_ элемент, который передается через конвейер.

Выражения можно использовать в командах, которые позволяют это делать. Это команды типа Select-Object и Format-Table

Дополнительные возможности с сортировкой

С командлетом сортировки тоже можно использовать выражения. Связи с этим по этим значениям можно и сортировать результат:

Пример выше сортирует пользователей AD по результату из функции.

Сортировка коллекций в массиве

В статье по работе с массивами в Powershell мы рассказывали, что в них могут храниться объекты. Если там хранятся коллекции, то их тоже можно сортировать:

Передача как параметров

Особенностью работы с хеш таблицами в Powershell это возможность их передачи через командную строку. На примере создания папки это выглядит так:

Иногда команды бывают очень длинными, что затрудняет чтение в скриптах. Мы можем изменить это представление передав параметры через коллекцию:

Таким образом мы можем передавать сразу несколько таблиц:

В командах и программах, где значения используются в виде /parametr:value тоже можно использовать такой подход. Для примера Robocopy:

Вложенные коллекции

Hashtable и массивы можно вкладывать:

Вызывать элементы можно так:

Добавление ключей и значений делается выше описанным способом:

Как видно по скриншоту ключи во вложенных хеш таблицах отображаются как Value. Если вы не знаете какой тип хранит таблица, то сможете узнать это через GetType():

С этим же примером, если мы не будем использовать GetType(), у нас выведутся все элементы:

Либо вызывать ключи обращаясь к конкретной коллекции:

Предположим, что нам нужно добавить значение в Support. На данный момент это строка, но мы сделаем массив:

Преобразование в JSON

Как вы уже заметили хэш таблицы в Powershell сильно похожи на JSON. Если вам удобнее для хранения или использования этот формат, то вы сможете выполнить преобразование:

О том как выполнить обратное преобразование будет рассказано ниже.

Вам так же будет интересно:

Получение списка USB устройств в Powershell

Вывод hashtable как таблицы

Если вам нужно изменить вывод таким образом, что бы вместо имен колонок были ключи, то это можно сделать через PSCustomObject :

Так же сработает и после создания коллекции:

 Можно использовать Out-GridView для представления в графическом интерфейсе:

Если будет интересно могу описать способ добавления hashtable в базу SQL с помощью Powershell и их вывод.

Подробно о том как работать с CSV в Powershell мы уже говорили. Это одна из самых простых в использовании команд:

Сохранение в файл и загрузка

Мы можем сохранить коллекции в файл для дальнейшего использования. Я не нашел способов с помощью которых можно сохранить коллекции без преобразования. Один из способов использовать импорт и экспорт в формат xml:

Для загрузки выполним импорт:

Сохранить в CSV для последующей загрузки возможна, на мой взгляд, только с «костылями». 

Можно использовать JSON, но так получится другой тип данных:

Для загрузки:

Как видно, в случае с JSON, мы больше не можем использовать элемент как параметр командной строки.

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

Удаление элементов (Remove-Item)Deleting Items (Remove-Item)

Чтобы удалить файлы и папки, используйте командлет Remove-Item .To delete files and folders, use the Remove-Item cmdlet. Командлеты Windows PowerShell, например Remove-Item , которые могут вносить значительные и необратимые изменения, часто будут запрашивать подтверждение при вводе команд.Windows PowerShell cmdlets, such as Remove-Item , that can make significant, irreversible changes will often prompt for confirmation when you enter its commands. Например, при попытке удалить папку New.Directory вас попросят подтвердить команду, так как папка содержит файлы:For example, if you try to remove the New.Directory folder, you will be prompted to confirm the command, because the folder contains files:

Так как ответ Да является ответом по умолчанию, чтобы удалить папку вместе файлами, нажмите клавишу ВВОД .Because Yes is the default response, to delete the folder and its files, press the Enter key. Чтобы удалить папку без подтверждения, используйте параметр -Recurse .To remove the folder without confirming, use the -Recurse parameter.

2. Создайте папку в Windows, используя PowerShell

Windows PowerShell в Windows 10,

Для этого примера мы создадим папку с именем Solvetic_Analisis на рабочем столе. Прежде всего мы должны разрешить размещение Windows PowerShell на рабочем столе Windows 10, для этого мы вводим следующее:

 кд. / Рабочий стол / 

Далее мы будем использовать командлет New-Item, который дает нам возможность создавать объекты в Windows 10, поэтому синтаксис, который мы должны использовать, следующий:

 New-Item Solvetic_Analisis -Type Directory 

New-Item: ссылается на командлет для создания объектов.

Solvetic_Analisis: это имя объекта, который мы создадим.

-Тип: Здесь мы указываем тип объекта для создания, в данном случае мы указываем каталог, потому что это папка.

Нажмите Enter, и мы увидим, что папка была правильно создана на рабочем столе.

Мы можем проверить его создание на рабочем столе.

Таким простым способом мы можем создавать папки в Windows 10 или любой другой версии Windows, используя PowerShell .

Копирование элементов (Copy-Item)Copying Items (Copy-Item)

Если вы знакомы с операциями копирования в других оболочках, поведение командлета Copy-Item в Windows PowerShell может показаться нестандартным.If you are familiar with the copy operations in other shells, you might find the behavior of the Copy-Item cmdlet in Windows PowerShell to be unusual. При копировании элемента из одного расположения в другое командлет Copy-Item не копирует его содержимое по умолчанию.When you copy an item from one location to another, Copy-Item does not copy its contents by default.

Например, при копировании каталога New.Directory с диска C: в каталог C:\temp команда выполняется, но файлы в каталоге New.Directory не копируются.For example, if you copy the New.Directory directory from the C: drive to the C:\temp directory, the command succeeds, but the files in the New.Directory directory are not copied.

Если отобразить содержимое C:\temp\New.Directory , там не будет файлов:If you display the contents of C:\temp\New.Directory , you will find that it contains no files:

Почему командлет Copy-Item не копирует содержимое в новое расположение?Why doesn’t the Copy-Item cmdlet copy the contents to the new location?

Командлет Copy-Item предполагалось использовать как универсальный; он не предназначен для простого копирования файлов и папок.The Copy-Item cmdlet was designed to be generic; it is not just for copying files and folders. Кроме того, даже при копировании файлов и папок может потребоваться копировать только контейнер без элементов внутри него.Also, even when copying files and folders, you might want to copy only the container and not the items within it.

Чтобы скопировать все содержимое папки, включите параметр Recurse командлета Copy-Item в команду.To copy all of the contents of a folder, include the Recurse parameter of the Copy-Item cmdlet in the command. Если вы уже скопировали каталог без содержимого, добавьте параметр Force , который позволит перезаписать пустую папку.If you have already copied the directory without its contents, add the Force parameter, which allows you to overwrite the empty folder.

powershell — получить последнюю созданную папку из пути с помощью powershell

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

  • Получить все элементы в папке. Это получит файлы и папки, поэтому шаг 2 необходим. | в конце строки сигнализирует о продолжении конвейера в следующей строке — объекты, созданные Get-ChildItem, будут передаваться по очереди одной команде.

    Get-ChildItem c:\temp |

  • Фильтр для папок. К сожалению, нет действительно элегантного способа. Не беспокойтесь о том, что в нем говорится «контейнер», а не «папка». Эти команды работают со многими разными вещами, а не только с файлами и папками, поэтому для именования была использована более общая концепция.

    Where { $_.PSIsContainer } |

  • Сортировать по дате, по убыванию, поэтому самая новая папка — первая.

    Sort CreationTime -Descending |

  • Выберите первую (самую новую) папку.

    Select -First 1

Короче говоря:

или

Обе эти строки сильно используют псевдонимы по умолчанию для команд в PowerShell, например ? для Where-Object. Однако вы должны использовать полные имена в сценариях, так как вы никогда не узнаете, как будут выглядеть псевдонимы на других машинах, на которых может работать код.

EDIT: У PowerShell 3 есть дополнительные параметры для Get-ChildItem, которые позволяют вам напрямую фильтровать файлы или папки, поэтому вам не нужен Where:

Как правило, вы будете работать с объектами и их свойствами в PowerShell. Две очень полезные команды: Get-Member и ее псевдоним gm и Get-Command или просто gcm. Get-Member расскажет вам, какие свойства и методы имеет объект; вы просто подключите к нему что-то еще:

сообщит вам, какие файлы свойств и каталоги имеют.

Get-Command отобразит все команды или те, которые соответствуют определенному шаблону. Команды PowerShell стараются быть очень последовательными в использовании глаголов и существительных. Чтобы найти все команды, которые заканчиваются на Object, вы можете попробовать gcm *-Object — это общие команды, работающие с почти всем. Get-Help ForEach-Object затем расскажет вам о конкретной команде ForEach-Object в этом случае.

Передача параметров

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

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

Теперь, для вызова функции, требуется передавать два параметра:

При вызове функции мы передаем два обязательных параметра со значениями. Эти значения, внутри функции, будут доступны под названиями $param1 и $param2. Эти переменные мы передаем в команду получения и фильтрации логов.

Установка значений по умолчанию

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

Мы должны всегда указывать ключ param2, что добавляет немного работы. Что бы это исправить достаточно поменять их местами:

Как видно на примере, если мы не указываем ключи param1 и param2 важна последовательность, так как именно в такой последовательности они будут присвоены переменным внутри функций.

Возвращение значений

В отличие от других языков, если мы присвоим переменной $result результат функции Get-DayLog, то она будет содержать значения:

Это будет работать до тех пор, пока мы не решим изменить функцию присвоив переменные:

Мы не можем получить результат используя переменную $result, так как функция не выводит информацию, а хранит ее в переменной $events. Вызывая $events мы тоже не получаем информацию, так как тут работает понятие «область видимости переменных».

Так как функции это отдельная часть программы вся логика и ее переменные не должны касаться другой ее части. Область видимости переменных подразумевает это же. Все переменные, созданные внутри функции остаются в ней же. Эту ситуацию можно исправить с помощью return:

Я бы рекомендовал всегда возвращать значения через return, а не использовать вывод используя команды типа Write-Output внутри функции. Использование return останавливает работу функции и возвращает значение, а это значит что его не стоит ставить по середине логики если так не планировалось.

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

Я вернул оба значения разделив их запятой. По умолчанию всегда возвращается массив. Массивы в Powershell это набор не именованных параметров. Более подробно  о них мы уже писали.

В случае с массивами, что бы добавит надпись о зарплате, и налоге нужно использовать индексы:

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

Подробно о хэш таблицах в Powershell вы можете почитать в предыдущих статьях. Далее так же будет несколько примеров с ними.

Вы можете возвращать любой тип данных и в любом формате и последовательности. Каждый из них имеет своё преимущество.

Область видимости переменных

Все переменные объявленные до момента вызова функции могут быть ей использованы:

Такой подход не запрещает переопределить переменную внутри функции дав ей другое значение:

Как уже писалось выше, значения внутри функции не доступны вне нее и у нас есть все возможности что бы этого не потребовалось. Тем не менее есть способ объявить внутри функции переменную, которая будет доступна вне нее.

Такие переменные называются глобальными. Объявляются приставкой $global:

Как вы видите, в отличие от предыдущего примера, переменная $zarplata изменила значение. Использование глобальных переменных является нежелательным так как может привести к ошибкам. Ваш скрипт может быть импортируемым модулем и об этой переменной может никто не знать, тем не менее она будет в области видимости.

Поиск совпадений одного из нескольких символов

Когда мы не уверены в конкретном символе — мы можем указать несколько использовав квадратные скобки [] . Значения, которые будут помещены в эти скобки будут соответствовать одному значению:

В примере выше ищутся совпадения либо по букве R или P. Можно указывать диапазон значений. Например такое написание говорит, что мы ищем цифры от 1 до 9. Если написать у нас будут искаться числа с 5 до 9, затем с 6 по 9 и т.д.

Ниже мы ищем значения, где есть буквы от ‘с’ до ‘я’, после которых есть буква ‘е’. Во втором примере мы ищем элементы массива, которые начинаются с букв ‘д’ до ‘с’:

Можно использовать любые метасимволы. В примере ниже у нас идет поиск числа или буквы, затем пробела и опять число-буквенное значение:

Вы часто можете увидеть написание похоже на следующее, что обозначает любую букву а A до z, от 0 до 9:

Получение списка служб

Узнать статус всех служб можно так:

Каждый результат выдаваемый командами в PS представляет собою объект. Этот объект содержит разные свойства и методы. Свойства — это например имя или статус. По умолчанию мы видим не все свойства. Что бы узнать какие еще свойства есть у команды нужно выполнить следующее:

Часть свойств реализована в виде ключей. Попробуем вывести сразу все свойства всех сервисов, но в таком случая вывод будет сложно читаемый:

Возможно вывести только имена, статус и тип запуска:

Или выведем информацию по одному сервису:

У сервисов есть короткое имя и отображаемое. Так мы выведем оба:

В именах мы можем использовать маски, а это значит что мы можем не знать полное название и использовать знак * в том месте, где не уверены в названии или написании:

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

  • Cannot find any service with service name
  • Cannot find any service with display name
  • Не удается найти службу с отображаемым именем

Кроме этого есть два ключа, которые тоже поддерживают маски:

  • Include — включают поиск по какой-то маске или точному соответствию.
  • Exclude — исключает упоминание.

Можно сравнить разницу с прошлым примером:

У сервисов несколько статусов:

  • Running — запущен.
  • Stopped — остановлен.
  • Paused — приостановлен (пауза).

По каждому из них мы можем отфильтровать результат:

Кроме этого есть 3 типа запуска:

  • Automatic — автоматический запуск вместе с системой.
  • AutomaticDelayedStart — запуск после загрузки системы.
  • Manual — ручной запуск.
  • Disabled — отключенный.

Допустим, что у нас есть какая-то проблема в операционной системе и мы хотим проверить все сервисы, который автоматически запускаются при включении ОС. Для этого нам нужно использовать несколько условий, где статус не равен Running и тип запуска равен Automatic:

Службы могут быть зависимы от других и для проверки этих свойств есть два параметра:

  • DependentServices — кто зависит от этого сервиса.
  • RequiredServices — от кого зависит этот сервис.

Аналогично параметрам команды выше есть свойства DependentServices и ServicesDependedOn (RequiredServices). Они выводят одно и то же.

Есть несколько свойств, которые не выведены в параметры — это:

  • CanPauseAndContinue — можно ли приостановить работу сервиса и возобновить.
  • CanShutdown — может ли быть выключен.
  • CanStop — может ли быть полностью остановлен.

Эти свойства так же можно увидеть в GUI. В командлете ниже я использую алиас gsv (короткое имя Get-Service):

Каждая команда PS, где присутствует параметр ComuterName, может быть выполнена удаленно. Для удаленного управления в Powershell нужны дополнительные настройки, которые уже описывались в предыдущей статье. 

Имена всех компьютеров, с которых мы хотим получить имена сервисов, можно указать через запятую:

Передача через конвейер или Pipeline

Вы наверняка работали через команды Powershell, которые позволяли использовать конвейер следующим образом:

Если мы захотим использовать подход описанный выше, создав новые команды в виде функций, то конвейер не будет работать:

Выполнив следующую команду мы сможем увидеть, что значения, которые могут приниматься через конвейер помечаются специальным атрибутом:

Таких атрибутов всего два:

  • ValueFromPipelineByPropertyName — получение значения из конвейера по имени;
  • ValueFromPipeline — получение через конвейер только значения .

Кроме этого, внутри нашей функции, мы должны добавить специальный блок Process. Наш скрипт в итоге будет выглядеть так:

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

Если бы мы не указали блок Process функция бы вернула только последней результат из массива 1..5:

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

Как уже писалось ValueFromPipelineByPropertyName принимает только именованные параметры и в случае с именем «bad» мы получаем ошибку:

  • Не удается привязать объект ввода к любым параметрам команды, так как команда не принимает входные данные конвейера
  • The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.

Причем передавать именованные параметры через хэш таблицы мы не можем, только через pscustomobject.

Вы можете указывать сразу два атрибута таким образом:

Это позволит использовать и значение с именем, если оно указано либо без него. Это не спасет вас от ситуации, если вы передаете параметр с другим именем:

Передача через конвейер нескольких значений

Для примера рассмотрим ситуацию, где нам нужно передать через конвейер два значения. Если Get-SomeNum будет возвращать массив, то через конвейер у нас будет проходить каждое число по отдельности. Это еще один повод использовать именованные параметры:

Расширенные функцииAdvanced Functions

Функцию в PowerShell можно легко преобразовать в расширенную.Turning a function in PowerShell into an advanced function is really simple. Одно из различий между функцией и расширенной функцией заключается в том, что расширенные функции имеют ряд общих параметров, которые добавляются в функцию автоматически.One of the differences between a function and an advanced function is that advanced functions have a number of common parameters that are added to the function automatically. К этим общим параметрам относятся такие параметры, как Verbose и Debug.These common parameters include parameters such as Verbose and Debug.

Я начну с функции , которая использовалась в предыдущем разделе.I’ll start out with the function that was used in the previous section.

Хочу обратить ваше внимание на то, что функция не имеет общих параметров.What I want you to notice is that the function doesn’t have any common parameters. Существует несколько различных способов просмотра общих параметров.There are a couple of different ways to see the common parameters

Один из них — просмотр синтаксиса с помощью .One is by viewing the syntax using .

Другой — детализация параметров с помощью .Another is to drill down into the parameters with .

Добавьте , чтобы преобразовать функцию в расширенную.Add to turn the function into an advanced function.

При добавлении общие параметры добавляются автоматически.Adding adds the common parameters automatically. Для требуется блок , но блок может быть пустым. requires a block, but the block can be empty.

Если выполнить детализацию параметров с помощью , можно увидеть имена параметров, включая общие параметры.Drilling down into the parameters with shows the actual parameter names including the common ones.

Сравнение ForEach и команды ForEach-Object

Foreach ForEach-Object
Загружает все элементы коллекции Загружает только один элемент в память через конвейер
Использует больше памяти из-за полной загрузки массивов Меньшее использование памяти из-за одного элемента
С небольшим объемом массивов работает быстрее Работает медленнее
Нельзя использовать через конвейер. Это не приведет к ошибке, но будет использован алиас командлета Можно использовать конвейер или параметр InputObject
Поддерживаются методы break и continue Нельзя прервать используя методы contiinue и break

Скорость работы обоих этих методов можно увидеть через следующие скрипты:

Написание и редактирование текста в области сценариевHow to write and edit text in the Script Pane

В области скриптов текст можно копировать, вырезать, вставлять, искать и заменять.You can copy, cut, paste, find, and replace text in the Script Pane. Также можно отменить и повторить последнее выполненное действие.You can also undo and redo the last action you just performed. Для этого используются те же клавиши, как и во всех других приложениях Windows.The keyboard shortcuts for these actions are the same shortcuts used for all Windows applications.

Ввод текста в области сценариевTo enter text in the Script Pane

  1. Установите курсор в область сценариев, щелкнув кнопкой мыши любую ее часть или выбрав пункт Перейти в область сценариев в меню Вид .Move the cursor to the Script Pane by clicking anywhere in the Script Pane, or by clicking Go to Script Pane in the View menu.
  2. Создайте сценарий.Create a script. Цветовая подсветка синтаксиса и заполнение нажатием клавиши TAB обеспечивают более широкие возможности для редактирования в интегрированной среде скриптов Windows PowerShell.Syntax coloring and tab completion provide a richer editing experience in Windows PowerShell ISE.
  3. Подробную информацию о заполнении нажатием клавиши TAB, помогающем при вводе кода, см. в статье How to Use Tab Completion in the Script Pane and Console Pane (Использование заполнения нажатием клавиши TAB в областях сценариев и консоли).See How to Use Tab Completion in the Script Pane and Console Pane for details about using the tab completion feature to help in typing.

Поиск текста в области сценариевTo find text in the Script Pane

  1. Чтобы найти текст в любой части скрипта, нажмите клавиши CTRL+F или выберите Find in Script (Найти в сценарии) в меню Edit (Правка).To find text anywhere, press CTRL+F or, on the Edit menu, click Find in Script .
  2. Чтобы найти текст после курсора, нажмите клавишу F3 или выберите Найти следующее в сценарии в меню Правка .To find text after the cursor, press F3 or, on the Edit menu, click Find Next in Script .
  3. Чтобы найти текст до курсора, нажмите клавиши SHIFT+F3 или выберите Find Previous in Script (Найти предыдущее в сценарии) в меню Edit (Правка).To find text before the cursor, press SHIFT+F3 or, on the Edit menu, click Find Previous in Script .

Поиск и замена текста в области сценариевTo find and replace text in the Script Pane

Нажмите клавиши CRTL+H или в меню Edit (Правка) выберите Replace in Script (Заменить в сценарии).Press CTRL+H or, on the Edit menu, click Replace in Script . Введите текст, который нужно найти, и замещающий текст, затем нажмите клавишу ВВОД.Enter the text you want to find and the replacement text, then press ENTER.

Копирование текста в области сценариевTo copy text in the Script Pane

  1. В области сценариев выделите текст, который требуется скопировать.In the Script Pane, select the text that you want to copy.

  2. Нажмите клавиши CTRL+C, щелкните значок Copy (Копировать) на панели инструментов или выберите Copy (Копировать) в меню Edit (Правка).Press CTRL+C or, on the toolbar, click the Copy icon, or on the Edit menu, click Copy .

Вырезание текста в области сценариевTo cut text in the Script Pane

  1. В области сценариев выделите текст, который требуется вырезать.In the Script Pane, select the text that you want to cut.
  2. Нажмите клавиши CTRL+X, щелкните значок Cut (Вырезать) на панели инструментов или выберите Cut (Вырезать) в меню Edit (Правка).Press CTRL+X or, on the toolbar, click the Cut icon, or on the Edit menu, click Cut .

Вставка текста в области сценариевTo paste text into the Script Pane

Нажмите клавиши CTRL+V, щелкните значок Paste (Вставить) на панели инструментов или выберите Paste (Вставить) в меню Edit (Правка).Press CTRL+V or, on the toolbar, click the Paste icon, or on the Edit menu, click Paste .

Отмена действия в области сценариевTo undo an action in the Script Pane

Нажмите клавиши CTRL+Z, щелкните значок Undo на панели инструментов или выберите Отменить в меню Правка .Press CTRL+Z or, on the toolbar, click the Undo icon, or on the Edit menu, click Undo .

Повторное выполнение действия в области сценариевTo redo an action in the Script Pane

Нажмите клавиши CTRL+Y, щелкните значок Повторить на панели инструментов или выберите Повторить в меню Правка .Press CTRL+Y or, on the toolbar, click the Redo icon, or on the Edit menu, click Redo .

Оцените статью
Рейтинг автора
5
Материал подготовил
Андрей Измаилов
Наш эксперт
Написано статей
116
Добавить комментарий