Sort images by permission... on stage PowerShell

once again looking interesting, lyubopitnost and other vsyakosti habré, came upon article learn how to use Python to restore order among illustrations, digital cemetery, which has almost every one of us. Since I had to deal with processing of images using PowerShell, I decided to conduct a meaningful comparison. Indicative from the point of view to demonstrate some of the characteristic capabilities of PowerShell for those who do not yet know him or know superficially.

Unfortunately, PowerShell was a strange situation, when a very powerful tool that is overlooked by the public and definitely needs some promotion. Moreover, recently it is included in Windows 7 and will soon be in jobs of a considerable number of users. But here such an occasion in the form of concise on the one hand, interesting but on the other tasks of an administrative nature to restore order in the information repository. So, let's start.

Begin with small lyrical digression. Even when talking about such seemingly simple tools as command processors, you want something sublime. And I think I found it. You'll laugh, but I chose the development for a command processor as multiparadigm. The first paradigm is imperative. We see it in almost all batch files and have seen it in the example of the original problem in Python. Another paradigm — functional. I called it so because of its similarity to the approach used in functional programming languages. In everyday life we know it as a command pipes, just pipes and many other affectionate term :) Briefly remind you how this looks in a simple example:

X:\> (dir /b folder1\*.txt && dir /b folder2\*.txt) | find "text" | sort

Here we see the three instructions, separated by a vertical bar. Each takes the results of the preceding, performs over them a certain operation and transmits the next command in the queue of the pipe. In our example, the standard cmd.exe gathers through the first user a list of text files from two folders. This list is passed to the find command, which leaves only those lines that contain the substring "text" and have they sent to the sort command, which sorts them. In functional programming languages it might look like this:

the sort(find((dir /b folder1\*.txt && dir /b folder2\*.txt) "text"))

Isn't there certain similarities? In fact, each element of the pipe is akin to the function. Just to existing record types functions (such as Postfix, prefix and infix record) added another papova record :)

Despite the fact that the command pipe, as you can see, has been since the days of MS-DOS, I would also like to thank the UNIX community, which sounds weird given the origin of PowerShell in the mountain of Microsoft. But that is a simple explanation. In UNIX-like systems, these mechanisms have been elevated to the rank of art, allowing to combine various commands in the most unusual and very useful combination.

It so happened that PowerShell learned this, in my opinion a very good line and was merged with another, not less successful solution. Unlike transmission lines in the command pipe of the same Linux, PowerShell deals with objects. That all became clearer, I propose to start implementing the task. We will do this, of course, using a "functional" approach using pipes, for the imperative approach would differ little from what we already have in the case of implementation on Python. And I want to compare not only the tools but also of the paradigm.

Because of the functionality of our approach, we divide the solution of the problem for some semblance of these features, and in our case — statements, each of which performs some well defined role. Separately, I note that I'll be intentionally slightly complicate the implementation to show more of the capabilities of PowerShell. In real conditions, something can be simplified, and something generally to get rid of.
Step 0. First, we will describe some of the conditions of execution as a whole. First- we are interested in input parameters and this is a good reason to consider working with variables in PowerShell.

PS X:\> $source="x:\folder\source"
PS X:\> $target="x:\folder\target"
PS X:\> $source, $target
x:\folder\source
x:\folder\target


The first line of the $source variable we defined as the value of the source folder where the pictures for further sorting. In the destination folder $target we will place the sorted pictures. The third line just says that you need to withdraw the values of these variables to the console, which we see next. Note that values are not just quoted. The fact that the values of the variables typed, and thus we have defined them as strings. In the absence of quotation marks, the CPU will treat the text as a command and assign a variable the results. For example:

PS X:\> $test=dir x:\folder\source
PS X:\> $test.Length

10

PS X:\> $test[0].GetType()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DirectoryInfo System.IO.FileSystemInfo


