{"id":55213,"date":"2008-07-16T02:30:00","date_gmt":"2008-07-16T02:30:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2008\/07\/16\/hey-scripting-guy-how-can-i-read-the-mac-address-from-a-text-file\/"},"modified":"2008-07-16T02:30:00","modified_gmt":"2008-07-16T02:30:00","slug":"hey-scripting-guy-how-can-i-read-the-mac-address-from-a-text-file","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-how-can-i-read-the-mac-address-from-a-text-file\/","title":{"rendered":"Hey, Scripting Guy! How Can I Read the MAC Address From a Text File?"},"content":{"rendered":"<p><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Question\" height=\"34\" alt=\"Hey, Scripting Guy! Question\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" width=\"34\" align=\"left\" border=\"0\" \/> <\/p>\n<p>Hey, Scripting Guy! I&#8217;ve been asked to create a script which will read the contents of addresses.txt and output ONLY the MAC addresses listed in the text file. Could you give me some pointers or even a demo script with which to give me a boost?<br \/>&#8212; FR<\/p>\n<p><img decoding=\"async\" height=\"5\" alt=\"Spacer\" src=\"https:\/\/devblogs.microsoft.com\/scripting\/wp-content\/uploads\/sites\/29\/2019\/05\/spacer.gif\" width=\"5\" border=\"0\" \/><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Answer\" height=\"34\" alt=\"Hey, Scripting Guy! Answer\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" width=\"34\" align=\"left\" border=\"0\" \/><a href=\"http:\/\/go.microsoft.com\/fwlink\/?linkid=68779&amp;clcid=0x409\"><img decoding=\"async\" class=\"farGraphic\" title=\"Script Center\" height=\"288\" alt=\"Script Center\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/ad.jpg\" width=\"120\" align=\"right\" border=\"0\" \/><\/a> <\/p>\n<p>Hi FR,<\/p>\n<p>The last time someone asked the person writing this column for a boost, the result was lower back pain that lasted for days. But this is a really common and important scripting problem and the scripts are pretty lightweight. So let\u2019s develop a script that provides a framework for this type of task. You said in your email that you had already figured out how to read the file contents into a variable in your script. But we\u2019ll begin there to be sure that everyone gets the full story.<\/p>\n<p>We need to use the FileSystemObject object. So we have to write a bit of startup code that prepares that object for use.<\/p>\n<pre class=\"codeSample\">Set objFS = CreateObject(\"Scripting.FileSystemObject\")<\/pre>\n<p>Let\u2019s assume that the text file we\u2019re working with is not super large, so loading it into memory is not an issue. In that case, the next step is to slurp up all the content in the file and store it in a variable in your script. You do this by using the reasonably-named ReadAll&nbsp;method.<\/p>\n<pre class=\"codeSample\">Set objFS = CreateObject(\"Scripting.FileSystemObject\")\nSet objFile = objFS.OpenTextFile(\"C:\\logs\\logfile.txt\")\nstrFileContents = objFile.ReadAll\n<\/pre>\n<p>OK,&nbsp;so we now have the contents of C:\\logs\\logfile.txt stored in the variable named strFileContents. To see that this worked, you can display those contents by adding the line WScript.Echo strFileContents to the end of the script.<\/p>\n<table class=\"dataTable\" id=\"EAD\" cellSpacing=\"0\" cellPadding=\"0\">\n<thead><\/thead>\n<tbody>\n<tr class=\"record\" vAlign=\"top\">\n<td class=\"\">\n<p class=\"lastInCell\"><b>Note<\/b>: If you do this, be sure to run the script by typing cscript.exe scriptname.vbs instead of using wscript.exe. That will ensure that the output is displayed in the command prompt window rather than in a popup message box.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"dataTableBottomMargin\"><\/div>\n<p>Now, we want to do something (extract a MAC address in this case) to each line that we read in from the file. But, at this point, we have all of the lines munged together and stored as a single string of text.<\/p>\n<p>Luckily, we haven\u2019t lost the information about where one line ended and another began. Line breaks are stored as characters that your favorite text editor hides from you. (Well, to be fair, it doesn\u2019t really hide them. It interprets them and breaks lines accordingly).<\/p>\n<p>ReadAll stored those line break characters in the strFileContents variable. Because of that, we can use those characters to to break up the stored content into lines and store those lines in a type of variable that can hold a set of things. That means we want to <i>split<\/i> the contents into distinct lines and store the results in an array variable.<\/p>\n<p>VBScript gives us a function that does exactly that. We\u2019ll use two of the Split function\u2019s arguments: a string to split up and a shorter string that indicates where to make the cuts. In our case, we want to snip the string whenever we encounter new line or carriage return linefeed characters. VBScript uses the symbol vbNewLine to indicate a new line.<\/p>\n<p>So, the following code chops the content in strFileContents into lines and stores those lines in the array variable named arrLines.<\/p>\n<pre class=\"codeSample\">arrLines = Split(strFileContents, vbNewLine)<\/pre>\n<p>Now that we have the lines stored in an array, we can use the handy For Each control structure to perform an operation on each line. Just to get warmed up, let\u2019s display the contents of our file with line numbers. I know, that isn\u2019t what FR asked about in his email. But a little experimentation is always a good idea when it comes to learning scripting. The following code should do the&nbsp;trick:<\/p>\n<pre class=\"codeSample\">Set objFS = CreateObject(\"Scripting.FileSystemObject\")\nSet objFile = objFS.OpenTextFile(\"C:\\logs\\logfile.txt\")\nstrFileContents = objFile.ReadAll\n\narrLines = Split(strFileContents,vbNewLine)\niLineNumber = 1\nFor Each strLine in arrLines\n   WScript.Echo iLineNumber &amp; \": \" &amp; strLine\n   iLineNumber = iLineNumber + 1\nNext\n<\/pre>\n<p>The important thing to understand here is that the For Each construct automatically stores the contents of the next line in the strLine variable. It then runs the script between the For Each and Next lines until all of the lines in arrLines have been processed.<\/p>\n<p>Now, FR actually wanted to look for a MAC address in each of the lines and to output just that MAC address. He didn\u2019t provide the exact format of the files he was scripting against. So, we\u2019ll first assume that they are highly-structured with each line comprised of five values separated by commas and that the MAC address always appears in the second position. Here\u2019s an example line:<\/p>\n<pre class=\"codeSample\">192.168.2.3,00-00-00-00-00-00,Account04,08200701,T<\/pre>\n<p>When data is divided \u2013 or <i>delimited<\/i> \u2013 like this, we can extract a portion of the data by using the same trick that we used to recover lines of data after reading it all into a single variable. We can use the Split function. This time, the content to be split is stored in the strLine variable and the delimiting character is the comma.<\/p>\n<p>The following line of code chops up a line into its constituent parts, delineated by commas, and stores those parts in an array called arrData.<\/p>\n<pre class=\"codeSample\">arrData = Split(strLine, \",\")<\/pre>\n<p>The content is now neatly-stored in the arrData array. If we were to do what we did last time, and use the For Each construct, we would end up outputting all of the data. But FR wants only the MAC addresses.<\/p>\n<p>That means we need to output only the second thing that was stored in the arrData array. Arrays are meant to be accessed in that way. The following code displays just the second item stored in the array:<\/p>\n<pre class=\"codeSample\">WScript.Echo arrData(1)<\/pre>\n<p>\u201cWait a minute\u201d, I hear you saying. We want the <i>second<\/i> bit of data and you are using an index of 1 which will, presumably, return the first bit. This is an oddity of array indexing. It starts at 0. So arrData(0) is the first element of the array and arrData(1) is actually the second element \u2013 which is what we want.<\/p>\n<p>Putting everything together, we now have the&nbsp;following:<\/p>\n<pre class=\"codeSample\">Set objFS = CreateObject(\"Scripting.FileSystemObject\")\nSet objFile = objFS.OpenTextFile(\"C:\\logs\\logfile.txt\")\nstrFileContents = objFile.ReadAll\narrLines = Split(strFileContents,vbNewLine)\n\nFor Each strLine in arrLines\n   arrData = Split(strLine, \",\")\n   WScript.Echo arrData(1)\nNext\n<\/pre>\n<p>This script gets the job done if we have structured data. We may need to adjust the index into the arrData array or change the delimiter character from a comma to something else. But it is fairly straightforward to tweak.<\/p>\n<p>If FR\u2019s data isn\u2019t so well-structured, then we need to come up with a correspondingly more flexible way to extract the MAC addresses. A great tool for the job is called regular expressions. There\u2019s nothing regular about regular expressions. They often look very odd. But the idea behind them is simple enough. A regular expression is a string that represents a pattern to match. <\/p>\n<p>We want to look at each line and see if it contains a MAC address. So we need to find a regular expression that matches a MAC address. Regular expressions are great because they are powerful and they\u2019ve been highly-used for so long that you can almost always search the Internet and find the pattern you need. Of course, if you\u2019re interested in learning how to create your own regular expressions, there are loads of great references available \u2013 most of them easy to find with a simple search. A quick search yielded the following regular expression for matching MAC addresses: ((?:(\\d{1,2}|[a-fA-F]{1,2}){2})(?::|-*)){6}.<\/p>\n<p>We can use the same basic scripting framework that we used before. We just need to remove the code that relies on each line being delimited by commas. We\u2019ll replace it with code that checks whether the line includes a match for our MAC address and, if so, outputs the portion of the line that matched.<\/p>\n<p>To use regular expressions in VBScript, you start with the following preparatory statement:<\/p>\n<pre class=\"codeSample\">Set objRegExp = new RegExp<\/pre>\n<p>You then load the regular expression that represents the pattern you want to match.<\/p>\n<pre class=\"codeSample\">objReg.Pattern = \"((?:(\\d{1,2}|[a-fA-F]{1,2}){2})(?::|-*)){6}\"<\/pre>\n<p>Finally, we test to determine if a line includes a match, indicating that it has a MAC address in it. If this test is positive, then we display the&nbsp;line.<\/p>\n<pre class=\"codeSample\">If objRegExp.Test(strLine) Then\n   WScript.Echo strLine\nEnd If\n<\/pre>\n<p>This is nearly what we want. But instead of the whole line, we just want to display the bit of the line that matched the regular expression. This following code does just that. The <b>Execute<\/b> method of the <b>RegExp<\/b> object returns a collection of all of the strings that matched the regular expression stored in the <b>Pattern<\/b> property. We can then use our trusty For Each construct to display all of those matches \u2013 even though we are pretty sure there will be only one match per line.<\/p>\n<pre class=\"codeSample\">Set colMatches = objRegExp.Execute(strLine)\nFor Each strMatch in colMatches\n   WScript.Echo strMatch\nNext\n<\/pre>\n<p>Putting this all together, we end up with the following&nbsp;script:<\/p>\n<pre class=\"codeSample\">Set objFS = CreateObject(\"Scripting.FileSystemObject\")\nSet objRegExp = new RegExp\nobjRegExp.Pattern = \"((?:(\\d{1,2}|[a-fA-F]{1,2}){2})(?::|-*)){6}\"\nSet objFile = objFS.OpenTextFile(\"C:\\logs\\logfile.txt\")\nstrFileContents = objFile.ReadAll\narrLines = Split(strFileContents,vbNewLine)\n\nFor Each strLine in arrLines\n   Set colMatches = objRegExp.Execute(strLine)\n   For Each strMatch in colMatches\n      WScript.Echo strMatch\n   Next\nNext\n<\/pre>\n<p>This script is a good starting point for handling any sort of text file manipulation. Experiment with it. Here are a few things to try:<\/p>\n<table class=\"\" cellSpacing=\"0\" cellPadding=\"0\" border=\"0\">\n<tbody>\n<tr>\n<td class=\"listBullet\" vAlign=\"top\">\u2022<\/td>\n<td class=\"listItem\">\n<p>Read in a file and output the first word that appears on each line<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"listBullet\" vAlign=\"top\">\u2022<\/td>\n<td class=\"listItem\">\n<p>Read in two files and output them one after the other<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"listBullet\" vAlign=\"top\">\u2022<\/td>\n<td class=\"listItem\">\n<p>Read in a file and output the lines in reverse order<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"listBullet\" vAlign=\"top\">\u2022<\/td>\n<td class=\"listItem\">\n<p>Read in a file and only output lines that match a regular expression that you found on the Internet<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>So, there you go FR. We have you covered whether your data is structured or is as out-of-whack as my back!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! I&#8217;ve been asked to create a script which will read the contents of addresses.txt and output ONLY the MAC addresses listed in the text file. Could you give me some pointers or even a demo script with which to give me a boost?&#8212; FR Hi FR, The last time someone asked the [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[3,4,14,5],"class_list":["post-55213","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-scripting-guy","tag-scripting-techniques","tag-text-files","tag-vbscript"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! I&#8217;ve been asked to create a script which will read the contents of addresses.txt and output ONLY the MAC addresses listed in the text file. Could you give me some pointers or even a demo script with which to give me a boost?&#8212; FR Hi FR, The last time someone asked the [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/55213","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=55213"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/55213\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=55213"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=55213"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=55213"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}