{"id":241,"date":"2021-03-05T12:34:14","date_gmt":"2021-03-05T20:34:14","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/powershell-community\/?p=241"},"modified":"2021-03-09T08:05:11","modified_gmt":"2021-03-09T16:05:11","slug":"reading-a-text-file-bottom-up","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/powershell-community\/reading-a-text-file-bottom-up\/","title":{"rendered":"Reading a text file bottom up"},"content":{"rendered":"<p><strong>Q:<\/strong> I have a log file in which new data is appended to the end of the file. That means the most recent entries are at the end of the file. I\u2019d like to be able to read the file starting with the last line and then ending with the first line, but I can\u2019t figure out how to do that.<\/p>\n<p><strong>A:<\/strong> There are loads of ways you can do this. A simple way is to use the power of array handling in PowerShell.<\/p>\n<h2>The Get-Content Cmdlet<\/h2>\n<p>Before getting into the solution, let&#8217;s look at the <code>Get-Content<\/code> cmdlet. The <code>Get-Content<\/code> cmdlet always reads a file from start to finish. You can always get the very last line of the file like this:<\/p>\n<pre><code class=\"powershell\">Get-Content -Path C:\\Foo\\BigFile.txt |\n  Select-Object -Last 1\n<\/code><\/pre>\n<p>This is similar to the <code>tail<\/code> command in Linux. As is so often the case, the command doesn&#8217;t quite do what you want it to. That being said, with PowerShell 7, there&#8217;s <em>always<\/em> a way.<\/p>\n<h2>Using Arrays<\/h2>\n<p>We can start by reading through the file from top to bottom. Before displaying those lines to the screen we store them in an array, with each line in the file representing one element in the array.<\/p>\n<p>As you probably know, an array is a collection of objects. An array can hold multiple objects of the same, or different, types. In this case you want the array to hold the lines in your file. Each line is a string. Once you have the lines in the array, you can work backwards to achieve your goal.<\/p>\n<h2>Creating a simple file<\/h2>\n<p>To demonstrate, let&#8217;s start by creating a simple file, and output it to a local text file, like this<\/p>\n<pre><code class=\"powershell-console\">PS C:Foo\\&gt; $File = @'\n&gt;&gt; violet\n&gt;&gt; indigo\n&gt;&gt; blue\n&gt;&gt; green\n&gt;&gt; yellow\n&gt;&gt; orange\n&gt;&gt; red\n&gt;&gt; '@\nPS C:\\Foo&gt; $File | Out-File -Path C:\\Foo\\SmallFile.txt\nPS C:\\Foo&gt; Get-ChildItem -Path C:\\Foo\\SmallFile.txt\n\n    Directory: C:\\Foo\n\nMode                 LastWriteTime         Length Name\n----                 -------------         ------ ----\n-a---          22\/01\/2021    20:13             44 SmallFile.txt\n<\/code><\/pre>\n<p>Once you have created the file, you can get the contents and display it, like this:<\/p>\n<pre><code class=\"powershell-console\">PS C:\\Foo&gt; $Array = Get-Content -Path C:\\Foo\\SmallFile.txt\nPS C:\\Foo&gt; $Array\nviolet\nindigo\nblue\ngreen\nyellow\norange\nred\n<\/code><\/pre>\n<p>Admittedly, all we seem to have done so far is get back to where we started &#8211; displaying the file from the start to the finish not the reverse. So how do we get to where you want to go?<\/p>\n<h2>Arrays vs text files<\/h2>\n<p>There\u2019s an important difference between a text file and an array. With a text file, using <code>Get-Content<\/code>, you read it from only from the start to the finish. Windows, .NET, and PowerShell do not provide a way to read the file in reverse. However, once you have the file contained in an array. it\u2019s easy to read the array from the bottom to the top.<\/p>\n<p>Let&#8217;s start by working out how many lines there are in the array. And, more as a sanity check, display how many lines there are in the file, like this:<\/p>\n<pre><code class=\"powershell-console\">PS C:\\Foo&gt; $Array = Get-Content -Path C:\\Foo\\SmallFile.txt\nPS C:\\Foo&gt; $Length = $Array.count\nPS C:\\Foo&gt; \"There are $Length lines in the file\"\nThere are 7 lines in the file\n<\/code><\/pre>\n<p>So that tells us you have the number of lines in the array that you expected.<\/p>\n<h2>Getting Array Members<\/h2>\n<p>So let&#8217;s give you a solution. In our sample array, <code>$Array<\/code> we have 7 lines. We can address any individual array member directly using <code>[&lt;index&gt;]<\/code> syntax (after the array name). So the first item in the array always has an index number of 0 or <code>$Array[0]<\/code>). In our array, the line <strong>violet<\/strong> has an index number of 0 so you can get to it using <code>$Array[0]<\/code>. Likewise, red has an index number of 6, or <code>$Array[6]<\/code>. But that doesn&#8217;t help us much &#8211; just yet!<\/p>\n<p>A particularly neat feature of array handling in PowerShell is that we can work backwards in an array using negative index values. An index of [-1] is always the last element of an array, [-2] is the penultimate line, and so on. So <code>$Array[-1]<\/code> is Red, <code>$Array[-2]<\/code> is Orange, and so on.<\/p>\n<p>So what we do is to look first at <code>$Array[-1]<\/code>, then <code>$Array[-2]<\/code>, and so on, all withing a simple foreach loop, like this:<\/p>\n<pre><code class=\"powershell-console\">PS C:\\Foo&gt; $Array = Get-Content -Path C:\\Foo\\SmallFile.txt\nPS C:\\Foo&gt; $Length = $Array.count\nPS C:\\Foo&gt; \"There are $Length lines in the file\"\nThere are 7 lines in the file\nPS C:\\Foo&gt; $Line = 1\nPS C:\\Foo&gt; 1..$Length | ForEach-Object {$Array[-$Line]; $Line++}\nred\norange\nyellow\ngreen\nblue\nindigo\nviolet\n<\/code><\/pre>\n<p>This code snippet first sets a variable, <code>$Line<\/code>, to 1. Then you read the file and display how many lines are in the file. You then use <code>ForEach-Object<\/code> to run a script block once for each line in the file. Inside the script block you get the array element starting at the end and output it to the console. Then you increment the line number and repeat.<\/p>\n<p>This may be a little confusing if you haven&#8217;t work with arrays, but once you get the hang of it, you see how simple it really is. Arrays are a fantastic capability within PowerShell.<\/p>\n<h2>For More Information<\/h2>\n<p>For more information on arrays in PowerShell, see <a href=\"https:\/\/docs.microsoft.com\/powershell\/module\/microsoft.powershell.core\/about\/about_arrays\">About_Arrays<\/a>. And for more information on <code>Get-Content<\/code> see the <a href=\"https:\/\/docs.microsoft.com\/powershell\/module\/microsoft.powershell.management\/get-content\">Get-Content<\/a> help page.<\/p>\n<h2>Summary<\/h2>\n<p>So as you saw, <code>Get-Content<\/code> does not read backwards through a file. If you bring the file contents into an array, you can easily read it backwards.<\/p>\n<h2>Tip of the Hat<\/h2>\n<p>This article is based on an earlier Scripting Guys blog article at <a href=\"https:\/\/devblogs.microsoft.com\/scripting\/can-i-read-a-text-file-from-the-bottom-up\/\">Can I Read a Text file from the Bottom Up?<\/a>. I am not sure who wrote the original article.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Q: I have a log file in which new data is appended to the end of the file. That means the most recent entries are at the end of the file. I\u2019d like to be able to read the file starting with the last line and then ending with the first line, but I can\u2019t [&hellip;]<\/p>\n","protected":false},"author":4034,"featured_media":77,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[13],"tags":[14,8],"class_list":["post-241","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell","tag-array","tag-scripting-guys-update"],"acf":[],"blog_post_summary":"<p>Q: I have a log file in which new data is appended to the end of the file. That means the most recent entries are at the end of the file. I\u2019d like to be able to read the file starting with the last line and then ending with the first line, but I can\u2019t [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/posts\/241","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/users\/4034"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/comments?post=241"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/posts\/241\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/media\/77"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/media?parent=241"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/categories?post=241"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/tags?post=241"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}