The result of this command the variable $test will be a collection of objects in the specified folder. The Length property, which we used in the user $test.Length is the number of elements in the collection. But $test[0].GetType() displays information about the type of the first element in the collection. As you can see, it's not a simple string, but a DirectoryInfo. If the first file would be FileInfo. This is a very important illustration to what I said earlier and that we will actively use later — PowerShell passes in pipe strings, and the objects well-defined types.

The next preparatory step is that we will use type illustrations, which is available in the library System.Windows.Forms that are not downloadable by default. We need to give instruction to PowerShell, so he uploaded it to. For example:

PS X:\> [void][reflection.assembly]::LoadWithPartialName("System.Windows.Forms")

Even here discern another significant feature of PowerShell is the creation of any objects that offers .NET or COM. Their name is Legion, but that's a separate issue. In this case, just accept the string as a given. For symmetry, we assume that the execution environment is ready.

Step 1. once the conditions of execution of the tasks ready, we will start with creating a list of files that pretend to be pictures. Formulation of the problem it is, because we assume that the file extension is useful information, but do not guarantee that the file is a picture. In most cases you do not need, but as I said earlier — the decision is intentionally difficult. So, we choose all files of specified extension in the specified folder and all its subfolders. Look our first step Paipa will be as follows:

PS X:\> dir $source -r -include *.jpg, *.png, *.gif

The option "-r" means recursive bypass of directories "-include" you can list the include files mask (or Maxi exclude list in the option "-exclude"). In response to this command we get a list of files.

Step 2. the Next element of the payp we are trying to create for each file received from the previous statement, the object bitmap. This statement illustrates several features of PowerShell but first, for clarity of the picture, example:

PS X:\> dir $source -r -include *.jpg, *.png, *.gif | select FullName, @{Name="Image"; Expression={New-Object System.Drawing.Bitmap $_.FullName}} -ErrorAction SilentlyContinue

The first thing we see in the second instruction pipe is the select command. Its purpose is to create new objects and pass them on. To do this, select comma-separated lists all the properties for the new object we are interested in. The first is FullName. Specified in this form means that we take it from the object that inherited the pipe and with the same name and value pass the new object. In our case, we are talking about the FullName property of the FileInfo class, which returns the full path to the file.
The following design is a little more complicated. It creates a new property whose name is passed in Name and value in the Expression. The value we create an instance of a class that describes the picture (System.Drawing.Bitmap), passing to its constructor the same FullName value with the file location illustration. Separately note the difference in syntax for accessing the FullName property. The select statement does this in a simplified form. In most other cases, the $_ variable means the object passed us by pipe to the property which we can call the dot and the property name.

If the file that we are going to work is not an illustration of a bitmap, then an attempt to create a System.Drawing.Bitmap would result in an error. In order for these errors to ignore, we added the ErrorAction option that allows you to ignore them. Note that this option is not unique to the select command, and belongs to the category of so-called Common Parameters that you can use almost all other instructions.

Step 3. At the end of the previous step we get the list of objects, each with two properties: the FullName with the full path to the file name and Image with an illustration in the instance of the Bitmap class. If any of the files failed to create a class of a bitmap image, the Image property is empty. So we need a step that allow to filter all objects that are not illustrations. Summary of additions the new manual will be:

PS X:\> dir $source -r -include *.jpg, *.png, *.gif | select FullName, @{Name="Image"; Expression={New-Object System.Drawing.Bitmap $_.FullName}} -ErrorAction SilentlyContinue | where { $_.Image }

All simply and succinctly. We meet a familiar appeal to the property. In this case, the Image property. We meet a new where statement, which allows you to send down the pipe only those objects that satisfy the specified condition in her condition. At the same time get acquainted with a simple check for empty values. Non-empty, we would have controlled the condition !$_.Image, to more complex conditions would attract comparison operations, logical operations, etc. for Example — where {$_.Image.Width-gt 1000 -and $_.Image.Height-gt 1000} for all illustrations whose width and height is greater than 1000.

Step 4. After we got the list of illustrations we will form for each of them the name of the folder where the picture should be saved. We will do this:

PS X:\> dir $source -r -include *.jpg, *.png, *.gif | select FullName, @{Name="Image"; Expression={New-Object System.Drawing.Bitmap $_.FullName}} -ErrorAction SilentlyContinue | where { $_.Image } | select FullName, @{Name="ImageFolder"; Expression={"{0}\{1}x{2}" -f $target $_.Image.Width, $_.Image.Height}}

With the select command and the formation of a new object, you are already familiar with, of greater interest here is the string formatting. As you can see, it all begins with the string format, and then lists the values that will be used when formatting. Increasingly, in accordance with the method string.Format of .NET with formatting rules can be found in MSDN.

Step 5. Looking at the result of the execution of this function, you will see that already have almost everything we need. Namely, we have a full path to the original illustrations in the same property FullName and path of the destination folder according to the dimensions in the new ImageFolder property. Remained solid imperative to create folders and copy/move the file. For this we use the foreach statement, which allows you to perform other instructions for each object received in the pipe. Look all along it would be this:

PS X:\> dir $source -r -include *.jpg, *.png, *.gif | select FullName, @{Name="Image"; Expression={New-Object System.Drawing.Bitmap $_.FullName}} -ErrorAction SilentlyContinue | where { $_.Image } | select FullName, @{Name="ImageFolder"; Expression={"{0}\{1}x{2}" -f $target $_.Image.Width, $_.Image.Height}} | foreach {if (-not (test-path $_.ImageFolder)) {md $_.ImageFolder}; copy $_.FullName -destination $_.ImageFolder; $_}

As you can see, the foreach is three instructions, separated by semicolons. The second definitely doesn't need any lengthy comment, because is a simple copy. Which, incidentally, can be replaced by the command move to move a file illustration. The first statement is a bit longer, but not much more difficult. In if conditional, we check the absence folder using logical negation and design test-path. If the folder is missing, only in this case, we create it. The third user could not be avoided, but I wanted to show that foreach is not a terminal statement in the halfpipe and after the treatment can be continued. Remember how in the first step, we deduced the values of the variables to the console? So here, the statement $_ prints the object we received in the pipe further into the pipe. Instead you can withdraw anything else. For example, define some variable and display it, for example foreach {....; $result = ...; $result}.
Summing up, these few short lines — there is a little more crowded version of the solution. Well if it's bad it is hard for me to judge. I guess I'm here and then, including what to put in his piggy Bank a fraction of your collective wisdom :)

UPD thank you amirul for example in an imperative style, which I do lazy. I hope this will eliminate some problems with the readability of the code. Although, I must admit, I want to understand and functional approach. It is no more difficult, after all, an intricate-looking string is the elementary decomposition into primitive atoms. It's just unusual, as unusual to many of us, LISP syntax, for example.

Copy Source | Copy HTML
  1. [void][reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
  2. $source = "x:\source"

    $target = "x:\target"

    foreach ($file in dir $source -r-inc *.jpg, *.gif, *.png) {

    try {

    $image = new-object System.Drawing.Bitmap $file.FullName

    $targetdir = "{0}\{1}x{2}" f $target $image.Width, $image.Height

    if (!(test-path $targetdir)) { the

  3. md $targetdir
  4. the
  5. }
  6. the
  7. copy $file $targetdir
  8. the

  9. Write-Host $file -> $targetdir
  10. the
  11. } catch {
  12. the
  13. Write-Host $file " **IS NOT COPIED**"
  14. the
  15. }
  16. the
  17. }
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

Tactoom. How about the middle of blogging?

SumIT Weekend of 18-19 February, the idea for iPad and Hackathon

Knowledge base. Part 2. Freebase: make requests to the Google Knowledge Graph