<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Scripting - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Scripting - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 08 May 2026 22:32:03 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/scripting/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Find Any File on Windows Like a Linux User (using Windows Powershell) ]]>
                </title>
                <description>
                    <![CDATA[ Sometimes you might struggle to find a file or program when you have no idea where it could be saved or installed. And the Windows user interface may not always give you the results you want. If that' ]]>
                </description>
                <link>https://www.freecodecamp.org/news/find-any-file-on-windows-like-a-linux-user/</link>
                <guid isPermaLink="false">69c44ce410e664c5daef3e59</guid>
                
                    <category>
                        <![CDATA[ Windows ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Powershell ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Scripting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Piotr &quot;NotBlackMagic&quot; Opoka ]]>
                </dc:creator>
                <pubDate>Wed, 25 Mar 2026 16:00:00 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/527c9267-0583-49c4-9e90-89abcf186b9d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Sometimes you might struggle to find a file or program when you have no idea where it could be saved or installed. And the Windows user interface may not always give you the results you want. If that's the case for you, you're in the right place.</p>
<p><code>Get-ChildItem</code> (also known as <code>gci</code>, <code>ls</code>, <code>dir</code> ) is a very powerful command. And one of its most iconic uses is to find/search for a file. It's more precise and more reliable than Windows Explorer. It even has better filtering options that show the results that are more relevant to you.</p>
<p>In this tutorial, you'll learn how to use <code>gci</code> and how to combine it with other commands so that it becomes an even more powerful tool. Remember to enable copy-pasting in Windows PowerShell, so it's easier for you to follow along. You can see how to enable it <a href="https://notblackmagic.hashnode.dev/enable-copy-pasting-in-windows-powershell-cli-in-3-steps">here</a>.</p>
<h3 id="heading-what-well-cover">What we'll cover:</h3>
<ol>
<li><p><a href="#heading-1-basic-explanation-of-the-get-childitem-command">Basic explanation of the Get-ChildItem command</a></p>
<ul>
<li><a href="#heading-most-used-examples-of-searching-by-gci-command">Most used examples of searching by gci command</a></li>
</ul>
</li>
<li><p><a href="#heading-2-setup-for-other-more-complex-examples">Setup for other more complex examples</a></p>
</li>
<li><p><a href="#heading-3-when-is-the-path-option-not-needed">When is the -Path option not needed?</a></p>
</li>
<li><p><a href="#heading-4-advanced-searching-combining-getchildren-with-the-whereobject-command">Advanced Searching – Combining Get-ChildItem with the Where-Object Command</a></p>
<ul>
<li><p><a href="#heading-41-how-to-search-through-only-a-particular-directory">4.1. How to search through only a particular directory</a></p>
</li>
<li><p><a href="#heading-42-how-to-search-while-excluding-a-particular-directory">4.2. How to search while excluding a particular directory</a></p>
</li>
<li><p><a href="#heading-43-searching-only-1-directory-from-many-with-exactly-the-same-name">4.3 Searching only 1 directory from many with exactly the same name</a></p>
</li>
<li><p><a href="#heading-44-filter-how-deep-how-many-folders-in-you-want-to-search-for-the-file">4.4 Filter how deep (how many folders in) you want to search for the file</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-5-how-to-search-through-hidden-files">How to Search Through Hidden Files</a></p>
</li>
<li><p><a href="#heading-6-how-can-you-know-all-the-properties-that-you-can-use-as-a-filter">How can you know all the properties that you can use as a filter?</a></p>
<ul>
<li><a href="#heading-how-to-retrieve-only-1-desired-property">How to retrieve only 1 desired property</a></li>
</ul>
</li>
<li><p><a href="#heading-7-i-dont-know-the-files-name-but-i-know-whats-inside-it-how-do-i-find-the-file-by-its-content">I don't know the file’s name, but I know what's inside it. How do I find the file by its content?</a></p>
</li>
<li><p><a href="#heading-8-i-cant-see-the-full-path-how-do-i-fix-this">I can't see the full path - how do I fix this?</a></p>
</li>
<li><p><a href="#heading-9-hard-to-read-open-the-results-in-the-text-editor-of-your-choice">Hard to read? Open the results in the text editor of your choice</a></p>
</li>
<li><p><a href="#heading-10-summary-the-ultimate-commands-for-searching-and-finding-whatever-you-need">Summary - the ultimate commands for searching and finding whatever you need</a></p>
</li>
</ol>
<h2 id="heading-1-basic-explanation-of-the-get-childitem-command">1. Basic Explanation of the <code>Get-ChildItem</code> Command</h2>
<p>Let's take a look at the example searching script to understand how it works:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Path "C:\path to\your directory\" -Filter "*whatImLookingFor*"
</code></pre>
<p><code>Get-ChildItem</code> (aliases: <code>dir</code>, <code>ls</code>, <code>gci</code>) lists the content of a folder or directory just like the Linux <code>ls</code> command does.</p>
<p>This command works by searching every single file and directory <strong>in the path specified.</strong> It shows you everything it found that <strong>matches the filter</strong>. It doesn't mean that this command doesn't look everywhere else – because it does.</p>
<p>So you specify the path that is the parent (folder), which means that every folder and file under it is its child. If you know some CSS and JavaScript, treat it the same way that these languages do.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/0bd18776-72bc-46be-bbaa-b616c5ce1c3a.png" alt="Picture: a visual explanation of -depth and -recurse parameters. It shows &quot;Documents&quot; folder at the bottom, which is tagged both as Parent and Depth 0. It points upwords to its child folders and a child file. Those are tagged as Depth 0 children of our Documents folder. They are simultaneously tagged as Depth 1 parents, so files and folders. to which they are pointing upwards, are their Depth 1 children." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>If you don't use <code>-Recurse</code> or <code>-Depth</code>, then the command works only in your current directory (parent Depth level 0) and searches for its children inside that directory (children Depth level 0).</p>
<p>If you use <code>-Recurse</code>, then the <code>gci</code> will search for what you want on ALL LEVELS. But by using<code>-Depth</code>, you can specify how deep you want it to look for a file/folder.</p>
<p>To recurse means "to repeat an operation". So, <code>-Recurse</code> means that <code>gci</code> will repeat the search for your file or folder in every child element of the <em>"Documents"</em> directory, and every directory inside it, all levels deep.</p>
<p>All of these files and folders are children of your <em>"Documents"</em> folder. If you delete the folder, you delete everything inside it too.</p>
<p><code>-Filter</code> filters the output of the command to only show what matches the filter (examples of how to use filter are further in the article).</p>
<p><code>-Path</code> tells where the command should be looking for files (by using "C:\", for example, you're telling it to look at the very basis of your computer). If you want to search in certain directory it would look like this:</p>
<pre><code class="language-powershell">Get-ChildItem -Path "C:\path to\your directory\"
</code></pre>
<p>OR</p>
<pre><code class="language-powershell">Get-ChildItem -Path "~\Documents\path to\your directory\"
</code></pre>
<p><code>~\</code> here is a shorthand for "inside current user's folder" or <strong>"C:\Users\YourUsername"</strong>.</p>
<p>Next, we can specify whether we'd like to look for a <strong>file</strong> or a <strong>folder</strong>, so we have fewer results to look at:</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "*whatImLookingFor*" -File
</code></pre>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "*whatImLookingFor*" -Directory
</code></pre>
<p>You might be wondering how you can stop the search if it takes too long. When you're using <code>-Recurse,</code> the output that you'll get might become quite overwhelming, especially if you didn't specify your command enough (more about that in <a href="#heading-3-when-is-the-path-option-not-needed">step 3</a> and <a href="#heading-4-advanced-searching-combining-getchildren-with-the-whereobject-command">step 4</a>). Luckily, you can stop any command in PowerShell after starting it with <strong>Ctrl + C</strong> OR <strong>Ctrl + Z</strong> OR <strong>Ctrl + X</strong>. All of them should work.</p>
<h2 id="heading-most-used-examples-of-searching-by-gci-command">Most Used Examples of Searching by <code>gci</code> Command</h2>
<p>Here are some handy examples of searching scripts that you can use:</p>
<p><strong>Example #1</strong>: search for all executive files on your PC (remember that you can stop this command with one of shortcuts, like <strong>Ctrl + C</strong>):</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "*.exe" -File
</code></pre>
<p>REMEMBER:<br>In order to paste commands into the PowerShell, you have to first enable it. <a href="https://notblackmagic.hashnode.dev/enable-copy-pasting-in-windows-powershell-cli-in-3-steps">Here's how</a>.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768406611072/f23a475e-071d-42d6-b300-f442d7f926c9.png" alt="Picture: gci command pasted into PowerShell." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>This command will show you a very long list of executable files and their folders (as shown in the image below).</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768406617447/c787e9a7-b2b8-4149-84fa-1320f94c7e48.png" alt="Picture: used gci command shows all the executable files it can find." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>These lists might be so long that it's impossible to find anything in them. That's why you'll learn how to use more advanced techniques of filtering in <a href="#heading-4-advanced-searching-combining-getchildren-with-the-whereobject-command">step 4</a> to see fewer unnecessary results that don't fit your criteria.</p>
<p><strong>Example #2</strong>: search for an executable file that has <em>"notepad"</em> in its name (or search for any program you need, basically):</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "notepad*.exe" -File
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768406643047/06e37fba-04d8-4806-839f-19982cd011ea.png" alt="Picture: gci command showing all executable &quot;notepad&quot; files." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>One of the results will show you the location of the file you want:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768406956329/e7582c25-7c96-4b01-aa36-8660d68a4d37.png" alt="Picture: gci command showing the path to the found executable file." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>In our case it's the <code>C:\Windows\System32</code> folder.</p>
<p>You can mix it however you want! Thanks to that command, you don't have to remember much about your file and it will still work.</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "n*pad*.*xe"
</code></pre>
<p>So what if you see some errors while scanning the whole system. Should you worry?</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768406995588/3b23deb1-1530-4fa7-8f52-427386bc37e9.png" alt="Picture: gci command showing error messages while searching for files." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>It's ok! Sometimes you might get lots of errors. They will most likely occur when a script scours the system folders/files. If you want to get rid of them, add <code>-ErrorAction SilentlyContinue</code>, like you see here:</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "notepad*.exe" -File -ErrorAction SilentlyContinue
</code></pre>
<p>You can try it now ;)</p>
<h2 id="heading-2-setup-for-other-more-complex-examples">2. Setup for Other More Complex Examples</h2>
<p>Now, let's look at even more use cases for this command. But first, we'll create a space where I can show you examples.</p>
<p>First, create new folder inside your <em>"Documents"</em> folder. Let's call it <em>"Items"</em>.</p>
<p>Inside it, create two text documents. Name one of them <em>"Item 1- Green Bracelet"</em> and the other <em>"Item 2- Blue Bracelet"</em> (Yes, make sure you write the first letter of each word in <strong>UPPER CASE</strong>).</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768407060476/eeb559d5-2627-4674-a780-e23f4e67f5a9.png" alt="Picture: example setup of files inside &quot;Items&quot; folder inside &quot;Documents folder&quot;." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Copy these files now.</p>
<p>Go one folder back (you can use the <strong>Ctrl + UpArrow</strong> shortcut ) and create another folder next to <em>"Items"</em> called <em>"More items"</em>:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768407053259/06d23003-aaa5-457b-8356-af10327d3436.png" alt="Picture: example setup. New &quot;More items&quot; folder created next to the &quot;Items&quot; folder." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Paste the copied files inside the "More items" folder and change their names, so they have only <strong>lower case</strong> letters (<em>"item 1- green bracelet"</em> and <em>"item 2- blue bracelet"</em> ).</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768407154078/314b476a-8062-4d77-82f0-0ce76cf4c39b.png" alt="Picture: example setup. All files inside &quot;More items&quot; folder have names with only lowercase letters." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>PRO TIP:<br>You can click once on a file with your mouse and then type the <strong>F2</strong> key on your keyboard in order to change their names.</p>
<h3 id="heading-3-when-is-the-path-option-not-needed">3. When is the <code>-Path</code> option not needed?</h3>
<p>You don't have to specify the path every time. You can always just move to the desired directory with the <code>cd</code> (change directory) command.</p>
<p>This command will move you to your <code>Documents</code> folder:</p>
<pre><code class="language-powershell">cd ~\Documents\
</code></pre>
<p>Now, you should be able to see PowerShell pointing to your <code>Documents</code> folder on the left of the screen:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/1cd597e0-ae5e-4ea4-b066-b573a3cc2b4b.png" alt="Picture: PowerShell pointing to the Documents folder." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>If you don't see this, then you can use double quotes <code>" "</code>, like in this command:</p>
<pre><code class="language-powershell">cd "~\Documents\"
</code></pre>
<p>Make sure that PowerShell is pointing to our desired folder. Now, the searching command looks like this without the <code>-Path</code> option:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" -File
</code></pre>
<p>Pretty simple, right?</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768407625727/e83b7cb3-9bdc-420b-b0ae-28fa9a5ceb42.png" alt="Picture: you can first use &quot;cd&quot; command to move to the directory you want. Then you don't have to use  &quot;Path&quot; option in your &quot;gci&quot; command." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>As you can see in the image above, we first moved to our desired directory, so later we could perform the search inside it without specifying the <code>-Path</code> option/parameter.</p>
<p>But the <code>-Path</code> option is very useful, either when you're creating a script or you want to search for something without moving away from the current directory:</p>
<pre><code class="language-powershell">Get-ChildItem -Path ~\Documents\ -Recurse -Filter "*item*" -File
</code></pre>
<pre><code class="language-powershell">Get-ChildItem -Path ~\Documents\ -Recurse -Filter "*item*" -Directory
</code></pre>
<p>Here's an example. I'm inside the <code>System32</code> folder and I want to know whether the thing I'm looking for is inside the <code>Documents</code> folder without moving in there:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768407360312/60915425-8432-40c3-a395-a59e1b363667.png" alt="Picture: &quot;gci&quot; command can looks for a file in a specific directory without moving us to this directory. All thanks to &quot;Path&quot; option." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>And it really is there!</p>
<p>From now on, because you already know what the <code>-Path</code> option is being used for, I won't be using it unless it's necessary.</p>
<h2 id="heading-4-advanced-searching-combining-get-childitem-with-the-where-object-command">4. Advanced Searching – Combining <code>Get-ChildItem</code> with the <code>Where-Object</code> Command</h2>
<p>Sometimes you might have several folders named exactly the same, but they're in different places. You might want to exclude them based on their content, which folder they are in, or based on their<code>-Depth</code> level (see the graphic with the explanation about <code>-Depth</code> level in <a href="#heading-1-basic-explanation-of-the-get-childitem-command">step 1</a>). That's what we're going to cover in the next few points.</p>
<p>For this part of the tutorial, make sure you've gone through <a href="#heading-2-setup-for-other-more-complex-examples">step 2</a> (but you can skip step 3 if you want).</p>
<h3 id="heading-41-searching-through-only-a-particular-directory">4.1. Searching through only a particular directory</h3>
<p>Let's say that we're now looking for the bracelets that we created in <strong>step 2</strong>. But, we want to see the results from only one folder. For that, we'll use case-sensitive search (<code>-clike</code>) to get only our preferred results. But <code>-clike</code> doesn't work with <code>gci</code> alone. We need to apply another filter with the <code>Where-Object { }</code> command:</p>
<pre><code class="language-powershell">Get-ChildItem -Path ~\Documents\ -Recurse -Filter "*item*" |   
Where-Object { $_.Name -clike "*Item*" }
</code></pre>
<p>OR (clearer version, without the <code>-Path</code> option):</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" |   
Where-Object { $_.Name -clike "*Item*" }
</code></pre>
<p>Let's review what's going on here:</p>
<ul>
<li><p><code>Get-ChildItem -Recurse -Filter "*item*"</code> searches for all files and folders with "item" in their name</p>
</li>
<li><p><code>|</code> – the "pipe" symbol is used to get the output of the previous command (the list of all files and folders filtered by <code>gci</code>) and send it to the next command (<code>Where-Object</code> is applying another filter to what is already filtered by <code>gci</code>).</p>
</li>
<li><p><code>Where-Object { }</code> is the command used for filtering the lists of objects. The filter is being specified inside the <code>{ }</code> curly brackets.</p>
</li>
<li><p><code>\(_</code> refers to all the separate objects. Treat it as <em>"ForEachObjectFromList".</em> And treat the whole sequence after the <code>|</code> as <em>"FindObjectsFromList that have a name with 'Item' "</em>.<br><code>\)_</code> is very often used with <code>Where-Object</code>, but also with some other commands.</p>
</li>
<li><p><code>.Name</code> – we choose a Name property to get from every object.</p>
</li>
<li><p><code>-clike</code> finds a match that is 100% correct. All letters must be the exact same case as the phrase we specified. <code>c</code> stands for "case sensitive" and it checks every letter to see if it's <strong>upper case</strong> or <strong>lower case</strong>.</p>
</li>
</ul>
<p>So, <code>Where-Object { $_.Name -clike "*Item*" }</code> is a filter that takes the <code>Name</code> parameter of every object from the list (created by <code>gci</code>) and checks with <code>-clike</code> if any <code>Name</code> has the word "Item" in it.</p>
<p>As you can see in the image below, now we'll get only the files with <strong>upper case</strong> names in our result:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408072301/d2ffaae0-f688-40c6-9c1e-4ddd37159146.png" alt="command looking for file in specific directory case-sensitive" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>IMPORTANT:<br><code>-like</code> alone means that we're looking for a certain pattern, no matter what case the letters are. The <code>c</code> in <code>-clike</code> means that we look for the thing with exactly the same capitalization of the letters (both upper and lower case, hence the <em>"c"</em>).</p>
<p>If you want to see the files <strong>without the upper case</strong> first letter, you can do that by changing "*Item*" from our current command to "*item*":</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" |   
Where-Object { $_.Name -clike "*item*" }
</code></pre>
<p>Let's try it out!</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768407562860/84b82488-8ee5-4b23-ac8f-831d9c586c41.png" alt="Picture: command looking for files with only lowercase letters in their names" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-42-how-to-search-while-excluding-a-particular-directory">4.2. How to search while excluding a particular directory</h3>
<p>In <strong>step 4.1</strong> we learned how to search only for files/folders with specific case-sensitive names in them. After applying only two changes to our previous code, we can exclude certain directories from our search.</p>
<p>Here's our starting command once again:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" |   
Where-Object { $_.Name -clike "*Item*" }
</code></pre>
<h4 id="heading-change-1">Change #1</h4>
<p>In the example above, <code>-clike</code> shows only files/folders <strong>including</strong> specific phrase in their names. If we change it to <code>-cnotlike</code>, we'll <strong>exclude</strong> from the search all files/folders with that specific phrase in their name.</p>
<p>Now our code looks like this:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" |   
Where-Object { $_.Name -cnotlike "*Item*" }
</code></pre>
<h4 id="heading-change-2">Change #2</h4>
<p>After the first change, <code>Where-Object { \(_.Name -cnotlike "*Item*" }</code> only excludes the names, not full paths. In order to avoid that, we need to exclude an actual path to these files. We can do that by changing <code>\)_.Name</code> to <code>$_.FullName</code>, which checks for a certain phrase in the whole path to the file <strong>and</strong> in the file's name.</p>
<p>Now, your command should look like this:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" |   
Where-Object { $_.FullName -cnotlike "*Item*" }
</code></pre>
<p>We excluded the "Items" folder from our search. You should now be able to see the files only from the "More items" directory. Try it out yourself!</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/547801a8-a825-4607-9dbe-42e3c4af238e.png" alt="Picture: excluding part of path with FullName -cnotlike." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>What if you want to exclude the "More items" directory instead? Just change the phrase inside the filter to something like this:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File |   
Where-Object { $_.FullName -cnotlike "*More*" }
</code></pre>
<p>We also changed the name of the file from "*item*" to "*green*" in our <code>gci</code> search (first line of code). That's why now we'll see only one bracelet in our result list:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408276671/f4043a3f-ba14-4ff4-be60-ad5c3e321003.png" alt="command looking for file with exclusion case-sensitive" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>The <code>gci</code> command has two filters applied. First, it searches for files with phrase "green" in their names. The second filter is the "Where-Object" command, which <strong>excludes</strong> anything that has the word "More" in its path. In our case, the "More items" folder got excluded.</p>
<p>We don't even need the case-sensitive filter in our case. The command will work the same when we <strong>exclude</strong> just a <strong>lowercase</strong> word "more". So let's change <code>-cnotlike "*More*"</code> to <code>-notlike "*more*"</code> and see if it's true:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File |   
Where-Object { $_.FullName -notlike "*more*" }
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408250133/f6dac0bd-fc74-4c24-8b4b-9ba780683956.png" alt="Picture: case-sensitive search working the same in current example as a not case-sensitive search." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>As you can see, the result is the same! Despite different cases of the letters, we still got the right <strong>keyword</strong>. So, case-sensitive search isn't always needed&nbsp;– only when you want to be very specific.</p>
<p>Sometimes, being too specific might be bad and make your code not work as intended. To see what I mean, let's look at the example below. Let's apply case-sensitive search once again, but to our unchanged, lowercase keyword "more" and see if it still works:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File |   
Where-Object { $_.FullName -cnotlike "*more*" }
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408273177/ee37dcf7-8720-44b7-aa04-1c09e1cfbe52.png" alt="Picture: case-sensitive search doesn't filter out anything now, because it's too specific. &quot;More items&quot; folder omits the filter now." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Case-sensitive search doesn't filter out anything now, because it's too specific. Both the "Items" and "More items" folders omit the filter now.</p>
<h4 id="heading-faq">FAQ:</h4>
<p>If the <code>Where-Object</code> command is what actually filters the output for us, shouldn't we drop (delete) the <code>-Filter</code> option from <code>gci</code>?</p>
<p>No, we should still use the <code>-Filter</code> option, because it already separates around 99% of the possible files, so the <code>Where-Object</code> command has to work roughly only on 1% of the objects. It makes this part of the command AT LEAST 100 times faster (more often 100,000 times or even faster).</p>
<p>You can try using this command in <code>-Path C:/</code> with and without the <code>-Filter</code> option. In my case, using the <code>-Filter</code> shortened the time needed for the whole sequence of commands to finish from 16 seconds to 8 seconds (first 7.99 seconds is used by <code>gci</code>, so that's why the time got shortened only by a half). That's what we call ✨<em>optimization</em>✨ :D</p>
<h3 id="heading-43-searching-only-1-directory-from-many-with-exactly-the-same-name">4.3 Searching only 1 directory from many with exactly the same name</h3>
<p>We've learned how to search for a phrase anywhere inside the path of a file. But what if we want to search inside exactly the "More items" folder? For that, we'll use the <code>-match</code> filter (which works similarly to the <code>-like</code> filter).</p>
<p>Our phrase will also use "\", instead of "\". This is because "\" is the symbol for a folder, but alone in programming it also has some other features, which we don't want.</p>
<p>This command will look for a match for the "More items" folder in the path of every file from the list. Then, it will show you this file if it matches.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/028f1187-e371-4496-8152-df778337a465.png" alt="Picture: &quot;gci&quot; with a filter for an exact folder." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>What if we want to check for two folders, one next to the other, simultaneously? Very easy! Just connect them with the sign for a folder "\". Here, the command will search inside the "More items" folder only if it's inside the "Documents" folder:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/6faef3fa-e2a8-46ff-bd5c-7c9cc27c1ab8.png" alt="Picture: searching for &quot;DocumentsMore*&quot;" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>As you can see, we didn't use "More items", only "More". You can shorten that filter how you want. It will still be applied to the whole path. See the example below:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File |
Where-Object { $_.FullName -match "s\\Mo*" }
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/817fb9cb-ddd3-4d9f-8ff5-2dbbc3eb7d0d.png" alt="Picture: filter works, even if it could be more specific" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Earlier, we used the <code>not</code> statement in <code>-like</code> filter to exclude certain files and directories. The same can be done with <code>-notmatch</code>:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File | 
Where-Object { $_.FullName -notmatch "ents\\Ite*" }
</code></pre>
<p>Be aware that we're now excluding the "Items" folder from the search, not "More items".</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/acf4d31f-9c8f-4092-b0bd-ed577ca982cf.png" alt="Picture: excluding &quot;Documentstems&quot; folders from search by using &quot;notmatch&quot; filter" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>And, with <code>-cmatch</code> we can apply the same case-sensitive filter as with <code>-clike</code>:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File | 
Where-Object { $_.FullName -cmatch "green*" }
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/7db7d3bb-9a9e-4881-b0e9-4a119c3f93d8.png" alt="7db7d3bb-9a9e-4881-b0e9-4a119c3f93d8" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>I hope you get the gist of it now.</p>
<h3 id="heading-44-filter-how-deep-how-many-folders-in-you-want-to-search-for-the-file">4.4 Filter how deep (how many folders in) you want to search for the file</h3>
<p>Sometimes you might have a very long path to some of your files. If you don't want to waste time searching every folder on your computer recursively, you can use <code>-Depth</code> option. It specifies how many folders to search inside your folder tree. I already showed you the picture of a folder tree in the beginning of this article, but you should take a look at it here once again.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/7a433d89-cfe5-4bf6-8f6d-b7b812414a93.png" alt="Picture: a visual explanation of -depth and -recurse parameters. It shows &quot;Documents&quot; folder at the bottom, which is tagged both as Parent and Depth 0. It points upwords to its child folders and a child file. Those are tagged as Depth 0 children of our Documents folder. They are simultaneously tagged as Depth 1 parents, so files and folders. to which they are pointing upwards, are their Depth 1 children." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>So, how does the <code>-Depth</code> parameter work?</p>
<p><code>-Depth 0</code> means that our command will search only the current folder. It will show results of all children of Depth level 0. Those results are:<br>1 "child file" and 2 "child folders".</p>
<p><code>-Depth 1</code> searches the current folder and its child-folders. It will show the results of all children of Depth level 1. Those results are:<br>1 "child file", 2 "child folders", 2 "grandchild files" and 1 "grandchild folder".</p>
<p><code>-Depth 2</code> searches the current folder and its child and grandchild folders. It will show results of all children of Depth level 2. Those results are:<br>1 "child file", 2 "child folders", 2 "grandchild files", 1 "grandchild folder" and 1 "great grandchild file".</p>
<p>Let's see the difference between these two commands:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" -Depth 0
</code></pre>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" -Depth 1
</code></pre>
<p>The first command will show you only the files and folders inside our current directory.<br>The second command will also search for them inside every folder found inside the current folder.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408448152/2d875580-e463-4903-b99f-d3b457f5eb5a.png" alt="depth parameter explanation" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>For the sake of practice, let's combine it with <code>Where-Object</code> to find the green bracelet:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" -Depth 1 | Where-Object { $_.name -clike"*green*" }
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408504470/4af154e6-9d24-43e4-b228-770da7de9151.png" alt="Picture: gci looking for file with set depth" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>I hope that this example showed you how easy it is to use multiple options ( <code>-Depth</code>, <code>-Recurse</code>) and filters (<code>-Filter</code>, <code>Where-Object</code>).</p>
<h2 id="heading-5-how-to-search-through-hidden-files">5. How to Search Through Hidden Files</h2>
<p>Some files are not that easily accessible to the user. You can see some of the hidden files and folders in Windows Explorer (<a href="https://notblackmagic.hashnode.dev/how-to-see-hidden-files-and-folders-in-windows-file-explorer">here's how</a>). But sometimes it's easier to find what you need if you see <strong>only</strong> those hidden files. That's possible with PowerShell.</p>
<p>The options we're going to use for that are:</p>
<ul>
<li><p><code>-Force</code>: show files otherwise not accessible by the user, such as hidden files.</p>
</li>
<li><p><code>-Hidden</code>: show <strong>only</strong> those hidden files and directories.</p>
</li>
</ul>
<p>This example will search for hidden files in our user's folder:</p>
<pre><code class="language-powershell">gci -Path ~\ -Force -Hidden
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408531210/22319e7b-f9f1-40cb-8037-7abcb9225c3b.png" alt="Picture: gci with -Forece and -Hidden parameters showing hidden files and folders" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Everything here is usually invisible to the typical user. But not for you now :D</p>
<p>The interesting thing is that there are more files not available to the user than the available ones. If you're brave enough, you can see them yourself (Remember! <strong>Ctrl + C</strong> stops the command!):</p>
<pre><code class="language-powershell">gci -Path ~\ -Force -Hidden -Recurse
</code></pre>
<h2 id="heading-6-how-can-you-know-all-the-properties-that-you-can-use-as-a-filter">6. How can you know all the properties that you can use as a filter?</h2>
<p>Up until now, we'vce used some common properties, like <code>Name</code> and <code>Fullname</code>. But there are many others that you might want to access, like <code>CreationTime</code> (date of creating the file) or <code>LastWriteTime</code> (date of last edit of the file).</p>
<p>In this section, I'll first show you how to see all the possible properties. After that, you'll learn how to retrieve only the property you want for scripting purposes.</p>
<p>Go through <strong>step 2</strong> above if you haven't already, because we're going to use the same files that we created before.</p>
<p>Move to the <code>Documents</code> folder in PowerShell.</p>
<p>I hope that this script looks familiar to you now. It searches for files with "item" in their names and checks if these names contain the word "green" (all lowercase letters):</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" | 
Where-Object { $_.Name -clike "*green*" }
</code></pre>
<p>We know that only one file should appear (if you don't trust me, just see for yourself). So, we're going to see every possible property we can use by appending (adding at the end) this fragment of code:<br><code>| Select-Object -Property *</code></p>
<p><code>Select-Object</code> (alias: <code>select</code>) is used for selecting different types of properties. By using an option <code>-Property</code> we tell it to show both values and names of all the properties.</p>
<p>For example:</p>
<p>Name of property: <code>FullName</code><br>Value of property: <code>~\Documents\More items\item 1- green bracelet.txt</code></p>
<p>The asterisk <code>*</code> at the end tells this command to show these names and values for every property possible.</p>
<p>The final version of this command looks like this:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" | 
Where-Object { $_.Name -clike "*green*" } | 
Select-Object -Property *
</code></pre>
<p>Try finding the <code>FullName</code> property in there :D</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408562185/710a4835-a415-4500-af4f-6371a83ae3b6.png" alt="getting all command options or properties" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>This command showed us all possible properties that we can use for that 1 file that it found. If there were more files fitting the filter, then every single one of them would have a similar list of properties. But for different types of files you will get different results.</p>
<h3 id="heading-how-to-retrieve-only-1-desired-property">How to retrieve only 1 desired property</h3>
<p>You've already learned how to check for all possible properties. So, how do we use any of them? Just put one of them instead an asterisk <code>*</code> at the end of the command, like we put <code>CreationTime</code> in here:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File |
Where-Object { $_.Name -clike "*green*" } | 
Select-Object -Property CreationTime
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/060de2e6-909f-4679-987c-e715fb7ee19b.png" alt="Picture: Select-Object shows only the CreationTime property" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>You can use any other property for the sake of this exercise, like <code>LastWriteTime</code>:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File |
Where-Object { $_.Name -clike "*green*" } | 
Select-Object -Property LastWriteTime
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/8223f738-9467-4961-8518-82e010a6e425.png" alt="Picture: Select-Object shows only the LastWriteTime property" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>What if you want to retrieve only the value of the property without its name (because you already know its name and it also messes up your script)? You can retrieve just the value, by changing the <code>-Property</code> to <code>-ExpandProperty</code>:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File |
Where-Object { $_.Name -clike "*green*" } | 
Select-Object -ExpandProperty LastWriteTime
</code></pre>
<p>See the result:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/7f08ecfd-4a19-4276-9d2a-eac53d5cdb93.png" alt="Picture: Changing -Property to -ExpandProperty makes the script to show only the value of the property without its name. the" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h2 id="heading-7-i-dont-know-the-files-name-but-i-know-whats-inside-it-how-do-i-find-the-file-by-its-content">7. I don't know the file’s name, but I know what's inside it. How do I find the file by its content?</h2>
<p>Sometimes it's easier to find a file by searching it by its content. Or perhaps you have lots of similar files and you'd like to check them quickly without opening and closing them. I'll show you some techniques that will let you achieve that in no time.</p>
<p>This command will search every file on your system for the specified word or phrase (in our case, the phrase is "match"):</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -File | 
Select-String -Pattern 'match' -List
</code></pre>
<p>Here's what's happening:</p>
<ul>
<li><p><code>Get-ChildItem -Path C:\ -Recurse -File</code>: as you already know, this part searches for every file on your computer.</p>
</li>
<li><p><code>|</code> – passes the list of files to the next command. So, the next command will search for a certain phrase only in the files listed by <code>gci</code>.</p>
</li>
<li><p><code>Select-String</code> – "String" is a common word in programming used to describe a word/phrase/some text. So, we select the phrase that we want to search for. That phrase is specified by the <code>-Pattern</code> parameter (in our case it's "match").</p>
</li>
<li><p><code>-List</code> tells the command to show only the first found match in every file (great if you want to just see the list of all found files).</p>
</li>
</ul>
<p>Here's an example output of our command:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/cb91d732-c15a-4b10-a127-1a78af2efe63.png" alt="Picture: Select-String showing path to the file and the place in the file where the pattern was found." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Of course, you have quite a lot of files, and some images may also appear in your search (like .svg files that are basically text files that tell the system how to draw an icon). So, it's always best to specify what type of file you're searching for. Let's look for the phrase "red" inside .svg files:</p>
<pre><code class="language-powershell">Get-ChildItem -Filter "*.svg" -Recurse | 
Select-String -Pattern 'red' -List
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/c1d33d66-014b-49af-9e8d-508a581d51fa.png" alt="Picture: gci looking for text inside svg graphic files." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>On the other hand, some text documents will never appear in your search (for example .doc and .docx documents are encoded in such a way that they're impossible to decode without Word).</p>
<p>But in regular text files, you can search for phrases with an emphasis on big and small letters with the <code>-CaseSensitive</code> option. Here, we're going to search for the phrase "github" with only lowercase letters:</p>
<pre><code class="language-powershell">Get-ChildItem -Filter "*.txt" -Recurse | 
Select-String -Pattern 'github' -List -CaseSensitive
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/affa057b-3fc4-402d-9f93-4a1b93bcb98f.png" alt="affa057b-3fc4-402d-9f93-4a1b93bcb98f" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Other options that you'll often use with the <code>Select-String</code> command are:</p>
<ul>
<li><code>Select-String -AllMatch</code> will show you all matches found in every searched file (instead of only 1 match found per file, like with <code>-List</code>).<br><code>Select-String -Context 3</code> shows the three lines of text before and after the line in which the match is found.<br><code>Select-String -Raw</code> won't show you the paths, just the content of the files. This is great for automation and scripts. It's often combined with the <code>-Context</code> option.</li>
</ul>
<p>Let's see some of these options in action:</p>
<pre><code class="language-powershell">Get-ChildItem -Filter "*.txt" -Recurse | 
Select-String -Pattern 'github' -AllMatch -Context 3
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771434300827/4a61d396-755c-49e8-a1f5-f0fd24347b5c.png" alt="looking for file based on its content" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Thanks to the <code>-Context</code> parameter, you can see a total of seven lines (three lines before and three lines after the match) in this file, one after another. This makes it easier to differentiate it from all the other matches found by <code>-AllMatch</code> that might be put in a very similar context.</p>
<p>If you ever feel like there's too much clutter on your screen, you can combine <code>Select-String</code> with <code>Select-Object</code> to get only the paths of the files with matched phrases.</p>
<p>The command below will search every .txt file on your computer for the phrase specified:</p>
<pre><code class="language-powershell">Get-ChildItem -Filter "*.txt" -Recurse | 
Select-String -Pattern 'github' -List
</code></pre>
<p>Let's add the <code>Select-Object -Property Path</code> filter at the end. Now, the command will only show the paths, so there's less clutter on your screen:</p>
<pre><code class="language-powershell">Get-ChildItem -Filter "*.txt" -Recurse | 
Select-String -Pattern 'github' -List | 
Select-Object -Property Path
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/71b9b173-3dd4-40a1-911f-0d494c6a44bb.png" alt="Picture: adding Select-Object makes the results more readable and easier to understand." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Some of the paths are not fully visible. We'll fix that in the next step.</p>
<h2 id="heading-8-i-cant-see-the-full-path-how-do-i-fix-this">8. I can't see the full path - how do I fix this?</h2>
<p>Let's format the results with the <code>Format-Table -Wrap -AutoSize</code> command. <code>-Autosize</code> allows the result to take the whole available space. <code>-Wrap</code> allows wrapping (continuing the text in the next line when it doesn't fit in the space available), which creates more space if it's needed.</p>
<p>Here's an example:</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Filter "*.txt" -Recurse | 
Select-String -Pattern 'github' -List | 
Select -Property Path | 
Format-Table -Wrap -AutoSize
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/bcea9d57-e966-4c41-8d24-bfb3acf95281.png" alt="bcea9d57-e966-4c41-8d24-bfb3acf95281" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Now, you can see the whole paths (or any other results you need) even in PowerShell!</p>
<h2 id="heading-9-hard-to-read-open-the-results-in-the-text-editor-of-your-choice">9. Hard to read? Open the results in the text editor of your choice</h2>
<p>You can send the results of any script/command in two ways:</p>
<p><code>&gt; ~\Documents\command_output.txt</code><br>AND<br><code>| Out-File ~\Documents\command_output.txt</code></p>
<p>Both of these will create a file inside your <code>Documents</code> folder, which you can later open in any program of your choice and edit.</p>
<p>Just add whichever solution you prefer to the end of your command, like here:</p>
<pre><code class="language-powershell">Get-ChildItem -Filter "*.txt" -Recurse | 
Select-String -Pattern 'match' -List | 
Select -Property Path | 
Out-File ~\Documents\command_output.txt
</code></pre>
<p>In the image below, first you'll see the same command, but without exporting the results to another file. The second command, at the bottom of the image, will export the results to the other file without showing them in PowerShell:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771431975160/41f0445e-1a0c-41f6-83b5-446f21e9bea9.png" alt="Picture: gci looking for file based on its content, but showing only paths to the files with found matches." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>You'll see the results from second command after opening the file in any text editor:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771432401191/851770d9-6596-4081-b537-45bf8373ac44.png" alt="Picture: command results are possible to open in any text editor." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>But, what if you can't see the full path even in your text editor?</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771432650086/f21b1bf6-6aad-4799-a34b-c5889b8f8ee7.png" alt="Picture: command results don't show all information you need. They sometimes stop showing, if it's more then default settings allow for." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>To address this, you can add <code>| Format-Table -Wrap -AutoSize</code> right before sending the results to the file:</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Filter "*.txt" -Recurse | 
Select-String -Pattern 'match' -List | 
Select -Property Path | 
Format-Table -Wrap -AutoSize |
Out-File ~\Documents\command_output.txt
</code></pre>
<p>And open the file to see the whole path!</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771434628016/b8bebea1-f6db-4d12-a0f5-70ca18492b9b.png" alt="Picture: bug fixed. Now, you can see all the information." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Just remember that you have to copy each line one by one. Where you see the arrows in the screenshot above is a "newline" character, which you have to delete. Only after doing that can you copy the whole path and paste it into Windows Explorer or into some script.</p>
<h2 id="heading-10-summary-the-ultimate-commands-for-searching-and-finding-whatever-you-need">10. Summary: the Ultimate Commands for Searching and Finding Whatever You Need</h2>
<p><a href="https://github.com/NotBlackMagician/NBM-cheat-sheets/blob/main/windows_powershell/NBM_cheat_sheet_Get-ChildItem_find_any_file_like_on_linux.txt">Here</a> you can download a free cheat sheet with explanations of the commands and examples in one place.</p>
<h3 id="heading-most-used-commands">Most used commands:</h3>
<ul>
<li>Case-sensitive search:</li>
</ul>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "*whatYouNeed*" |   
Where-Object { $_.Name -clike "*whatYouNeed*" } |   
Select-Object { $_.FullName } |
Format-Table -Wrap -AutoSize
</code></pre>
<ul>
<li>Alternatively, send the result to a file:</li>
</ul>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "*whatYouNeed*" |   
Where-Object { $_.Name -clike "*whatYouNeed*" } |   
Select-Object { $_.FullName } |
Format-Table -Wrap -AutoSize |
Out-File ~\Documents\command_output.txt
</code></pre>
<ul>
<li>Search by file's content:</li>
</ul>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse | 
Select-String -Pattern 'what you remember' -AllMatch -Context 2 |
Format-Table -Wrap -AutoSize
</code></pre>
<ul>
<li>Alternatively, send the result to the file:</li>
</ul>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse | 
Select-String -Pattern 'what you remember' -CaseSensitive -AllMatch -Context 2 |
Format-Table -Wrap -AutoSize |
Out-File ~\Documents\command_output.txt
</code></pre>
<p>These commands should work for anything you want to find. I hope you understand now how they function after reading through this tutorial ;)</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>If you want to learn more about these commands, I show you how to work with them in depth in my tutorial <a href="https://notblackmagic.hashnode.dev/learn-windows-powershell-commands-like-a-linux-user">“Learn PowerShell commands like a Linux user”</a>.</p>
<p>If what you found here helped you in any way, consider following me on my social media in order to help me reach further audience: <a href="https://social.linux.pizza/@SecretDevil">Mastodon</a>, <a href="https://www.linkedin.com/in/piotr-opoka-4320143a5/">LinkedIn</a>.</p>
<p>You can also rate me on <a href="https://github.com/NotBlackMagician">Github</a> and support me on <a href="https://ko-fi.com/piotropoka">Ko-fi!</a></p>
<p>Thank you for any support you're able to give. Have a great day!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Useful Nmap Scripts for Ethical Hackers ]]>
                </title>
                <description>
                    <![CDATA[ Nmap is short for Network Mapper. It’s an open-source Linux command-line tool for scanning IP addresses and ports in a network and detecting installed applications. Nmap allows network admins to identify devices running on their network, discover ope... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/useful-nmap-scripts-for-ethical-hackers/</link>
                <guid isPermaLink="false">672e23dd41db65fcc7264dc0</guid>
                
                    <category>
                        <![CDATA[ nmap ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Scripting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ networking ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scanning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Fri, 08 Nov 2024 14:44:45 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731077044881/75a0f1c6-0aae-4ed6-bcfd-777b2ae2b1b6.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Nmap is short for Network Mapper. It’s an open-source Linux command-line tool for scanning IP addresses and ports in a network and detecting installed applications.</p>
<p>Nmap allows network admins to identify devices running on their network, discover open ports and services, and detect vulnerabilities.</p>
<p>Here is the basic syntax to use nmap:</p>
<pre><code class="lang-plaintext">nmap &lt;ip/url&gt;
</code></pre>
<p>Let’s do a quick scan and see what we can find. We can use the URL <a target="_blank" href="http://scanme.nmap.org">scanme.nmap.org</a> to try out a scan. Nmap allows us to use this server to practice scans.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1050/1*daqo4BGtxBZdWF2TLCxQHw.png" alt="Nmap sample scan" width="1050" height="513" loading="lazy"></p>
<p>As you can see, we have found some open ports and services. These act as entry points for further analysis or exploitation.</p>
<p>Nmap is usually the first tool that ethical hackers learn. <a target="_blank" href="https://www.stealthsecurity.sh/p/nmap-tutorial">Here is a full tutorial if you want to learn more about Nma</a><a target="_blank" href="https://www.stealthsecurity.sh/p/nmap-tutorial">p.</a></p>
<h2 id="heading-nmap-scripting-engine">Nmap Scripting Engine</h2>
<p>A key feature is the Nmap Scripting Engine (NSE). It lets users run scripts to do detailed network scans and gather specific information.</p>
<p>Scripts help you perform a list of actions automatically instead of performing them step by step.</p>
<p>These scripts cover a range of functionalities, from service detection to vulnerability scanning. In this article, we’ll look at a few useful Nmap scripts.</p>
<p>I’ll walk you through each script, explain what it does, and show you how to use it. By the end, you’ll have a solid understanding of how to use these scripts as an ethical hacker.</p>
<blockquote>
<p><strong><em>Note:</em></strong> <em>This tutorial is to help you understand network security. Hacking or even scanning another server without permission is illegal.</em></p>
</blockquote>
<h1 id="heading-http-enum"><strong>HTTP-Enum</strong></h1>
<p>Imagine you’re tasked with checking a website’s security and want to see if there are any hidden pages or directories. You suspect there might be admin panels, login pages, or test files that aren’t linked on the main site.</p>
<p>Finding these hidden areas could reveal critical security weaknesses, such as unprotected admin pages or old files that might still hold sensitive information.</p>
<p>The <code>http-enum</code> script is used to scan a web server and find common directories and files that might be hidden from the main site navigation.</p>
<p>Think of it like opening doors in a building to see what’s behind each one. It searches for paths like login pages, admin panels, config files, and other directories that aren’t typically linked on the main website.</p>
<p>For example, a login page or an admin section may exist at specific paths but aren’t visible to regular users. This information is useful because knowing these locations can help you identify security weak points.</p>
<p>Here is the command to run the http-enum script:</p>
<pre><code class="lang-plaintext">nmap - script http-enum -p 80 &lt;target-ip&gt;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730379312717/5e6300c2-0030-4400-b998-e395c0b69a4f.png" alt="http-enum sample response" width="1413" height="923" loading="lazy"></p>
<p>As you can see, the above sample result shows /login.php, /docs and other exposed URL paths. These can be entry points to find restricted information in a web server.</p>
<h1 id="heading-smb-os-discovery"><strong>SMB-OS-Discovery</strong></h1>
<p>Suppose you’re exploring a company’s network to understand what kind of systems they have in place, specifically in a Windows environment.</p>
<p>Knowing the exact operating system and version of each server helps you assess vulnerabilities. For example, an older version of Windows might have unpatched flaws that need attention.</p>
<p>The <code>smb-os-discovery</code> script targets servers that use the SMB protocol, mainly found in Windows environments, to gather information about the server’s operating system. It can reveal details like the Windows version, the server name, and its domain.</p>
<p>This script helps you understand what type of system you’re dealing with, which is key for checking security flaws specific to that OS.</p>
<p>Here is the syntax to run the smb-os-discovery script.</p>
<pre><code class="lang-plaintext">nmap - script smb-os-discovery -p 445 &lt;target-ip&gt;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730444262829/c8f76aee-d0a6-4203-b572-17df1272211c.png" alt="smb-os-discovery sample result" width="1200" height="850" loading="lazy"></p>
<p>As you can see in the above sample result, the script connects to the SMB service on the target and retrieves OS information. This can help you quickly identify the Windows version and other details about the server.</p>
<h1 id="heading-http-headers"><strong>HTTP-Headers</strong></h1>
<p>Imagine you’re evaluating a website’s configuration and security settings. You want to know what kind of server it’s running, what methods are allowed, and if it’s enforcing HTTPS connections.</p>
<p>These details give you insights into whether the server’s configuration aligns with best practices, helping you spot any missing security settings.</p>
<p>The <code>http-headers</code> script checks the headers sent by a web server when a user connects to it it. Headers tell you the server type (like Apache or NGINX), security settings (like HTTPS requirements), allowed methods, and caching rules.</p>
<p>These details are like the server’s blueprint for communication, often revealing if the server has certain protections enabled.</p>
<p>Here is the syntax to run the http-headers script:</p>
<pre><code class="lang-plaintext">nmap - script http-headers -p 80 &lt;target-ip&gt;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730380306638/703870e4-de5b-4858-b1b2-20634a8598a9.png" alt="http-headers sample response" width="1427" height="905" loading="lazy"></p>
<p>You can see that the sample response shows headers like like <code>X-Powered-By</code>, <code>Set-Cookie</code>, and so on. These headers can help to find security issues such as cross-site scripting (XSS) and clickjacking.</p>
<h1 id="heading-ssh-brute"><strong>SSH-Brute</strong></h1>
<p>Let’s say you’re testing a server’s defenses against unauthorized access through SSH. You know that weak passwords are a common risk, so you need a way to check if any accounts have easily guessable credentials.</p>
<p>This test will help you identify weak SSH logins that need stronger passwords to protect the server.</p>
<p>The <code>ssh-brute</code> script tries to log into an SSH server by guessing usernames and passwords. SSH, or Secure Shell, is often used for remote logins.</p>
<p>If the usernames and passwords are easy to guess, this script might find a way in. It’s a useful test to see if login credentials are strong enough to prevent unauthorized access.</p>
<p>Here is the syntax to run the ssh-brute script:</p>
<pre><code class="lang-plaintext">nmap - script ssh-brute -p 22 &lt;target-ip&gt;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730811335746/c6f6ad27-37d7-467d-8f5c-cd71a37aff0f.jpeg" alt="ssh-brute sample response" width="1219" height="834" loading="lazy"></p>
<p>As you can see, this script tries different username-password combinations on the SSH server. If successful, it will display the correct credentials.</p>
<h1 id="heading-dns-brute"><strong>DNS-Brute</strong></h1>
<p>Imagine you’re mapping out a company’s network and want to see if they have any subdomains that aren’t publicly listed. Each subdomain might serve a different purpose, such as hosting email servers or internal testing sites.</p>
<p>Discovering these subdomains helps you check if any of them are exposing sensitive services.</p>
<p>The <code>dns-brute</code> script helps you find subdomains associated with a given domain by trying out common names, like “www,” “mail,” or “ftp.” Subdomains can host separate services and applications, each with its own set of vulnerabilities.</p>
<p>Here is the syntax to run the dns-brute script:</p>
<pre><code class="lang-plaintext">nmap - script dns-brute &lt;target-domain&gt;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730379500675/c8c646c3-1d76-440a-8092-6ce26f9aa127.png" alt="dns-brute script" width="1408" height="923" loading="lazy"></p>
<p>As you can see, the script attempts to resolve a list of common subdomains, and finds one internal hostname. Using this script can reveal subdomains that aren’t listed in public records, helping you to gain a fuller picture of an organization’s network layout.</p>
<h1 id="heading-conclusion"><strong>Conclusion</strong></h1>
<p>These Nmap scripts provide a powerful way to audit, troubleshoot, and secure networks. By understanding what each script does and how to use it, you’ll be able to uncover hidden issues and safeguard your infrastructure.</p>
<p><strong>To learn how to build a career in Cybersecurity, check out</strong> <a target="_blank" href="https://book.stealthsecurity.sh/?utm_source=www.stealthsecurity.sh&amp;utm_medium=newsletter&amp;utm_campaign=top-cybersecurity-certifications-you-should-know-about"><strong><em>The Hacker’s Han</em></strong></a><a target="_blank" href="https://book.stealthsecurity.sh/?utm_source=www.stealthsecurity.sh&amp;utm_medium=newsletter&amp;utm_campaign=top-cybersecurity-certifications-you-should-know-about"><strong><em>dbook</em>. To practice ha</strong></a><strong>cking real systems and get help from other hackers, join</strong> <a target="_blank" href="https://www.skool.com/hackershub?utm_source=www.stealthsecurity.sh&amp;utm_medium=newsletter&amp;utm_campaign=top-cybersecurity-certifications-you-should-know-about"><strong><em>The Hacker’</em></strong></a><a target="_blank" href="https://www.skool.com/hackershub?utm_source=www.stealthsecurity.sh&amp;utm_medium=newsletter&amp;utm_campaign=top-cybersecurity-certifications-you-should-know-about"><strong><em>s Hub</em>.</strong></a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Tampermonkey to Improve a Website's UI – Example Using freeCodeCamp ]]>
                </title>
                <description>
                    <![CDATA[ What is Tampermonkey? Tampermonkey is a browser extension that lets you add custom scripts to websites, making them work or look the way you want. It's like giving websites a makeover or adding new features.  These scripts are called userscripts and ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/customize-website-experience-with-tampermonkey/</link>
                <guid isPermaLink="false">66ba2e3eab41bfc0b9b131e2</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Scripting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tasnim Ferdous ]]>
                </dc:creator>
                <pubDate>Tue, 22 Aug 2023 22:32:35 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/08/cover_image.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="heading-what-is-tampermonkey">What is Tampermonkey?</h2>
<p>Tampermonkey is a browser extension that lets you add custom scripts to websites, making them work or look the way you want. It's like giving websites a makeover or adding new features. </p>
<p>These scripts are called userscripts and you can make tampermonkey run those scripts when you visit a particular site.</p>
<h2 id="heading-how-to-use-tampermonkey-to-customize-the-user-experience-of-a-website">How to Use Tampermonkey to Customize the User Experience of a Website</h2>
<p>There are a lot of use cases for Tampermonkey. The most obvious one is adding your own custom styling. You can add custom css for a specific site and change up the appearance as you want. But as you can run a script, you can also manipulate the DOM elements. </p>
<p>I will list out of the things I have done to give you some ideas to what is possible.</p>
<ol>
<li>Increase readability by changing font properties.</li>
<li>Remove ads on sites that don't allow an ad blocker.</li>
<li>Declutter a site so that you can focus on the portion that you're interested in.</li>
<li>Add keyboard shortcuts for repetitive tasks.</li>
<li>Add buttons for custom actions.</li>
<li>Automatically fill out form data.</li>
</ol>
<p>Today you'll get a glimpse of what you can do with Tampermonkey by writing scripts that'll work on the freeCodeCamp /news site. </p>
<p>First we will see how to declutter for a more focused reading experience. Then we will attach copy buttons on the code snippets. And lastly, we'll automatically generate table of contents that you can access with a toggle button. </p>
<p>The source code is available on <a href="https://github.com/renzhamin/freecodecamp-enhancer" target="_blank">GitHub</a>.</p>
<p>It's worth pointing out that any changes your script makes will only be available in your browser. So as long as you don't deal with any sensetive data on that site, you can go as wild as you want. </p>
<p>But be aware that some sites may have some policy in regards to using third party JavaScript and take disciplinary action if you violate that policy.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>As we will be modifying a website, basic knowledge of HTML, CSS, and JavaScript is required to go through this tutorial. Some experience with DOM manipulation would be great as well. </p>
<p>If you can effectively manipulate exisiting DOM elements, that will allow you to write Tampermonkey scripts to make yourself more productive on any website. </p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#how-to-install-tampermonkey">How to Install Tampermonkey</a></li>
<li><a class="post-section-overview" href="#how-to-create-a-new-userscript">How to Create a New Userscript</a></li>
<li><a class="post-section-overview" href="#how-to-change-a-buttons-behaviour">How to Change a Button's Behaviour</a></li>
<li><a class="post-section-overview" href="#how-to-implement-reader-view-for-freecodecamp-news-articles">How to Implement Reader View for freeCodeCamp /news Articles</a></li>
<li><a class="post-section-overview" href="#how-to-add-copy-buttons-to-the-code-snippets">How to Add Copy Buttons to the Code Snippets</a></li>
<li><a class="post-section-overview" href="#how-to-auto-generate-a-table-of-contents">How to Auto-generate a Table of Contents</a></li>
<li><a class="post-section-overview" href="#key-takeways">Key Takeways</a></li>
</ul>
<p><a></a></p>
<h2 id="heading-how-to-install-tampermonkey">How to Install Tampermonkey</h2>
<p>For Chrome, the extension is available on the <a href="https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo" target="_blank">chrome web store.</a></p>
<p>It's also available for Firefox, which you can install from <a href="https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/" target="_blank">Firefox Add-Ons.</a></p>
<p>For other browsers, you can visit the <a href="https://www.tampermonkey.net/index.php" target="_blank">Tampermonkey Home Page</a>. Currently, Chrome, Firefox, Edge, Safari and Opera are officially supported. But the one from Chrome web store works fine on Chromium-based browsers like Brave.</p>
<p><a></a></p>
<h2 id="heading-how-to-create-a-new-userscript">How to Create a New Userscript</h2>
<p>The easiest way to get started is using the <code>create new script</code> option from the toolbar.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/create-new-script.jpeg" alt="create new script" width="600" height="400" loading="lazy"></p>
<p>You will be presented with something like this:</p>
<pre><code><span class="hljs-comment">// ==UserScript==</span>
<span class="hljs-comment">// @name         New Userscript</span>
<span class="hljs-comment">// @namespace    http://tampermonkey.net/</span>
<span class="hljs-comment">// @version      0.1</span>
<span class="hljs-comment">// @description  try to take over the world!</span>
<span class="hljs-comment">// @author       You</span>
<span class="hljs-comment">// @match        https://www.freecodecamp.org/news/*</span>
<span class="hljs-comment">// @icon         https://www.google.com/s2/favicons?sz=64&amp;domain=freecodecamp.org</span>
<span class="hljs-comment">// @grant        none</span>
<span class="hljs-comment">// ==/UserScript==</span>

(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
<span class="hljs-meta">    'use strict'</span>;

    <span class="hljs-comment">// Your code here...</span>
})();
</code></pre><p>The userscript header contains information about the script and other important parameters as well. </p>
<p>For now the relevant part is <code>@match</code>. This tells Tampermonkey for which sites to run the script. I have changed to match any article on the freeCodeCamp news site by using the "*" wildcard.</p>
<p>Test it out by putting something simple as <code>alert("HI")</code> on the function. Then navigate to any article on freecodecamp.org/news. </p>
<p>Lets do something interesting next. This will be a brief introduction to DOM manipulation. </p>
<p>Before writing code in the userscript, it's better to write out your code in the dev console first. Then, when you have working code, you can just paste it in the Tampermonkey script. This is how we will write all the scripts in this article as well.</p>
<p><a></a></p>
<h2 id="heading-how-to-change-a-buttons-behaviour">How to Change a Button's Behaviour</h2>
<p>At the end of each freeCodeCamp article there is a button that says "Tweet a thanks". You can see the onclick function of the button by using the element picker from the dev tools and clicking on that button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/tweet-btn.jpeg" width="600" height="400" alt="tweet-btn" loading="lazy"></p>
<p>Clicking on that button will open a new window in Twitter with some default text for a tweet like the following:</p>
<pre><code>Thank you @twitter-username-<span class="hljs-keyword">of</span>-author <span class="hljs-keyword">for</span> writing <span class="hljs-built_in">this</span> helpful article.

Title <span class="hljs-keyword">of</span> Article
<span class="hljs-attr">https</span>:<span class="hljs-comment">//www.freecodecamp.org/news/slug-of-article</span>
</code></pre><p>Let's say that you want to change up the default text to something like this:</p>
<pre><code>This article is quite fascinating.

Title <span class="hljs-keyword">of</span> Article by @twitter-username-<span class="hljs-keyword">of</span>-author
<span class="hljs-attr">https</span>:<span class="hljs-comment">//www.freecodecamp.org/news/slug-of-article</span>
</code></pre><p>The new text is static as you will chose it, but there are some variables here. You need to extract these 3 things:</p>
<ol>
<li>The link of the article.</li>
<li>The Twitter username of the author.</li>
<li>Title of the article.</li>
</ol>
<p>Extracting the link is as <code>location.href</code>.</p>
<p>Now how to extract the Twitter username?</p>
<p>At first glance, you may think the name after the author image is the answer. But that's actually the author's name (which is not necessarily unique) – it's not their Twitter username. So, where would you start looking for it?</p>
<p>Since you can just click the button or inspect it, you already know the values of all these variables. So a good way to find the Twitter username would be just to search for it in the entire HTML document. As it is unique, there won't be many occurrences. </p>
<p>Open the dev tools and search for the Twitter username. You will find this tag:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"twitter:creator"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"@username"</span> /&gt;</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/twitter-username-search.jpeg" width="600" height="400" alt="twitter-username-search" loading="lazy"></p>
<p>Which is not surprising, as most publishing sites put information such article title, tags, description, and author information in the meta tags for SEO purposes. But most of the time you can just find what you are looking for by using the inspection tool.</p>
<p>So how do we extract the "content" property of this meta tag? Considering that an article is written by only one author, there will always be only one meta tag with name "twitter:creator". So you can just use the querySelector.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> username = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'meta[name="twitter:creator"]'</span>).content
</code></pre>
<p>Now on to extracting the title. If you have wandered around looking for the Twitter username, you will find that there is also a meta tag with name "twitter:title". But this time, the title is something that you can visually see and inspect. In most cases that's the easier way to go about it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/article-title.png" width="600" height="400" alt="article-title" loading="lazy"></p>
<p>If you inspect on the article title above the cover image, you will see it's inside an h1 with the class "post-full-title". You can select this with the following:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> title = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"h1.post-full-title"</span>).textContent
</code></pre>
<p>Now we have all the pieces we need to make the change. Inspect the target button and you will see it comes with an id of "tweet-btn". Now let's define our text.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> text = <span class="hljs-string">`This article is quite fascinating.

<span class="hljs-subst">${title}</span> by <span class="hljs-subst">${username}</span>
<span class="hljs-subst">${location.href}</span>
`</span>
</code></pre>
<p>Putting it all together by changing the button's onclick, here's what you should have:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">change_tweet_text</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"tweet-btn"</span>).onclick = <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">const</span> username = <span class="hljs-built_in">document</span>.querySelector(
            <span class="hljs-string">'meta[name="twitter:creator"]'</span>
        ).content
        <span class="hljs-keyword">const</span> title = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"h1.post-full-title"</span>).textContent

        <span class="hljs-keyword">const</span> text = <span class="hljs-string">`This article is quite fascinating.

<span class="hljs-subst">${title}</span> by <span class="hljs-subst">${username}</span>
<span class="hljs-subst">${location.href}</span>
`</span>

        <span class="hljs-keyword">const</span> share_link = <span class="hljs-built_in">encodeURI</span>(
            <span class="hljs-string">`https://twitter.com/intent/tweet?text=<span class="hljs-subst">${text}</span>`</span>
        )

        <span class="hljs-built_in">window</span>.open(share_link, <span class="hljs-string">"share-twitter"</span>, <span class="hljs-string">"width=550, height=235"</span>)

        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
    }
}
</code></pre>
<p>Because the text contains newline characters, we have to convert it url-encoded format.</p>
<p>Paste the function inside the userscript and call it.</p>
<pre><code class="lang-js">;(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    change_tweet_text()
})()
</code></pre>
<p>Now reload the article page and click on the button to see if it's working as expected.</p>
<p>It is possible that it won't work as expected. Especially if the page takes a lot of time to load. </p>
<p>With this script, we are modifying a button which is a DOM element. But what if the element is not yet loaded? Then you will face unexpected results. </p>
<p>Tampermonkey provides the "// @run-at" header which you can specify to "document-end", but I've found that it still produces unexpected results sometimes. </p>
<p>The right way to mitigate it is to use the "load" event which is emitted when the whole page is loaded. So, we can refactor our driver function in the following way:</p>
<pre><code class="lang-js">addEventListener(<span class="hljs-string">"load"</span>, main)

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    change_tweet_text()
}
</code></pre>
<p>You can be extra careful and throw a delay as well:</p>
<pre><code class="lang-js">addEventListener(<span class="hljs-string">"load"</span>, <span class="hljs-built_in">setTimeout</span>(main, <span class="hljs-number">2000</span>))
</code></pre>
<p>From now on, we will call all the functions in this main function which will run after the load event is fired.</p>
<p>Note that you could have achieved the same result using a regular expression. In that case, you could extract the variables from the button's onclick which contains the URL with the full text. </p>
<p>But extracting information from the tags is better because if one day the default tweet text changes, you would need the change the regex as well.</p>
<p><a></a></p>
<h2 id="heading-how-to-implement-reader-view-for-freecodecamp-news-articles">How to Implement Reader View for freeCodeCamp /news Articles</h2>
<p>If you make your browser fullscreen, the top navigation bar still stays at the top – which is not the best reading experience. So, we want to hide everything except the main article body. </p>
<p>Let's start inspecting. You will see that, close to the "main" tag, there is an "article" tag with class "post". It contains all the text of the article including the heading and cover image.</p>
<p>This time, we will live-edit the CSS of the page. In Firefox, you can use the "Style Editor" tab in the dev tools. Click on the "+" icon and start testing out your CSS.</p>
<p>As of now, there is no built-in way to hide all elements except one (in our case article.post) using CSS. You might be tempted to use <code>:not(article.post)</code> but it won't work because if any ancestor of the element is hidden, then all the descendents will be hidden as well. </p>
<p>We can accomplish our goal by using the ":has" selector. <code>:has(article.post)</code> will select all the ancestors of article.post so we can select the inverse of it with <code>:not(:has(article.post))</code>. </p>
<p>But there's still a problem: the descendent of article.post is also ignored by this selector. We can bring them back by chaining another not –<code>:not(article.post, article.post *)</code>. This will select everything that is not an article.post or a descendent of it.</p>
<p>This is the final CSS:</p>
<pre><code class="lang-css"><span class="hljs-selector-pseudo">:not(</span><span class="hljs-selector-pseudo">:has(article.post))</span><span class="hljs-selector-pseudo">:not(article.post</span>, <span class="hljs-selector-tag">article</span><span class="hljs-selector-class">.post</span> *) {
    <span class="hljs-attribute">display</span>: none;
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/style-editor.png" width="600" height="400" alt="style-editor" loading="lazy"></p>
<p>At the time of writing this, the ":has" selector is experimental in Firefox. But you can go to <code>about:config</code> and enable it by changing the value of <code>layout.css.has-selector.enabled</code> to true.</p>
<p>What if you want to use the navigation bar? It would be a shame to manually write the CSS and remove it when you don't want it, right? Especially now that you can do some scripting.</p>
<p>We will add a keyboard shortcut to the site that will make the browser fullscreen. We'll also apply that CSS and, upon exiting fullscreen, the CSS will be removed.</p>
<p>One of the exposed APIs of tampermonkey is <code>GM_addStyle(css)</code> for adding CSS. For this API to work, you need to include <code>// @grant           GM_addStyle</code> to the userscript header and also remove <code>// @grant none</code> if you have that.</p>
<p>To create the shortcut, you will use a "keydown" event listener.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add_declutter_toggle</span>(<span class="hljs-params">key</span>) </span>{
    <span class="hljs-keyword">const</span> css = <span class="hljs-string">`
        .declutter :not(:has(article.post)):not(article.post, article.post *) {
            display: none;
        }`</span>

    GM_addStyle(css)

    <span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"keydown"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (event.key === key) {
            <span class="hljs-keyword">if</span> (<span class="hljs-built_in">document</span>.fullscreen) {
                <span class="hljs-built_in">document</span>.exitFullscreen()
                <span class="hljs-built_in">document</span>.body.classList.remove(<span class="hljs-string">"declutter"</span>)
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">document</span>.body.classList.add(<span class="hljs-string">"declutter"</span>)
                <span class="hljs-built_in">document</span>.documentElement.requestFullscreen()
            }
        }
    })
}
</code></pre>
<p>We are applying the CSS to the elements with the class "declutter". By toggling the "declutter" on the body we are essentially toggling the CSS on the entire page.</p>
<p>Now, we just have to add the function call in our main function.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    change_tweet_text()
    add_declutter_toggle(<span class="hljs-string">"F"</span>)
}
</code></pre>
<p>Reload the page and you will see that pressing "F" will toggle fullscreen and also remove everything except article body when in the fullscreen state.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/declutter_demo.gif" alt="declutter demo" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h2 id="heading-how-to-add-copy-buttons-to-the-code-snippets">How to Add Copy Buttons to the Code Snippets</h2>
<p>The first step is always identifying the correct selector. If you inspect any code snippet you will see that they're contained in <code>&lt;code&gt;</code> blocks and have a <code>&lt;pre&gt;</code> as a parent. So, to select all code snippets, the selector can be <code>pre &gt; code</code>. </p>
<p>Now the question is where to place the button? For easy access, we want the button to be on top of the code snippet and aligned to the right. You may think, placing it between the <code>&lt;pre&gt;</code> tag and <code>&lt;code&gt;</code> tag
could work – but that will interfere with the content of the code snippet which is not ideal.</p>
<p>The ideal solution is wrapping the <code>&lt;pre&gt;</code> tag with a div and creating the structure as <code>div &gt; button &gt; pre &gt; code</code>.</p>
<p>The copying is done by the clipboard API using the <code>navigator.clipboard.writeText</code> method. We will also apply some styling and let the reader know their copying was successful by changing the button text from "copy" to "copied" for a small duration of time.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">attach_code_copy_btn</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> css = <span class="hljs-string">`
        .copy-btn {
            float: right;
            margin-bottom: 5px;
            border-radius: 1rem;
            font-size: 0.8em;
            width: 7rem;
        }

        .pre-wrapper {
            width: 100%;
        }
    `</span>
    GM_addStyle(css)

    <span class="hljs-keyword">const</span> codes = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">"pre &gt; code"</span>)
    codes.forEach(<span class="hljs-function">(<span class="hljs-params">code</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> pre = code.parentElement
        <span class="hljs-keyword">const</span> btn = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"button"</span>)
        btn.textContent = <span class="hljs-string">"copy"</span>
        btn.classList.add(<span class="hljs-string">"copy-btn"</span>)
        btn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span>
            navigator.clipboard.writeText(code.textContent).then(<span class="hljs-function">() =&gt;</span> {
                btn.textContent = <span class="hljs-string">"copied"</span>
                <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> (btn.textContent = <span class="hljs-string">"copy"</span>), <span class="hljs-number">2500</span>)
            })
        )
        <span class="hljs-keyword">const</span> div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>)
        div.classList.add(<span class="hljs-string">"pre-wrapper"</span>)
        div.appendChild(btn)
        wrap(pre, div)
    })
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">wrap</span>(<span class="hljs-params">elem, wrapper</span>) </span>{
    elem.parentNode.insertBefore(wrapper, elem)
    wrapper.appendChild(elem)
}
</code></pre>
<p>The <code>attach_code_copy_btn</code> function finds all code snippets and wraps them around a div, adds classes to them, and attaches a button which will copy the code content on click.</p>
<p>Call the function just as before:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    change_tweet_text()
    add_declutter_toggle(<span class="hljs-string">"F"</span>)
    attach_code_copy_btn()
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/copy_demo.gif" alt="copy demo" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<p>Heres some practice, try adding click event listeners to all <code>&lt;code&gt;</code> tags that are not inside a <code>&lt;pre&gt;</code> tag. On a mouse click on the <code>&lt;code&gt;</code> block the contents of the  block will be copied.</p>
<h2 id="heading-how-to-auto-generate-a-table-of-contents">How to Auto-generate a Table of Contents</h2>
<p>freeCodeCamp is a great place for reading in-depth articles. The articles are well organized as well. Most authors use proper headings and provide a table of contents. But the ToC is not accessible on every part of the page. </p>
<p>If you want to jump somewhere you will have to go back to the ToC then click on your section of interest. And then there are shorter articles that don't provide a ToC at all.</p>
<p>That's where we can introduce a userscript that will automatically generate a table of contents which is accessible anywhere on the page. This improves the user experience when reading longer articles. </p>
<p>The first thing you should know is how a ToC works. A section in the ToC is just an anchor tag with an href value of <code>#some-section</code>. Here "some-section" can be the name property of an anchor tag of the target location (for example, <code>&lt;a name="some-section"&gt;</code>) or if its used as an id on any element (for example, <code>&lt;h2 id="some-section"&gt;</code>).</p>
<p>If you inspect on any heading tag, you will see that the id attribute is present. This is automatically inserted when the article page is built. So we can use that for our ToC.</p>
<p>This is a ToC generator function modified from a <a href="https://stackoverflow.com/questions/187619/is-there-a-javascript-solution-to-generating-a-table-of-contents-for-a-page" target="_blank">StackOverflow answer</a>.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generate_toc</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> toc = <span class="hljs-string">""</span>
    <span class="hljs-keyword">let</span> level = <span class="hljs-number">1</span>

    <span class="hljs-keyword">let</span> container = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".post-content"</span>)
    <span class="hljs-keyword">const</span> regex = <span class="hljs-regexp">/&lt;h([2-5])\s+id="([^"]+)"&gt;([^&lt;]+)&lt;\/h([2-5])&gt;/gi</span>
    <span class="hljs-keyword">const</span> matches = [...container.innerHTML.matchAll(regex)]
    matches.forEach(<span class="hljs-function">(<span class="hljs-params">match</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (match.length != <span class="hljs-number">5</span>) <span class="hljs-keyword">return</span>
        <span class="hljs-keyword">const</span> [_, openLevel, id, titleText, closeLevel] = match

        <span class="hljs-keyword">if</span> (openLevel !== closeLevel) {
            <span class="hljs-keyword">return</span>
        }

        <span class="hljs-keyword">if</span> (openLevel &gt; level) {
            toc += <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(openLevel - level + <span class="hljs-number">1</span>).join(<span class="hljs-string">"&lt;ol&gt;"</span>)
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (openLevel &lt; level) {
            toc += <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(level - openLevel + <span class="hljs-number">1</span>).join(<span class="hljs-string">"&lt;/li&gt;&lt;/ol&gt;"</span>)
        } <span class="hljs-keyword">else</span> {
            toc += <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(level + <span class="hljs-number">1</span>).join(<span class="hljs-string">"&lt;/li&gt;"</span>)
        }

        level = <span class="hljs-built_in">parseInt</span>(openLevel)

        <span class="hljs-keyword">if</span> (!id) {
            id = titleText.replace(<span class="hljs-regexp">/ /g</span>, <span class="hljs-string">"_"</span>)
        }
        toc += <span class="hljs-string">'&lt;li&gt;&lt;a href="#'</span> + id + <span class="hljs-string">'"&gt;'</span> + titleText + <span class="hljs-string">"&lt;/a&gt;&lt;/li&gt;"</span>
    })

    <span class="hljs-keyword">if</span> (level) {
        toc += <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(level + <span class="hljs-number">1</span>).join(<span class="hljs-string">"&lt;/ol&gt;"</span>)
    }

    <span class="hljs-keyword">return</span> toc
}
</code></pre>
<p>We are using a regex to match h2, h3, h4, and h5 tags and creating an ordered list to display to ToC contents and returning the HTML of the whole ToC.</p>
<p>Now we will add this HTML to a div that will contain the ToC. That div will be wrapped inside a dialog component so that we can use a modal to easily show/hide the ToC. We will also add a button that will be used as the toggle.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">show_toc</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">document</span>.documentElement.style.overflow = <span class="hljs-string">"hidden"</span>
    <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"toc-dialog"</span>).showModal()
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add_toc_toggle</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> dialog = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"dialog"</span>)
    dialog.setAttribute(<span class="hljs-string">"id"</span>, <span class="hljs-string">"toc-dialog"</span>)
    <span class="hljs-keyword">const</span> toc_div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>)
    toc_div.classList.add(<span class="hljs-string">"toc-content"</span>)
    toc_div.innerHTML = <span class="hljs-string">"&lt;h2&gt;Table of Contents&lt;/h2&gt;"</span>
    dialog.appendChild(toc_div)

    <span class="hljs-keyword">const</span> show_toc_btn = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"button"</span>)
    show_toc_btn.setAttribute(<span class="hljs-string">"id"</span>, <span class="hljs-string">"toc-toggle"</span>)
    show_toc_btn.innerHTML =
        <span class="hljs-string">'&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"&gt;&lt;path d="M.361 256C.361 397 114 511 255 511C397 511 511 397 511 256C511 116 397 2.05 255 2.05C114 2.05 .361 116 .361 256zM192 150V363H149V150H192zM234 150H362V193H234V150zM362 235V278H234V235H362zM234 320H362V363H234V320z"&gt;&lt;/path&gt;&lt;/svg&gt;'</span>

    show_toc_btn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> {
        show_toc()
    })

    <span class="hljs-comment">// Click anywhere to close</span>
    dialog.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
        <span class="hljs-built_in">document</span>.documentElement.style.overflow = <span class="hljs-string">"auto"</span>
        event.currentTarget.close()
    })

    dialog.addEventListener(<span class="hljs-string">"close"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
        <span class="hljs-built_in">document</span>.documentElement.style.overflow = <span class="hljs-string">"auto"</span>
        event.currentTarget.close()
    })

    <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".post-content"</span>).appendChild(dialog)
    <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".post-content"</span>).appendChild(show_toc_btn)
}
</code></pre>
<p>Finally we will provide a driver function that will perform all the necessary tasks related to ToC generation. We will also add a keyboard shortcut to toggle the ToC.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add_toc</span>(<span class="hljs-params">toc_toggle_key</span>) </span>{
    <span class="hljs-keyword">const</span> css = <span class="hljs-string">`
        .toc-content {
            margin: 0;
            padding: 0;
            max-width: 65vw;
        }

        @media (width &lt;= 800px) {
            .toc-content {
                max-width: 100vw;
            }
        }

        .toc-content h2 {
            padding: 20px 30px;
        }

        .toc-content ol {
            counter-reset: item;
        }

        .toc-content li {
            display: flex;
            white-space: nowrap;
            gap: 5px;
        }

        .toc-content li:before {
            content: counters(item, ".") " ";
            counter-increment: item;
        }

        .toc-content a {
            text-decoration: none;
            white-space: normal;
            flex-grow: 1;
        }

        .toc-content li:hover {
            background: rgba(82, 142, 227, 0.3);
            cursor: pointer;
        }

#toc-toggle {
            background: transparent;
            position: fixed;
            bottom: 5%;
            right: 5%;
            width: 5rem;
        }

#toc-toggle svg {
            fill: rgb(82, 142, 227);
            scale: 1;
        }

#toc-toggle svg:hover {
            scale: 1.15;
        }

#toc-dialog {
            padding: 0;
            border: 0;
        }
`</span>
    GM_addStyle(css)

    add_toc_toggle()
    <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".toc-content"</span>).innerHTML += generate_toc()

    <span class="hljs-keyword">if</span> (toc_toggle_key) {
        <span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"keydown"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
            <span class="hljs-keyword">if</span> (event.key === toc_toggle_key) {
                show_toc()
            }
        })
    }
}
</code></pre>
<p>Now, just call the driver function in main.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    change_tweet_text()
    add_declutter_toggle(<span class="hljs-string">"F"</span>)
    attach_code_copy_btn()
    add_toc(<span class="hljs-string">";"</span>)
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/toc_demo-1.gif" alt="toc demo" width="600" height="400" loading="lazy"></p>
<p>Now whenever, you read an article on freeCodeCamp, you can use the toggle button or a shortcut to view the ToC.</p>
<p>For practice, try making the toc sticky in the side (left/right). If you are on a wider screen, having the toc on the side will make the reading experience even better. </p>
<p><a></a></p>
<h2 id="heading-key-takeways">Key Takeways</h2>
<p>You should never run arbitrary JavaScript on any occasion. That goes for Tampermonkey scripts as well. </p>
<p>I recommend going through every line of code before using a userscript. As you can already imagine, a userscript can drastically alter a website's appearance. If you are not careful enough, you might click a button that you expect to do one thing but it does something completely different, so always err on the side of caution.</p>
<p>When writing userscripts, keep these things in mind:</p>
<ol>
<li>Always test your code in the dev console first and take care of all edge cases.</li>
<li>Prefer creating/manipulating DOM elements using built-in functions instead of modifying the innerHTML.</li>
<li>If you find any unexpected behaviour, try throwing a long delay and checking if that resolves the issue.</li>
</ol>
<p>You can read my other articles on my <a href="https://blog.renzhamin.com" target="_blank">blog</a>. Find me on <a href="https://twitter.com/renzhamin" target="_blank">Twitter</a> <a href="https://www.linkedin.com/in/renzhamin/" target="_blank">LinkedIn</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ A Practical Guide to Regular Expressions – Learn RegEx with Real Life Examples ]]>
                </title>
                <description>
                    <![CDATA[ What are Regular Expressions? Regular expressions, also known as regex, work by defining patterns that you can use to search for certain characters or words inside strings. Once you define the pattern you want to use, you can make edits, delete certa... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/practical-regex-guide-with-real-life-examples/</link>
                <guid isPermaLink="false">66ba2e427787ec7052709309</guid>
                
                    <category>
                        <![CDATA[ Bash ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Regex ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Regular Expressions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Scripting ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tasnim Ferdous ]]>
                </dc:creator>
                <pubDate>Tue, 01 Aug 2023 20:42:27 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/08/The-Most-concise-guide-with-real-life-examples.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="heading-what-are-regular-expressions">What are Regular Expressions?</h2>
<p>Regular expressions, also known as regex, work by defining patterns that you can use to search for certain characters or words inside strings.</p>
<p>Once you define the pattern you want to use, you can make edits, delete certain characters or words, substitute one thing for another, extract relevant information from a file or any string that contains that particular pattern, and so on.</p>
<h3 id="heading-why-should-you-learn-regex">Why Should You Learn Regex?</h3>
<p>Regex let you to do text processing in a way that can save you a lot of time. It can also introduce some fun in the process.</p>
<p>Using regex can make locating information much easier. Once you find your target, you can batch edit/replate/delete or whatever processing you need to do.</p>
<p>Some practical examples of using regex are batch file renaming, parsing logs, validating forms, making mass edits in a codebase, and recursive search.</p>
<p>In this tutorial, we're going to cover regex basics with the help of this <a href="https://regexr.com/" target="_blank">site</a>. Later on, I will introduce some regex challenges that you'll solve using Python. I'll also show you how to use tools like <code>sed</code> and <code>grep</code> with regex.</p>
<p>Like many things in life, regular expressions are one of those things that you can only truly understand by doing. I encourage you to play around with regex as you are going through this article.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>

<ul>
<li><a class="post-section-overview" href="#regex-basics">Regex Basics</a><ul>
<li><a class="post-section-overview" href="#exact-match">Exact match</a></li>
<li><a class="post-section-overview" href="#character-set">Character set</a><ul>
<li><a class="post-section-overview" href="#match-ranges-in-regex">Match ranges in regex</a></li>
<li><a class="post-section-overview" href="#match-any-character-not-in-the-set">Match any character not in the set</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#character-classes">Character classes</a></li>
<li><a class="post-section-overview" href="#heading-quantifiers">Quantifiers</a></li>
<li><a class="post-section-overview" href="#capture-groups">Capture groups</a><ul>
<li><a class="post-section-overview" href="#how-to-use-logical-or-in-regex">How to use logical OR in regex</a></li>
<li><a class="post-section-overview" href="#how-to-reference-capture-groups">How to reference capture groups</a></li>
<li><a class="post-section-overview" href="#how-to-name-capture-groups">How to name capture groups</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="post-section-overview" href="#how-to-use-regex-with-command-line-tools">How to use regex with command line tools</a><ul>
<li><a class="post-section-overview" href="#recursive-regex-search-with-grep">Recursive regex search with grep</a></li>
<li><a class="post-section-overview" href="#substitution-with-sed">Substitution with sed</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#lookarounds">Advanced Regex: Lookarounds</a><ul>
<li><a class="post-section-overview" href="#heading-lookbehinds">Lookbehinds</a></li>
<li><a class="post-section-overview" href="#heading-lookaheads">Lookaheads</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#practical-examples-of-regex">Practical Examples of Regex</a><ul>
<li><a class="post-section-overview" href="#logs-parsing">Logs parsing</a></li>
<li><a class="post-section-overview" href="#bulk-file-renaming">Bulk File Renaming</a></li>
<li><a class="post-section-overview" href="#email-validation">Email validation</a></li>
<li><a class="post-section-overview" href="#password-constraints">Password constraints</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#final-words">Final words</a></li>
</ul>

<p><a></a></p>
<h2 id="heading-regex-basics">Regex Basics</h2>
<p>A regular expression is nothing but a sequence of characters that match a pattern. Besides using literal characters (like 'abc'), there are some meta characters (*,+,? and so on) which have special purposes. There are also features like character classes which can help you simplify your regular expressions. </p>
<p>Before writing any regex, you'll need to learn about all the basic cases and edge cases for the pattern you are looking for. </p>
<p>For instance, if you want to match 'Hello World', do you want the line to start with 'Hello' or can it start with anything? Do you want exactly one space between 'Hello' and 'World' or there can be more? Can other characters come after 'World' or should the line end there? Do you care about case sensitivity? And so on.</p>
<p>These are the kind of questions you must have the answer to before you sit down to write your regex.</p>
<p><a></a></p>
<h3 id="heading-exact-match">Exact match</h3>
<p>The most basic form of regex involves matching a sequence of characters in a similar way as you can do with Ctrl-F in a text editor.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/exact_match.png" alt="exact_match" width="600" height="400" loading="lazy"></p>
<p>On the top you can see the number of matches, and on the bottom an explanation is provided for what the regex matches character by character.</p>
<p><a></a></p>
<h3 id="heading-character-set">Character set</h3>
<p>Regex character sets allow you to match any one character from a group of characters. The group is surrounded by square brackets []. </p>
<p>For example, <code>t[ah]i</code> matches "tai" and "thi". Here 't' and 'i' are fixed but between them can occur 'a' or 'h'.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/match_set.png" alt="match_set" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h4 id="heading-match-ranges-in-regex">Match ranges in regex</h4>
<p>Sometimes you may want to match a group of characters which are sequential in nature, such as any uppercase English letter. But writing all 26 letters would be quite tedious. </p>
<p>Regex solves this issue with ranges. The "-" acts as a range operator. Some valid ranges are shown below:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Range</td><td>Matches</td></tr>
</thead>
<tbody>
<tr>
<td>[A-Z]</td><td>uppercase letters</td></tr>
<tr>
<td>[a-z]</td><td>lowercase letters</td></tr>
<tr>
<td>[0-9]</td><td>Any digit</td></tr>
</tbody>
</table>
</div><p>You can also specify partial ranges, such as <code>[b-e]</code> to match any of the letters 'bcde' or <code>[3-6]</code> to match any of the numbers '3456'.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/match_set_ranges-1.png" alt="match_set_ranges" width="600" height="400" loading="lazy"></p>
<p>You are not limited to specifying only one range inside a character set. You can use multiple ranges and also combine them with any other additional character(s). Here, <code>[3-6u-w;]</code> will match any of '3456uvw' or semicolon ';'.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/match_set_ranges_multi-1.png" alt="match_set_ranges_multi" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h4 id="heading-match-any-character-not-in-the-set">Match any character not in the set</h4>
<p>If you prefix the set with a '^', the inverse operation will be performed. For example, <code>[^A-Z0-9]</code> will match anything except uppercase letters and digits.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/match_set_not.png" alt="match_set_not" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h3 id="heading-character-classes">Character classes</h3>
<p>While writing regex, you'll need to match certain groups such as digits quite often and multiple times in the same expression as well. </p>
<p>So for example, how would you match a pattern like 'letter-digit-letter-digit'? </p>
<p>With what you've learned up until now, you can come up with <code>[a-zA-Z]-[0-9]-[a-zA-z]-[0-9]</code>. This works, but you can see how the expression can get quite messy as the pattern length gets bigger.</p>
<p>To make the expression simpler, classes have been assigned to well-defined character groups such as digits. The following table shows these classes and their equivalent expression with character sets:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Class</td><td>Matches</td><td>Equivalent expression</td></tr>
</thead>
<tbody>
<tr>
<td>.</td><td>anything except newline</td><td>[^\n\r]</td></tr>
<tr>
<td>\w</td><td>word character</td><td>[a-zA-Z0-9_]</td></tr>
<tr>
<td>\W</td><td>non-word character</td><td>[^\w]</td></tr>
<tr>
<td>\d</td><td>digits</td><td>[0-9]</td></tr>
<tr>
<td>\D</td><td>non-digits</td><td>[^\d]</td></tr>
<tr>
<td>\s</td><td>space, tab, newlines</td><td>[ \t\r\n\f]</td></tr>
<tr>
<td>\S</td><td>non whitespace characters</td><td>[^\s]</td></tr>
</tbody>
</table>
</div><p>Character classes are quite handy and make your expressions much cleaner. We will use them extensively throughout this tutorial, so you can use this table as a reference point and come back here if you forget any of the classes.</p>
<p>Most of the time, we won't care about all the positions in a pattern. The "." class saves us from writing all possible characters in a set. </p>
<p>For example, <code>t..</code> matches anything that starts with t and any two characters afterwards. This may remind you of the SQL <code>LIKE</code> operator which would use <code>t%%</code> to accomplish the same thing.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/match_any.png" alt="match_any" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h3 id="heading-quantifiers">Quantifiers</h3>
<p>The word "pattern" and "repetition" go hand in hand. If you want to match a 3 digit number you can use <code>\d\d\d</code>. But what if you need to match 11 digits? You could write '\d' 11 times, but a general rule of thumb while writing regex or just doing any kind of programming is that if you find yourself repeating something more than twice, you are probably unaware of some feature. </p>
<p>In regex, you can use quantifiers for this purpose. To match 11 digits, you can simply write the expression <code>\d{11}</code>.</p>
<p>The table below lists the quantifiers you can use in regex:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Quantifier</td><td>Matches</td></tr>
</thead>
<tbody>
<tr>
<td>*</td><td>0 or more</td></tr>
<tr>
<td>?</td><td>0 or 1</td></tr>
<tr>
<td>+</td><td>1 or more</td></tr>
<tr>
<td>{n}</td><td>exactly n times</td></tr>
<tr>
<td>{n, }</td><td>n or more times</td></tr>
<tr>
<td>{n, m}</td><td>n to m times inclusive</td></tr>
</tbody>
</table>
</div><p>In this example, the expression <code>can\s+write</code> matches <code>can</code> followed by 1 or more whitespaces followed by <code>write</code>. But you can see 'canwrite' is not matched as <code>\s+</code> means at least one whitespace needs to be matched. This is useful when you are searching through text which is not trimmed.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/match_multi_whitespaces.png" alt="match_multi_whitespaces" width="600" height="400" loading="lazy"></p>
<p>Can you guess what <code>can\s?write</code> will match?</p>
<p><a></a></p>
<h3 id="heading-capture-groups">Capture groups</h3>
<p>Capture groups are sub-expressions enclosed in parentheses (). You can have any number of capture groups, and even nested capture groups.</p>
<p>The expression <code>(The ){2}</code> matches 'The ' twice. But without a capture group, the expression <code>The {2}</code> would match 'The' followed by 2 spaces, as the quantifier will be applied on the space character and not on 'The ' as a group.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/capture_this.png" alt="capture_this" width="600" height="400" loading="lazy"></p>
<p>You can match any pattern inside capture groups as you would with any valid regex. Here <code>(is\s+){2}</code> matches if it finds 'is' followed by 1 or more spaces twice.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/capture_is.png" alt="capture_is" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h4 id="heading-how-to-use-logical-or-in-regex">How to use logical OR in regex</h4>
<p>You can use "|" to match multiple patterns. <code>This is (good|bad|sweet)</code> matches 'This is ' followed by any of 'good' or 'bad' or 'sweet'.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/or-1.png" alt="or" width="600" height="400" loading="lazy"></p>
<p>Again, you must understand the importance of capture groups here. Think about what the expression <code>This is good|bad|sweet</code> would match?</p>
<p> <img src="https://www.freecodecamp.org/news/content/images/2023/07/or_no_capture.png" alt="or_no_capture" width="600" height="400" loading="lazy"></p>
<p>With a capture group, <code>good|bad|sweet</code> is isolated from <code>This is</code>. But if it's not inside a capture group, the entire regex is only one group. So the expression <code>This is good|bad|sweet</code> will match if the string contains 'This is good' or 'bad' or 'sweet'.</p>
<p><a></a></p>
<h4 id="heading-how-to-reference-capture-groups">How to reference capture groups</h4>
<p>Capture groups can be referenced in the same expression or while performing replacements as you can see on the Replacement tab. </p>
<p>Most tools and languages allow you to reference the nth captured group with '\n'. In this site '$n' is used while referencing on replacement. The syntax for replacement will vary depending on the tools or language you're using. For JavaScript, for example, its '$n', while for Python its '\n'.</p>
<p>In the expression <code>(This) is \1 power</code>, 'This' is captured and then referenced with '\1', effectively matching <code>This is This power</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/refer_capture.png" alt="refer_capture" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h4 id="heading-how-to-name-capture-groups">How to name capture groups</h4>
<p>You can name your capture groups with the syntax <code>(?&lt;name&gt;pattern)</code> and backreference them in the same expression with <code>\k&lt;name&gt;</code>. </p>
<p>On replacement, referencing is done by <code>$&lt;name&gt;</code>. This is the syntax for JavaScript and can vary among languages. You can learn about the differences <a href="https://www.regular-expressions.info/named.html" target="_blank">here</a>. Also note that this feature might not be available in some languages.</p>
<p>In the expression <code>(?&lt;lang&gt;[\w+]+) is the best but \k&lt;lang&gt; .*</code>, the pattern <code>[\w+]+</code> is captured with the name 'lang' and backreferenced with <code>\k&lt;lang&gt;</code>. This pattern will match any word character or '+' character 1 or more times. The <code>.*</code> at the end of the regex matches any character 0 or more times. And finally on replacement, the referencing is done by <code>$&lt;lang&gt;</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/named_capture-1.png" alt="named_capture" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h2 id="heading-how-to-use-regex-with-command-line-tools">How to Use Regex with Command Line Tools</h2>
<p>There are good CLI tools available that let you perform regex from your terminal. These tools save you even more time as you can easily test different regex without writing code in some langauge and then compiling or interpreting it. </p>
<p>Some of the well-known tools are grep, sed, and awk. Let's look at a few examples to give you some ideas on how you can leverage these tools.</p>
<p><a></a></p>
<h3 id="heading-recursive-regex-search-with-grep">Recursive regex search with grep</h3>
<p>You can execute the power of regex through grep. Grep can search patterns in a file or perform recursive search.</p>
<p>If you are on Windows, you can install grep using winget. Run this command in powershell:</p>
<pre><code class="lang-powershell">winget install <span class="hljs-literal">-e</span> -<span class="hljs-literal">-id</span> GnuWin32.Grep
</code></pre>
<p>I will show you the solution to a challenge I created for a CTF competition at my university. </p>
<p>The file attached to the challenge is a <a href="https://github.com/renzhamin/regex-guide/blob/main/ripG.zip" target="_blank">zip file</a> that contains multiple levels of directories and a lot of files in it. The name of the competition was Coderush with flag format <code>coderush{flag is here}</code>. So you have to search for the pattern <code>coderush{.*}</code> which will match the flag format <code>coderush{any character here}</code>.</p>
<p>Unzip the file with <code>unzip ripG.zip</code> and cd into it with <code>cd ripG</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/huge_files.png" alt="huge_files" width="600" height="400" loading="lazy"></p>
<p>There are 358 directories and 8731 files. Instead of searching the pattern in the files one by one, you can employ grep like this:</p>
<pre><code class="lang-sh">grep --color -R <span class="hljs-string">"coderush{.*}"</span>
</code></pre>
<p>The "-R" flag enables recursive search.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/grep.png" alt="recursive search with grep" width="600" height="400" loading="lazy"></p>
<p>You can learn more about grep and its command line options <a href="https://www.freecodecamp.org/news/grep-command-in-linux-usage-options-and-syntax-examples/" target="_blank">here</a></p>
<p><a></a></p>
<h3 id="heading-substitution-with-sed">Substitution with sed</h3>
<p>You can use sed to perform insertion, deletion, substitution on text files by specifying a regex. If you are on windows, you can get sed from <a href="https://github.com/mbuilov/sed-windows" target="_blank">here</a>. Or if you use WSL, tools like grep and sed will already be available.</p>
<p>This is the most common usage of sed:</p>
<pre><code class="lang-sh">sed <span class="hljs-string">'s/pattern/replacement/g'</span> filename
<span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">${text}</span>"</span> | sed <span class="hljs-string">'s/pattern/replacement/g'</span>
</code></pre>
<p>Here, the option "g" is specified to replace all occurrences.</p>
<p>Some other useful options are <code>-n</code> to suppress the default behaviour of printing all lines and using p instead of g to print only the lines which are affected by the regex.</p>
<p>Let's take a look at the content of <a href="https://github.com/renzhamin/regex-guide/blob/main/texts.txt" target="_blank">texts.txt</a>.</p>
<pre><code>Hello rand chars World <span class="hljs-number">56</span> rand chars
Henlo <span class="hljs-number">52</span> rand chars W0rld rand chars
GREP rand chars Henlo <span class="hljs-number">62</span> rand chars
Henlo <span class="hljs-number">10</span> rand chars Henlo rand chars
GREP rand chars Henlo <span class="hljs-number">45</span> rand chars
</code></pre><p>Our task is replacing <code>Henlo number</code> with <code>Hello number</code> only in the lines where "GREP" is present. So, we are searching for the pattern <code>Henlo ([0-9]+)</code> which will match 'Henlo ' followed by 1 or more digits and all the digits are captured. Then our replacement string will be <code>Hello \1</code> – the '\1' is referencing the capture group containing the digits.</p>
<p>One way to accomplish that would be using grep to grep the lines which have "GREP" present then perform the replacement with sed.</p>
<pre><code class="lang-sh">grep <span class="hljs-string">"GREP"</span> texts.txt | sed -En <span class="hljs-string">'s/Henlo ([0-9]+)/Hello \1/p'</span>
</code></pre>
<p>The "-E" option enables extended regex without which you would need to escape the parentheses.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/grep_sed.png" alt="grep_sed" width="600" height="400" loading="lazy"></p>
<p>Or you could just use sed. Use <code>/pattern/</code> to restrict substitution on only the lines where pattern is present.</p>
<pre><code class="lang-sh">sed -En <span class="hljs-string">'/GREP/ s/Henlo ([0-9]+)/Hello \1/p'</span> texts.txt
</code></pre>
<p><a></a></p>
<h2 id="heading-advanced-regex-lookarounds">Advanced Regex: Lookarounds</h2>
<p>Lookaheads and Lookbehinds (together known as lookarounds) are features of regex that allow you to check the existence of a pattern without including it in the match.</p>
<p>You can think of them as zero width assertions – they assert the existence of a pattern but do not consume any characters in the match. These are very powerful features, but they're also computationally expensive. So make sure you keep an eye on performance if you are using them often.</p>
<p><a></a></p>
<h3 id="heading-lookbehinds">Lookbehinds</h3>
<p>Let's say you want to match the word 'linux', but you have 2 conditions.</p>
<ol>
<li>The word 'GNU' must occur before 'linux' occurs. If a line contains 'linux' but doesn't have 'GNU' before it, we want to discard that line.</li>
<li>We want to match only <code>linux</code> and nothing else.</li>
</ol>
<p>We already know how to satisfy the 1st condition. <code>GNU.*</code> will match 'GNU' followed by any number of characters. Then finally we match the word <code>linux</code>. This will match all of <code>GNU-any-characters-linux</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/GNU_LINUX.png" alt="GNU_LINUX" width="600" height="400" loading="lazy"></p>
<p>But how do we prevent matching <code>GNU.*</code> while still maintaining the 1st condition?</p>
<p>That's where a positive lookbehind comes in. You can mark a capture group as a positive lookbehind by prefixing it with <code>?&lt;=</code>. In this example, the expression becomes <code>(?&lt;=GNU.*)linux</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/positive_lookbehind.png" alt="positive_lookbehind" width="600" height="400" loading="lazy"></p>
<p>Now only <code>linux</code> is matched and nothing else.</p>
<p>Note that the expressions <code>(?&lt;=GNU.*)linux</code> and <code>linux(?&lt;=GNU.*)</code> will behave exactly the same. In the 2nd expression, although <code>linux</code> is before the lookbehind, there is <code>.*</code> after 'GNU' which matches <code>linux</code>. This means it satisfies the lookbehind. </p>
<p>To make it simpler, think about the pattern without the lookbehind. The pattern <code>GNU.*</code> will match 'GNU' and anything after it, in our case matching <code>linux</code>.</p>
<p>Now we can derive a generalized statement that the expression <code>(?&lt;=C)X</code> will match the pattern X – only if pattern C came before X (and C must not be included in the match).</p>
<p>You can also reverse the 1st condition. Match lines that contains the word <code>linux</code> only if <code>GNU</code> never came before it. This is called a negative lookbehind. The prefix in this case is <code>?&lt;!</code>. The inverse of the previous expression would be <code>(?&lt;!GNU.*)linux</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/negative_lookbehind.png" alt="negative_lookbehind" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h3 id="heading-lookaheads">Lookaheads</h3>
<p>Lookaheads are also assertions like lookbehinds, as you saw in the previous example. The only difference is that lookbehinds make an assertion before and lookaheads makes assertion after.</p>
<p>Let's say you have these two conditions:</p>
<ol>
<li>Match <code>Hello</code> only if <code>World</code> comes somewhere after it.</li>
<li>Match only Hello and nothing else.</li>
</ol>
<p>The prefix for a positive lookahead is <code>?=</code>. The expression <code>Hello(?=.*World)</code> will meet both conditions. This is similar to <code>Hello.*World</code> except that only <code>Hello</code> will be matched whereas <code>Hello.*World</code> will match 'Hello', 'World' and anything in between.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/postive_lookahead-1.png" alt="postive_lookahead" width="600" height="400" loading="lazy"></p>
<p>Similar to the example in a positive lookbehind, the expressions <code>Hello(?=.*World)</code> and <code>(?=.*World)Hello</code> are equivalent. Because the <code>.*</code> before 'World' matches <code>Hello</code>, satisfying the 1st condition.</p>
<p>A negative lookahead is just the complement of a negative lookbehind. You can use it by prefixing it with <code>?!</code>. <code>(?!World)Hello</code> will match <code>Hello</code> only if there is no <code>World</code> anywhere after it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/negative_lookahead.png" alt="negative_lookahead" width="600" height="400" loading="lazy"></p>
<p>Here is a summary of the syntax for lookarounds when you want to match the pattern X with assertion C.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Operation</td><td>RegEx</td></tr>
</thead>
<tbody>
<tr>
<td>positive lookahead</td><td><code>(?=C)X</code></td></tr>
<tr>
<td>negative lookahead</td><td><code>(?!C)X</code></td></tr>
<tr>
<td>positive lookbehind</td><td><code>(?&lt;=C)X</code></td></tr>
<tr>
<td>negative lookbehind</td><td><code>(?&lt;!C)X</code></td></tr>
</tbody>
</table>
</div><p><a></a></p>
<h2 id="heading-practical-examples-of-regex">Practical Examples of Regex</h2>
<p><a></a></p>
<h3 id="heading-logs-parsing">Logs parsing</h3>
<p>In this <a href="https://github.com/renzhamin/regex-guide/blob/main/log_train.txt" target="_blank">log file</a>, these are the lines which we care about:</p>
<pre><code>[<span class="hljs-number">1</span>/<span class="hljs-number">10000</span>] Train loss: <span class="hljs-number">11.30368</span>, Valid loss: <span class="hljs-number">8.95446</span>, <span class="hljs-attr">Elapsed_time</span>: <span class="hljs-number">7.58941</span>
[<span class="hljs-number">500</span>/<span class="hljs-number">10000</span>] Train loss: <span class="hljs-number">0.96180</span>, Valid loss: <span class="hljs-number">0.20098</span>, <span class="hljs-attr">Elapsed_time</span>: <span class="hljs-number">82.48651</span>
[<span class="hljs-number">1000</span>/<span class="hljs-number">10000</span>] Train loss: <span class="hljs-number">0.04051</span>, Valid loss: <span class="hljs-number">0.11927</span>, <span class="hljs-attr">Elapsed_time</span>: <span class="hljs-number">156.86243</span>
</code></pre><p>Our task is to extract the training loss and validation loss for purposes such as plotting loss over the epochs. We need to extract the training loss values like <code>11.30368, 0.96180, 0.04051</code> and put them in an array.</p>
<p>All the relevant values are prefixed with '<code>Train loss:</code>', so we can use this in our regex as it is. To match the float numbers we have to match some digits followed by a "<code>.</code>" and then followed by more digits. You can do this with <code>\d+\.\d+</code>. Because we want to keep track of these numbers, they should be inside a capture group.</p>
<p>As "." has special purpose in regex, when you want to match a "." character you have to escape it with a backslash. This is applicable for all characters with a special purpose. But you dont have to escape it inside a character set.</p>
<p>Putting it altogether, the expression for extracting training loss is <code>Train loss: (\d+\.\d+)</code>. We can use the same logic to extract validation loss with <code>Valid loss: (\d+\.\d+)</code>.</p>
<p>Here is one way to extract this information using Python:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> re

f = open(<span class="hljs-string">"log_train.txt"</span>, <span class="hljs-string">"r"</span>).read()

train_loss = re.findall(<span class="hljs-string">r'Train loss: (\d+\.\d+)'</span>, f)
valid_loss = re.findall(<span class="hljs-string">r'Valid loss: (\d+\.\d+)'</span>, f)

train_loss = [float(i) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> train_loss]
valid_loss = [float(i) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> valid_loss]

print(<span class="hljs-string">"train_loss ="</span>, train_loss)
print(<span class="hljs-string">""</span>)
print(<span class="hljs-string">"valid_loss ="</span>, valid_loss)
</code></pre>
<p>When there is one capture group, <code>re.findall</code> searches all the lines and returns the values inside the capture group in a list. </p>
<p>Any regex function only return strings, so the values are converted to floats and printed out. Then you can directly use them in another Python script as a list of floats.</p>
<p>This is the result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/extract_loss.png" alt="extract_loss" width="600" height="400" loading="lazy"></p>
<p>You could also use sed, save the output in train_losses.txt, and read from the file. First we use '/Train/' to target only the lines with 'Train' present then we are applying the same regex as before.</p>
<pre><code class="lang-sh">sed -En <span class="hljs-string">'/Train/ s/.*Train loss: ([0-9]+\.[0-9]+).*/\1/p'</span> log_train.txt | tee train_losses.txt
</code></pre>
<p>".*" is added at the start and end so that sed matches the contents of all the relevant lines. Then the entire line is replaced by the value of the capture group. The <code>tee</code> command is used to redirect the output of sed into train_losses.txt while also printing the contents in the terminal.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/extract_loss_sed.png" alt="extract_loss_sed" width="600" height="400" loading="lazy"></p>
<p>Take a moment to think about what would you need to extract the epochs. You have to extract 500 from [500/10000] for all such lines. The array should look like [1, 500, 1000, 1500, ...]. You can follow the same approach as we used for the previous example. </p>
<p>Note that if you want to match "<code>[</code>" or "<code>]</code>", you have to escape it. The answer is given <a target="_blank" href="https://github.com/renzhamin/regex-guide/blob/main/extract_epochs_array.py">here</a>.</p>
<p><a></a></p>
<h3 id="heading-bulk-file-renaming">Bulk File Renaming</h3>
<p>You have these <a href="https://github.com/renzhamin/regex-guide/tree/main/bulk-rename" target="_blank">files</a> with some random values as prefixes. You have to rename all files as 1.mp4, 2.mp4 and so on.
This is how the files were generated.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/create_files.png" alt="create_files" width="600" height="400" loading="lazy"></p>
<p>This is a common scenario where you have a list of files which have their sequence number in the name but there are also some other characters that you don't want.</p>
<p>The pattern has to match anything up to Episode then an underscore and then the number and .mp4 at the end. </p>
<p>The relevant value is the number before '.mp4' which we will put inside a capture group. <code>.*Episode_</code> will match everything up to the number. Then we can capture the number with <code>([0-9]+)</code> and also match .mp4 with <code>\.mp4</code>. </p>
<p>So the final regex is <code>.*Episode_([0-9]+)\.mp4</code>. As we want to keep the <code>.mp4</code> the replacement string will be <code>\1.mp4</code>.</p>
<p>This is one way to solve it using sed.</p>
<pre><code class="lang-sh"><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> *.mp4; <span class="hljs-keyword">do</span>
    newname=$(<span class="hljs-built_in">echo</span> <span class="hljs-variable">$i</span> | sed -En <span class="hljs-string">'s/.*Episode_([0-9]+)\.mp4/\1.mp4/p'</span>)
    mv <span class="hljs-variable">$i</span> <span class="hljs-variable">$newname</span>
<span class="hljs-keyword">done</span>;ls
</code></pre>
<p>First the new name is saved in a variable and then the mv command is used to rename the file.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/bulk_rename.png" alt="bulk_rename" width="600" height="400" loading="lazy"></p>
<p>Could we have just used <code>.*</code> in place of <code>.*Episode_</code> ? In this example, yes. But there might be filenames such as <code>Steins_Gate0.mp4</code> where the <code>0</code> is part of the movie name and you didn't really want to rename this file so its always better to be as specific as possible.</p>
<p>What if some files were named as "Random_Episode6.mp4"? The difference being, there is no underscore after Episode. What change will you need to make?</p>
<p>The answer is that you'll need to add a "?" after the "_" to make it optional. The regex will be <code>.*Episode_?([0-9]+)\.mp4</code>.</p>
<p><a></a></p>
<h3 id="heading-email-validation">Email validation</h3>
<p>There are all sorts of complicated regex for validating email.</p>
<p>Here is a simple one: <code>^[^@ ]+@[^@.]+\.\w+$</code>. It matches the format <code>A@B.C</code></p>
<p>The table below breaks down this pattern into smaller pieces:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Pattern</td><td>Matches</td></tr>
</thead>
<tbody>
<tr>
<td><code>^</code></td><td>start of line</td></tr>
<tr>
<td><code>[^@ ]+</code></td><td>anything except "@" and space character</td></tr>
<tr>
<td><code>@[^@.]+</code></td><td>@ followed by anything except "@" and "." characters</td></tr>
<tr>
<td><code>\.\w+</code></td><td>"." followed by word characters</td></tr>
<tr>
<td><code>$</code></td><td>end of line</td></tr>
</tbody>
</table>
</div><p><img src="https://www.freecodecamp.org/news/content/images/2023/07/email_validation.png" alt="email_validation" width="600" height="400" loading="lazy"></p>
<p>In the regexr site, you can enable the multline flag from the Flags tab in the upper right corner. The 'gm' at the end indicates that the multiline flag is enabled.</p>
<p>We can see that line 2,3,5,6 didn't match. Can you find out the reason and which part of the regex is responsible for disqualifying it?</p>
<p>The answer is given <a href="https://github.com/renzhamin/regex-guide/blob/main/email_validation.md" target="_blank">here</a></p>
<p><a></a></p>
<h3 id="heading-password-constraints">Password constraints</h3>
<p>You can also use regex to impose constraints. Here we will uncover the power of positive lookaheads. </p>
<p>Lets say we want to accept a string only if there is a digit in it. You already know how to find a digit with the '\d' class. To accomplish that, we can use <code>[^\d]*\d</code>. This will match any non-digit character 0 or more times and then match a digit. </p>
<p>We can also use the expression <code>.*\d</code> to match one digit. So if there is no digit in the string then the lookahead will fail and the none of the characters of that string will be matched, returning an empty string "". </p>
<p>When we are using a programming language, we can check if the regex returned an empty string and determine that the constraints are not satisfied.</p>
<p>We will create a regex which imposes the following criteria:</p>
<ol>
<li>Minimum 8 characters and maximum 16 characters.</li>
<li>At least one lower case letter.</li>
<li>At least one upper case letter.</li>
<li>At least one number.</li>
</ol>
<p>To achieve this, you can use positive lookaheads. This is the regex:</p>
<p><code>^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,16}$</code></p>
<p>The table below explains which part of the regex imposes which constraint:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Pattern</td><td>Imposed Constraint</td></tr>
</thead>
<tbody>
<tr>
<td><code>.{8,16}</code></td><td>min 8 and max 16 characters</td></tr>
<tr>
<td><code>(?=.*[a-z])</code></td><td>minimum one lower case letter</td></tr>
<tr>
<td><code>(?=.*[A-Z])</code></td><td>minimum one upper case letter</td></tr>
<tr>
<td><code>(?=.*\d)</code></td><td>minimum one digit</td></tr>
</tbody>
</table>
</div><p><img src="https://www.freecodecamp.org/news/content/images/2023/07/pass_constraints.png" alt="pass_constraints" width="600" height="400" loading="lazy"></p>
<p>What modification you would need for imposing at least 5 upper case letters?</p>
<p>You may think <code>(?=.*[A-Z]{5,})</code> will do the job. But this expression requires all the 5 letters to be together. A string like <code>rand-ABCDE-rand</code> will be matched but <code>0AxBCDxE0</code> will not be matched even though it has 5 upper case letters (as they are not adjacent).</p>
<p>Yet again, we have capture groups coming to the rescue. We want to match 5 uppercase letters anywhere in the string. We already know that we can match 1 uppercase letter with <code>.*[A-Z]</code>. Now we will put them inside a capture group and attach a quantifier of minimum 5. The expression will be <code>(.*[A-Z]){5,}</code>.</p>
<p>Here is the final answer:</p>
<p>In place of <code>(?=.*[A-Z])</code> you will need <code>(?=(.*[A-Z]){5,})</code>. The expression becomes <code>^(?=.*[a-z])(?=(.*[A-Z]){5,})(?=.*\d).{8,16}$</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/pass_5_upper.png" alt="pass_5_upper" width="600" height="400" loading="lazy"></p>
<p>You could also require that the password not contain certain words to enforce stronger passwords. </p>
<p>For example, we want to reject the password if contains <code>pass</code> or <code>1234</code>. Negative lookaheads is the tool for this job. The regex would be <code>^(?!.*(pass|1234)).*$</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/restrict_words-1.png" alt="restrict_words-1" width="600" height="400" loading="lazy"></p>
<p>In this regex, we put <code>pass</code> and <code>1234</code> inside a capture group and used the logical OR operator. This capture group is nested inside another capture group which is prefixed with <code>?!.*</code>. This makes it a negative lookahead that matches if there are at least 8 characters by <code>.{8,}</code> with the condition that, <code>pass</code> or <code>1234</code> can't be present anywhere in the string.</p>
<p><a></a></p>
<h2 id="heading-final-words">Final Words</h2>
<p>I hope you got a good amount of practice while going through this article. It's ok if you forget some syntax. What's important is understanding the core concepts and having a good idea of what's possible with regex. Then, if you forget a pattern, you can just google it or reference a cheatsheet. </p>
<p>The more you practice, the more you will get by without outside help. Eventually you will be able write super complex and effective regex completely offline. </p>
<p>There are already some good regex cheatsheets out there, so I wanted to create something more in-depth here that you can reference for the core concepts and common use cases. </p>
<p>If you're looking for a cheatsheet, the one from <a href="https://quickref.me/regex.html" target="_blank">QuickRef</a> is helpful. It's a good place to recall the syntax and they also provide some basic overview of regex related functions in various programming languages.</p>
<p>Most regex techniques are the same in all programming languages and tools – but certain tools might offer additional features. So do some research on the tool you are using to pick the best one for you.</p>
<p>My final suggestion would be not to force using regex just because you can. A lot of the times a regular <code>string.find()</code> is enough to get the job done. But if you live in the terminal, you really can do a lot just with regex for sure.</p>
<p>If you like this type of article, you may keep an eye on my <a href="https://blog.renzhamin.com" target="_blank">blog</a> or twitter.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Become an Unreal Automation Expert ]]>
                </title>
                <description>
                    <![CDATA[ By Tim Grossmann Table of contents Automating Workflows with Blueprints, C++, and Python Why the Unreal Engine is interesting Why use Blueprints for workflow and even game programming Why use C++ for workflow and game programming Why use Python for ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/becoming-an-unreal-automation-expert/</link>
                <guid isPermaLink="false">66d4614937bd2215d1e245eb</guid>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ C++ ]]>
                    </category>
                
                    <category>
                        <![CDATA[ epic ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Scripting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ unreal-engine ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 06 May 2020 20:20:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/04/udemy_cover_img.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Tim Grossmann</p>
<h2 id="heading-table-of-contents">Table of contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-automating-workflows-with-blueprints-c-and-python">Automating Workflows with Blueprints, C++, and Python</a></li>
<li><a class="post-section-overview" href="#heading-why-the-unreal-engine-is-interesting">Why the Unreal Engine is interesting</a><ul>
<li><a class="post-section-overview" href="#heading-why-use-blueprints-for-workflow-and-even-game-programming">Why use Blueprints for workflow and even game programming</a></li>
<li><a class="post-section-overview" href="#heading-why-use-c-for-workflow-and-game-programming">Why use C++ for workflow and game programming</a></li>
<li><a class="post-section-overview" href="#heading-why-use-python-for-workflow-optimisation">Why use Python for workflow optimisation</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-writing-our-own-automated-project-clean-up-script-using-python">Writing our own automated project clean-up script using Python</a><ul>
<li><a class="post-section-overview" href="#heading-but-does-it-pay-off">But does it pay off?</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-learn-how-to-automate-and-optimise-workflows-with-scripting-on-udemy">Learn how to automate and optimise workflows with scripting on Udemy</a></li>
</ol>
<h2 id="heading-automating-workflows-with-blueprints-c-and-python">Automating Workflows with Blueprints, C++, and Python</h2>
<p><strong>Every job has repetitive tasks and processes that can be automated.</strong> Those tasks can take up a significant amount of your time. </p>
<p>Someone with only basic knowledge about scripting can build a script that might cut down the time to execute those tasks to a bare minimum. In the long run, this time saving accumulates into additional hours that can be used for more productive work.</p>
<p>Learning how to automate things, therefore, is an <strong>invaluable skill</strong>. A skill that can be acquired in different, more specific sectors and then applied to other, more general ones.</p>
<p>Especially considering the current difficult times, learning a new skill that will have a significant impact on your employability is vital. </p>
<p>In this article, we want to describe the importance of automation when working with real-time software like the Unreal Engine.</p>
<h2 id="heading-why-the-unreal-engine-is-interesting">Why the Unreal Engine is interesting</h2>
<p>Epic’s Unreal Engine is one of the most popular game and real-time 3D applications out there. It's used for the creation of entertainment content like games and interactive setups.</p>
<p>Disciplines like virtual and augmented reality require the usage of sophisticated processes that, just like any other pipeline, can be optimised through automation.</p>
<p>In addition to that, the gaming and 3D real-time market is continually growing, which increases the demand for talent. </p>
<p>According to a study conducted by <a target="_blank" href="https://www.burning-glass.com/">Burning Glass Technologies</a>, a labor market analytics firm, the average salaries for Unreal developers <strong>increased by 22% last year</strong>, the <strong>wages for Artists, by 51%</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/Unreal-Engine-blog-demand-for-unreal-engine-skills-at-all-time-high-blog_body_image_salary-1600x759-0a004f058af1d5692e834705555726a8d70e7a7f.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Source: [https://www.unrealengine.com/en-US/blog/demand-for-unreal-engine-and-real-time-3d-skills-at-all-time-high](https://www.unrealengine.com/en-US/blog/demand-for-unreal-engine-and-real-time-3d-skills-at-all-time-high" rel="nofollow noopener)</em></p>
<h3 id="heading-why-use-blueprints-for-workflow-and-even-game-programming">Why use Blueprints for workflow and even game programming</h3>
<p>In addition to the general C++ programming interface, the Unreal Engine provides a <strong>graphical programming system called Blueprints</strong>. They expose the full functionality as C++ code, which means that everything, including in-game features, can be programmed without huge performance drawbacks.</p>
<p>The graphical interface allows us to quickly compose entities into a flow to create functionality. Looking at an example that sets the material for a static mesh actor, we can see that the Blueprint is easily readable.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/utility.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>Blueprints script to set a material for a static mesh actor</em></p>
<h3 id="heading-why-use-c-for-workflow-and-game-programming">Why use C++ for workflow and game programming</h3>
<p>The “native” approach to coding for the unreal engine is C++. It is used to create in-game logic, simplify level creation and workflows, and improve the development pipeline. It’s more complex and difficult to learn then Blueprints but can add an additional boost in run-time and performance.</p>
<p>Its <strong>performance advantage</strong> makes it the language of choice for essential, lower-level operations such as rendering and physics in game development. For developers that are already proficient with C++, it is a convenient addition to be able to automate processes without learning an additional language directly.</p>
<h3 id="heading-why-use-python-for-workflow-optimisation">Why use Python for workflow optimisation</h3>
<p>Compared to Blueprints or C++, Python is one of the <strong>de-facto standard languages used for automating tasks</strong>. It’s simple to learn, understandable, and extremely versatile since it can run on nearly any platform without additional effort.</p>
<p>Epic lists Python as one of the required skills, for example, for Data Pipeliners in their <strong><a target="_blank" href="https://epicgames.ent.box.com/s/n12ixy53l8cknz73npimsr54frkvm72c">Creator’s Field Guide to Emerging Careers in Interactive 3D</a>.</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/Screenshot-2020-04-30-at-16.41.33.png" alt="Image" width="600" height="400" loading="lazy">
<em>https://epicgames.ent.box.com/s/n12ixy53l8cknz73npimsr54frkvm72c</em></p>
<p>The <strong>Unreal Engine has full Python scripting support</strong>. Unfortunately, it is not suitable for real-time and in-game scripting, but can only be used for Unreal Editor scripting. Python’s simplicity, however, makes it an incredible option for fast prototyping of pipeline automation.</p>
<p>Epic themselves try to promote the usage of Python using the <a target="_blank" href="https://docs.unrealengine.com/en-US/Engine/Editor/ScriptingAndAutomation/Python/index.html">documentation</a>, <a target="_blank" href="https://docs.unrealengine.com/en-US/PythonAPI/index.html">API docs</a>, and even a <a target="_blank" href="https://m.youtube.com/watch?v=0guOMTiwmhk">recorded webinar</a> about Python scripting for the Unreal Engine.</p>
<h2 id="heading-writing-our-own-automated-project-clean-up-script-using-python">Writing our own automated project clean-up script using Python</h2>
<p>Larger projects can get messy fast. Having a script at hand that can do a clean-up by looking at all of our selected assets and automatically moving them into appropriate folders helps us drastically improve our workflow.  </p>
<p>The below schematic drawing explains the idea behind the script.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/WhatsApp-Image-2020-05-01-at-14.11.07.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<blockquote>
<p>As of now, the by default version of Python set in Unreal is Python 2.7. If you want to use Python 3, you can follow the process <a target="_blank" href="https://docs.unrealengine.com/en-US/Engine/Editor/ScriptingAndAutomation/Python/index.html">described here</a> to switch to Python 3.</p>
</blockquote>
<p>To begin with, we first need to import the unreal library into our scope. Once we have done this, we can create class instances of the elementary classes. For now, we only need the <code>[EditorUtilityLibrary](https://docs.unrealengine.com/en-US/PythonAPI/class/EditorUtilityLibrary.html?highlight=editorutilitylibrary)</code> which enables us to get a list of all the selected assets. </p>
<p>We can get the number of selected assets by using Python's <code>len()</code> method and use Unreal's logging method to get output on the Debug Log in the Unreal Engine.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> unreal

<span class="hljs-comment"># create unreal class instances</span>
editor_util = unreal.EditorUtilityLibrary()

<span class="hljs-comment"># get the selected assets</span>
selected_assets = editor_util.get_selected_assets()
num_assets = len(selected_assets)

unreal.log(num_assets)
</code></pre>
<p>The code snippet above will print a single number to the <code>Debug Log</code>. For each of the selected assets, we now want to get some information like the asset name and class.</p>
<p>The Unreal <code>[ObjectBase](https://docs.unrealengine.com/en-US/PythonAPI/class/_ObjectBase.html?highlight=objectbase#unreal._ObjectBase)</code> object has several helper methods to get the name, class, and other attributes. We will make use of the <code>get_fname()</code> and <code>get_class()</code> methods.</p>
<pre><code class="lang-python"><span class="hljs-keyword">for</span> assets <span class="hljs-keyword">in</span> selected_assets:
    <span class="hljs-comment"># get the class instance and the clear text name</span>
    asset_name = asset.get_fname()
    asset_class = asset.get_class()

    unreal.log(<span class="hljs-string">"{} - {}"</span>.format(asset_name, asset_class))
</code></pre>
<p>However, this will only give us the class definition and not the clear text name of the class itself, which we want to use for folder creation.</p>
<p>To get the display name instead of the class definition, we need to create an instance of the <code>[SystemLibrary](https://docs.unrealengine.com/en-US/PythonAPI/class/SystemLibrary.html?highlight=systemlibrary)</code>. It's <code>get_class_display_name()</code> method takes a class definition and returns the class name as <code>String</code>.</p>
<pre><code class="lang-python"><span class="hljs-comment"># create unreal class instances</span>
editor_util = unreal.EditorUtilityLibrary()
system_lib = unreal.SystemLibrary()

...

<span class="hljs-keyword">for</span> assets <span class="hljs-keyword">in</span> selected_assets:
    <span class="hljs-comment"># get the class instance and the clear text name</span>
    asset_name = asset.get_fname()
    asset_class = asset.get_class()
    class_name = system_lib.get_class_display_name(asset_class)

    unreal.log(<span class="hljs-string">"Name: {} - Class: {}"</span>.format(asset_name, class_name))
</code></pre>
<p>Now we can see something like this "Name: NewMaterial - Class: Material" printed to our log. This is precisely the kind of information we needed.</p>
<p>The last step is to "rename" our assets to a given location. For example, every Material will be renamed to <code>"/Material/&lt;Name of Material Asset&gt;"</code> which will move it into the according to folders.</p>
<p>To "rename" assets, we need an additional class. The <code>rename_loaded_asset()</code> method is part of the <a target="_blank" href="https://docs.unrealengine.com/en-US/PythonAPI/class/EditorAssetLibrary.html?highlight=editorassetlibrary"><code>EditorAssetLibrary</code></a>, so we need to create an instance of this class first. In addition to that, we have to create a new location to which the asset will be relocated.</p>
<p>To keep this more platform-independent, we will use the <code>os</code> module and its <code>path.join()</code> method.</p>
<p>Once we have created the <code>new_path</code> variable, we can use it in the method call to <code>rename_loaded_asset()</code> to relocate our current asset.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> unreal

<span class="hljs-comment"># create unreal class instances</span>
editor_util = unreal.EditorUtilityLibrary()
system_lib = unreal.SystemLibrary()
editor_asset_lib = unreal.EditorAssetLibrary()

...

<span class="hljs-keyword">for</span> assets <span class="hljs-keyword">in</span> selected_assets:
    <span class="hljs-comment"># get the class instance and the clear text name</span>
    asset_name = asset.get_fname()
    asset_class = asset.get_class()
    class_name = system_lib.get_class_display_name(asset_class)

    <span class="hljs-comment"># assemble new path and relocate asset</span>
    new_path = os.path.join(<span class="hljs-string">"/Game"</span>, class_name, asset_name)
    editor_asset_lib.rename_loaded_asset(asset_name, new_path)

    unreal.log(<span class="hljs-string">"Moved {} to {}"</span>.format(asset_name, new_path))
</code></pre>
<p>Executing this script in the Unreal Engine, the log will provide you with such a message: <code>"Moved NewMaterial to /Game/Material/NewMaterial"</code>.<br>Observing our project, we can now see that all the selected assets have been cleaned into folders named according to their classes.</p>
<p>As you can see, creating a basic script is quite simple. Of course, we need to take care of error handling, suitable logging, and a friendly user interface for more sophisticated tools, but even simple scripts can save a lot of time.  </p>
<h2 id="heading-but-does-it-pay-off">But does it pay off?</h2>
<p>To show you how big the demand for automation in this sector is, here is a screenshot of the <strong>monthly sales of a tool</strong> with functionality containing the script we created in this article. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/income_tool.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Of course, it is essential to understand the needs of users and artists working in the Unreal Engine to know which tasks are suitable for automation.</p>
<h2 id="heading-learn-how-to-automate-and-optimise-workflows-with-scripting-on-udemy">Learn how to automate and optimise workflows with scripting on Udemy</h2>
<p>To help close the gap of Unreal scripting resources, we created an <strong><a target="_blank" href="https://www.udemy.com/course/becoming-an-unreal-automation-expert/?referralCode=F42ED1F45E3543848AEE">extensive Udemy on-demand course</a></strong> to learn Unreal Engine Editor scripting from scratch. </p>
<p>You can use the promo code, <a target="_blank" href="https://www.udemy.com/course/draft/2969558/?referralCode=F42ED1F45E3543848AEE"><strong>AUTOMATEUNREAL20</strong></a><strong>.</strong> You can get the course for as little as $10 now. That’s less than three Frappuccinos from Starbucks!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/article_udemy_promo.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you have any questions or feedback, feel free to reach out to us on Twitter or directly in the discussion section of the course :)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ A quick guide to Redis Lua scripting ]]>
                </title>
                <description>
                    <![CDATA[ By Andrei Chernikov Redis is a popular in-memory grid used for interprocess communication and data storage. You might’ve heard that it lets you run Lua scripts, but you are still not sure why. If this sounds like you, read on. Prerequisites You shou... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/a-quick-guide-to-redis-lua-scripting/</link>
                <guid isPermaLink="false">66d45d974a7504b7409c332e</guid>
                
                    <category>
                        <![CDATA[ Lua ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Redis ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Scripting ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 25 Sep 2019 18:34:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/09/lua_script-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Andrei Chernikov</p>
<p>Redis is a popular in-memory grid used for interprocess communication and data storage. You might’ve heard that it lets you run Lua scripts, but you are still not sure why. If this sounds like you, read on.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/lua_script.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>You should have <a target="_blank" href="https://redis.io/topics/quickstart">Redis installed on your system</a> to follow this guide. It might be helpful to check <a target="_blank" href="https://redis.io/commands">Redis commands reference</a> while reading.</p>
<h2 id="heading-why-do-i-need-lua-scripts">Why do I need Lua Scripts?</h2>
<p>In short: performance gain. Most tasks you do in Redis involve many steps. Instead of doing these steps in the language of your application, you can do it inside Redis with Lua.</p>
<ul>
<li>This may result in better performance.</li>
<li>Also, all steps within a script are executed in an atomic way. No other Redis command can run while a script is executing.</li>
</ul>
<p>For example, I use Lua scripts to change JSON strings stored in Redis. I describe this in detail closer to the end of this article.</p>
<h2 id="heading-but-i-dont-know-any-lua">But I don’t know any Lua</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/dont-know-s.png" alt="Image" width="600" height="400" loading="lazy">
<em>A person who doesn't know any lua</em></p>
<p>Don’t worry, Lua is not very hard to understand. If you know <a target="_blank" href="https://en.wikipedia.org/wiki/List_of_C-family_programming_languages">any language of the C family</a>, you should be okay with Lua. Also, I am providing working examples in this article.</p>
<h2 id="heading-show-me-an-example">Show me an example</h2>
<p>Let’s start by running scripts via <strong>redis-cli</strong>. Start it with:</p>
<pre><code class="lang-shell">redis-cli
</code></pre>
<p>Now run the following command:</p>
<pre><code class="lang-redis">eval “redis.call(‘set’, KEYS[1], ARGV[1])” 1 key:name value
</code></pre>
<p>The <strong>EVAL</strong> command is what tells Redis to run the script which follows. The <code>”redis.call(‘set’, KEYS[1], ARGV[1])”</code>string is our script which is functionally identical to the Redis’s <code>set</code> command. Three parameters follow the script text:</p>
<ol>
<li>The number of provided keys</li>
<li>Key name</li>
<li>First argument</li>
</ol>
<p>Script arguments fall into two groups: <strong>KEYS</strong> and <strong>ARGV</strong>.</p>
<p>We specify how many keys the script requires with the number immediately following it. In our example, it is <strong>1</strong>. Immediately after this number, we need to provide these keys, one after another. They are accessible as <strong>KEYS</strong> table within the script. In our case, it contains a single value <code>key:name</code> at index <strong>1</strong>.</p>
<p><em>Note, that Lua indexed tables start with index</em> <strong><em>1,</em></strong> <em>not</em> <strong><em>0</em></strong><em>.</em></p>
<p>We can provide any number of arguments after the keys, which will be available in Lua as the <strong>ARGV</strong> table. In this example, we provide a single <strong>ARGV</strong>-argument: string <code>value</code>. As you already guessed, the above command sets the key <code>key:name</code> to value <code>value</code>.</p>
<p>It is considered a good practice to provide keys which the script uses as <strong>KEYS</strong>, and all other arguments as <strong>ARGV</strong>. So you shouldn’t specify <strong>KEYS</strong> as 0 and then provide all keys within the <strong>ARGV</strong> table.</p>
<p>Let’s now check if the script completed successfully. We are going to do this by running another script which gets the key from Redis:</p>
<p>eval “return redis.call(‘get’, KEYS[1])” 1 key:name</p>
<p>The output should be <code>”value”</code>, which means that the previous script successfully set the key <code>“key:name”</code>.</p>
<h2 id="heading-can-you-explain-the-script">Can you explain the script?</h2>
<p><img src="https://miro.medium.com/max/465/1*Utfw9sl2XFHDpyulDvoIvA.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>Doge after seeing the script above</em></p>
<p>Our first script consists of a single statement: the <code>redis.call</code> function:</p>
<pre><code class="lang-lua">redis.call(‘set’, KEYS[1], ARGV[1])
</code></pre>
<p>With<code>redis.call</code> you can execute any Redis command. The first argument is the name of this command followed by its parameters. In the case of the <code>set</code> command, these arguments are <strong>key</strong> and <strong>value</strong>. All Redis commands are supported. <a target="_blank" href="https://redis.io/commands/eval">According to the documentation</a>:</p>
<blockquote>
<p><em>Redis uses the same Lua interpreter to run all the commands</em></p>
</blockquote>
<p>Our second script does a little more than just running a single command — it also returns a value:</p>
<pre><code class="lang-redis">eval “return redis.call(‘get’, KEYS[1])” 1 key:name
</code></pre>
<p>Everything returned by the script is sent to the calling process. In our case, this process is <strong>redis-cli</strong> and you will see the result in your terminal window.</p>
<h2 id="heading-something-more-complex">Something more complex?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/man-looking-at-board.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>A person planning to build a complex Redis script</em></p>
<p>I once used Lua scripts to return elements from a hash map in a particular order. The order itself was specified by hash keys stored in a sorted set.</p>
<p>Let’s first set up our data by running these commands in <strong>redis-cli</strong>:</p>
<pre><code class="lang-redis">hmset hkeys key:1 value:1 key:2 value:2 key:3 value:3 key:4 value:4 key:5 value:5 key:6 value:6
zadd order 1 key:3 2 key:1 3 key:2
</code></pre>
<p>These commands create a hash map at key <code>hkeys</code> and a sorted set at key <code>order</code> which contains selected keys from <code>hkeys</code> in a specific order.</p>
<p><em>You might want to check</em> <a target="_blank" href="https://redis.io/commands/hmset"><em>hmset</em></a> <em>and</em> <a target="_blank" href="https://redis.io/commands/zadd"><em>zadd</em></a> <em>commands reference for details.</em></p>
<p>Let’s run the following script:</p>
<pre><code class="lang-redis">eval “local order = redis.call(‘zrange’, KEYS[1], 0, -1); return redis.call(‘hmget’,KEYS[2],unpack(order));” 2 order hkeys
</code></pre>
<p>You should see the following output:</p>
<pre><code class="lang-redis">“value:3”
“value:1”
“value:2”
</code></pre>
<p>Which means that we got values of the keys we wanted and in the correct order.</p>
<h2 id="heading-do-i-have-to-specify-full-script-text-to-run-it">Do I have to specify full script text to run it?</h2>
<p>No! Redis allows you to preload a script into memory with the <strong>SCRIPT LOAD</strong> command:</p>
<pre><code class="lang-redis">script load “return redis.call(‘get’, KEYS[1])”
</code></pre>
<p>You should see an output like this:</p>
<pre><code class="lang-redis">“4e6d8fc8bb01276962cce5371fa795a7763657ae”
</code></pre>
<p>This is the unique hash of the script which you need to provide to the <strong>EVALSHA</strong> command to run the script:</p>
<pre><code class="lang-redis">evalsha 4e6d8fc8bb01276962cce5371fa795a7763657ae 1 key:name
</code></pre>
<p><em>Note: you should use actual</em> <strong><em>SHA1</em></strong> <em>hash returned by the</em> <strong><em>SCRIPT LOAD</em></strong> <em>command, the hash above is only an example.</em></p>
<h2 id="heading-what-did-you-mention-about-changing-json">What did you mention about changing JSON?</h2>
<p>Sometimes people store JSON objects in Redis. Whether it is a good idea or not is another story, but in practice, this happens a lot.</p>
<p>If you have to change a key in this JSON object, you need to get it from Redis, parse it, change the key, then serialize and set it back to Redis. There are a couple of problems with this approach:</p>
<ol>
<li>Concurrency. Another process can change this JSON between our get and set operations. In this case, the change will be lost.</li>
<li>Performance. If you do these changes often enough and if the object is rather big, this might become the bottleneck of your app. You can win some performance by implementing this logic in Lua.</li>
</ol>
<p>Let’s add a test JSON string to Redis under key <code>obj</code>:</p>
<pre><code class="lang-redis">set obj ‘{“a”:”foo”,”b”:”bar”}’
</code></pre>
<p>Now let’s run our script:</p>
<pre><code class="lang-redis">EVAL ‘local obj = redis.call(“get”,KEYS[1]); local obj2 = string.gsub(obj,”(“ .. ARGV[1] .. “\”:)([^,}]+)”, “%1” .. ARGV[2]); return redis.call(“set”,KEYS[1],obj2);’ 1 obj b bar2
</code></pre>
<p>Now we will have the following object under key <code>obj</code>:</p>
<pre><code>{“a”:”foo”,”b”:”bar2<span class="hljs-string">"}</span>
</code></pre><p>You can instead load this script with the <strong>SCRIPT LOAD</strong> command:</p>
<pre><code class="lang-redis">SCRIPT LOAD ‘local obj = redis.call(“get”,KEYS[1]); local obj2 = string.gsub(obj,”(“ .. ARGV[1] .. “\”:)([^,}]+)”, “%1” .. ARGV[2]); return redis.call(“set”,KEYS[1],obj2);’
</code></pre>
<p> and then run it like this:</p>
<pre><code class="lang-redis">EVALSHA &lt;your_script_sha&gt; 1 obj b bar2
</code></pre>
<p><strong>Some notes:</strong></p>
<ul>
<li>The <code>..</code> is the string concatenation operator in Lua.</li>
<li>We use a RegEx pattern to match key and replace its value. If you don’t understand this Regular Expression, <a target="_blank" href="https://medium.freecodecamp.org/simple-regex-tricks-for-beginners-3acb3fa257cb">you can check my recent guide</a>.</li>
<li>One difference of the Lua RegEx flavor from most other flavors is that we use <code>%</code> as both backreference mark and escape character for RegEx special symbols.</li>
<li>We still escape <code>”</code> with <code>\</code> and not <code>%</code> because we escape Lua string delimiter, not RegEx special symbol.</li>
</ul>
<h2 id="heading-should-i-always-use-lua-scripts">Should I always use Lua scripts?</h2>
<p>No. I recommend only using them when you can prove that it results in better performance. Always run benchmarks first.</p>
<p>If all you want is atomicity, then <a target="_blank" href="https://redis.io/topics/transactions">you should check Redis transactions instead</a>.</p>
<p>Also, your script shouldn’t be too long. Remember that while a script is running, everything else is waiting for it to finish. If your script takes quite some time, it can cause bottlenecks instead of improving performance. The script stops after reaching a timeout (5 seconds by default).</p>
<p><img src="https://miro.medium.com/max/940/1*KuCJoYrILg1eaBYHpEhQhg.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>Redis scripts should not take too much time</em></p>
<h2 id="heading-last-word">Last Word</h2>
<p>For more information on Lua check <a target="_blank" href="http://www.lua.org/start.html">lua.org</a>.</p>
<p>You can check <a target="_blank" href="https://github.com/aikei/redis-json">my node.js library on GitHub</a> for some examples of Lua scripts (see <code>src/lua</code> folder). You can also use this library in node.js to change JSON objects without writing any Lua scripts yourself.</p>
<p>— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —</p>
<p><strong>Thank you for reading this article. Questions and comments are much appreciated. You are also welcome to follow me</strong> <a target="_blank" href="https://twitter.com/aikei_en"><strong>on Twitter</strong></a><strong>.</strong></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Set Up a Fresh Ubuntu Desktop Using Only Dotfiles and Bash Scripts ]]>
                </title>
                <description>
                    <![CDATA[ One of my most favourite things about open source files on GitHub is the ability to see how others do (what some people might call) mundane things, like set up their .bashrc and other dotfiles. While I’m not as enthusiastic about ricing as I was when... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-set-up-a-fresh-ubuntu-desktop-using-only-dotfiles-and-bash-scripts/</link>
                <guid isPermaLink="false">66bd8f512fbe28509b2d6e92</guid>
                
                    <category>
                        <![CDATA[ Bash ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Scripting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ shell script ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Victoria Drake ]]>
                </dc:creator>
                <pubDate>Wed, 21 Aug 2019 13:05:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/08/E62F2B0D-E706-4A55-BAAC-84EF3F59D538.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>One of my most favourite things about open source files on GitHub is the ability to see how others do (what some people might call) mundane things, like set up their <code>.bashrc</code> and other dotfiles. While I’m not as enthusiastic about ricing as I was when I first came to the Linux side, I still get pretty excited when I find a config setting that makes things prettier and faster, and thus, better.</p>
<p>I recently came across a few such things, particularly in <a target="_blank" href="https://github.com/tomnomnom">Tom Hudson’s</a> dotfiles. Tom seems to like to script things, and some of those things include automatically setting up symlinks, and installing Ubuntu repository applications and other programs. This got me thinking. Could I automate the set up of a new machine to replicate my current one?</p>
<p>Being someone generally inclined to take things apart in order to see how they work, I know I’ve messed up my laptop on occasion. (Usually when I’m away from home, and my backup harddrive isn’t.) On those rare but really inconvenient situations when my computer becomes a shell of its former self, (ba-dum-ching) it’d be quite nice to have a fast, simple way of putting Humpty Dumpty back together again, just the way I like.</p>
<p>In contrast to creating a <a target="_blank" href="https://askubuntu.com/questions/19901/how-to-make-a-disk-image-and-restore-from-it-later">disk image and restoring it later</a>, a collection of bash scripts is easier to create, maintain, and move around. They require no special utilities, only an external transportation method. It’s like passing along the recipe, instead of the whole bundt cake. (Mmm, cake.)</p>
<p>Additionally, functionality like this would be super useful when setting up a virtual machine, or VM, or even just a virtual private server, or VPS. (Both of which, now that I write this, would probably make more forgiving targets for my more destructive experimentations… live and learn!)</p>
<p>Well, after some grepping and Googling and digging around, I now have a suite of scripts that can do this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/cover-1.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>See <a target="_blank" href="https://victoria.dev/verbose/how-to-set-up-a-fresh-ubuntu-desktop-using-only-dotfiles-and-bash-scripts">a video of the setup here</a>.</em></p>
<p>This is the tail end of a test run of the set up scripts on a fresh Ubuntu desktop, loaded off a bootable USB. It had all my programs and settings restored in under three minutes!</p>
<p>This post will cover how to achieve the automatic set up of a computer running Ubuntu Desktop (in my case, Ubuntu LTS 18.04) using bash scripts. The majority of the information covered is applicable to all the Linux desktop flavours, though some syntax may differ. The bash scripts cover three main areas: linking dotfiles, installing software from Ubuntu and elsewhere, and setting up the desktop environment. We’ll cover each of these areas and go over the important bits so that you can begin to craft your own scripts.</p>
<h1 id="heading-dotfiles">Dotfiles</h1>
<p>Dotfiles are what most Linux enthusiasts call configuration files. They typically live in the user’s home directory (denoted in bash scripts with the <a target="_blank" href="https://www.tldp.org/LDP/abs/html/internal.html#BUILTINREF">builtin</a> variable <code>$HOME</code>) and control the appearance and behaviour of all kinds of programs. The file names begin with <code>.</code>, which denotes hidden files in Linux (hence “dot” files). Here are some common dotfiles and ways in which they’re useful.</p>
<h2 id="heading-bashrc"><code>.bashrc</code></h2>
<p>The <code>.bashrc</code> file is a list of commands executed at startup by interactive, non-login shells. <a target="_blank" href="https://www.tldp.org/LDP/abs/html/intandnonint.html">Interactive vs non-interactive shells</a> can be a little confusing, but aren’t necessary for us to worry about here. For our purposes, any time you open a new terminal, see a prompt, and can type commands into it, your <code>.bashrc</code> was executed.</p>
<p>Lines in this file can help improve your workflow by creating aliases that reduce keystrokes, or by displaying a helpful prompt with useful information. It can even run user-created programs, like <a target="_blank" href="https://github.com/victoriadrake/eddie-terminal">Eddie</a>. For more ideas, you can have a look at <a target="_blank" href="https://github.com/victoriadrake/dotfiles/blob/ubuntu-19.10/.bashrc">my <code>.bashrc</code> file on GitHub</a>.</p>
<h2 id="heading-vimrc"><code>.vimrc</code></h2>
<p>The <code>.vimrc</code> dotfile configures the champion of all text editors, <a target="_blank" href="https://www.vim.org/about.php">Vim</a>. (If you haven’t yet wielded the powers of the keyboard shortcuts, I highly recommend <a target="_blank" href="https://vim-adventures.com/">a fun game to learn Vim with</a>.)</p>
<p>In <code>.vimrc</code>, we can set editor preferences such as display settings, colours, and custom keyboard shortcuts. You can take a look at <a target="_blank" href="https://github.com/victoriadrake/dotfiles/blob/ubuntu-19.10/.vimrc">my <code>.vimrc</code> on GitHub</a>.</p>
<p>Other dotfiles may be useful depending on the programs you use, such as <code>.gitconfig</code> or <code>.tmux.conf</code>. Exploring dotfiles on GitHub is a great way to get a sense of what’s available and useful to you!</p>
<h1 id="heading-linking-dotfiles">Linking dotfiles</h1>
<p>We can use a script to create symbolic links, or <a target="_blank" href="https://en.wikipedia.org/wiki/Symbolic_link#POSIX_and_Unix-like_operating_systems">symlinks</a> for all our dotfiles. This allows us to keep all the files in a central repository, where they can easily be managed, while also providing a sort of placeholder in the spot that our programs expect the configuration file to be found. This is typically, but not always, the user home directory. For example, since I store my dotfiles on GitHub, I keep them in a directory with a path like <code>~/github/dotfiles/</code> while the files themselves are symlinked, resulting in a path like <code>~/.vimrc</code>.</p>
<p>To programmatically check for and handle any existing files and symlinks, then create new ones, we can use <a target="_blank" href="https://github.com/victoriadrake/dotfiles/blob/master/scripts/symlink.sh">this elegant shell script</a>. I compliment it only because I blatantly stole the core of it from <a target="_blank" href="https://github.com/tomnomnom/dotfiles/blob/master/setup.sh">Tom’s setup script</a>, so I can’t take the credit for how lovely it is.</p>
<p>The <code>symlink.sh</code> script works by attempting to create symlinks for each dotfile in our <code>$HOME</code>. It first checks to see if a symlink already exists, or if a regular file or directory with the same name exists. In the former case, the symlink is removed and remade; in the latter, the file or directory is renamed, then the symlink is made.</p>
<h1 id="heading-installing-software">Installing software</h1>
<p>One of the beautiful things about exploring shell scripts is discovering how much can be achieved using only the command line. As someone whose first exposure to computers was through a graphical operating system, I find working in the terminal to be refreshingly fast.</p>
<p>With Ubuntu, most programs we likely require are available through the default Ubuntu software repositories. We typically search for these with the command <code>apt search &lt;program&gt;</code> and install them with <code>sudo apt install &lt;program&gt;</code>. Some software we’d like may not be in the default repositories, or may not be offered there in the most current version. In these cases, we can still install these programs in Ubuntu using a <a target="_blank" href="https://en.wikipedia.org/wiki/Ubuntu#Package_Archives">PPA, or Personal Package Archive</a>. We’ll just have to be careful that the PPAs we choose are from the official sources.</p>
<p>If a program we’d like doesn’t appear in the default repositories or doesn’t seem to have a PPA, we may still be able to install it via command line. A quick search for “installation command line” should get some answers.</p>
<p>Since bash scripts are just a collection of commands that we could run individually in the terminal, creating a script to install all our desired programs is as straightforward as putting all the commands into a script file. I chose to organize my installation scripts between the default repositories, which are installed by <a target="_blank" href="https://github.com/victoriadrake/dotfiles/blob/ubuntu-19.10/scripts/aptinstall.sh">my <code>aptinstall.sh</code> script</a>, and programs that involve external sources, handled with <a target="_blank" href="https://github.com/victoriadrake/dotfiles/blob/ubuntu-19.10/scripts/programs.sh">my <code>programs.sh</code> script.</a></p>
<h1 id="heading-setting-up-the-desktop-environment">Setting up the desktop environment</h1>
<p>On the recent occasions when I’ve gotten a fresh desktop (intentionally or otherwise) I always seem to forget how long it takes to remember, find, and then change all the desktop environment settings. Keyboard shortcuts, workspaces, sound settings, night mode… it adds up!</p>
<p>Thankfully, all these settings have to be stored somewhere in a non-graphical format, which means that if we can discover how that’s done, we can likely find a way to easily manipulate the settings with a bash script. Lo and behold the terminal command, <code>gsettings list-recursively</code>.</p>
<p>There are a heck of a lot of settings for GNOME desktop environment. We can make the list easier to scroll through (if, like me, you’re sometimes the type of person to say “Just let me look at everything and figure out what I want!”) by piping to <code>less</code>: <code>gsettings list-recursively | less</code>. Alternatively, if we have an inkling as to what we might be looking for, we can use <code>grep</code>: <code>gsettings list-recursively | grep 'keyboard'</code>.</p>
<p>We can manipulate our settings with the <code>gsettings set</code> command. It can sometimes be difficult to find the syntax for the setting we want, so when we’re first building our script, I recommend using the GUI to make the changes, then finding the <code>gsettings</code> line we changed and recording its value.</p>
<p>For some inspiration, you can view <a target="_blank" href="https://github.com/victoriadrake/dotfiles/blob/ubuntu-19.10/scripts/desktop.sh">my <code>desktop.sh</code> settings script on GitHub</a>.</p>
<h1 id="heading-putting-it-all-together">Putting it all together</h1>
<p>Having modular scripts (one for symlinks, two for installing programs, another for desktop settings) is useful for both keeping things organized and for being able to run some but not all of the automated set up. For instance, if I were to set up a VPS in which I only use the command line, I wouldn’t need to bother with installing graphical programs or desktop settings.</p>
<p>In cases where I do want to run all the scripts, however, doing so one-by-one is a little tedious. Thankfully, since bash scripts can themselves be run by terminal commands, we can simply write another master script to run them all!</p>
<p>Here’s my master script to handle the set up of a new Ubuntu desktop machine:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>

./symlink.sh
./aptinstall.sh
./programs.sh
./desktop.sh

<span class="hljs-comment"># Get all upgrades</span>
sudo apt upgrade -y

<span class="hljs-comment"># See our bash changes</span>
<span class="hljs-built_in">source</span> ~/.bashrc

<span class="hljs-comment"># Fun hello</span>
figlet <span class="hljs-string">"... and we're back!"</span> | lolcat
</code></pre>
<p>I threw in the upgrade line for good measure. It will make sure that the programs installed on our fresh desktop have the latest updates. Now a simple, single bash command will take care of everything!</p>
<p>You may have noticed that, while our desktop now looks and runs familiarly, these scripts don’t cover one very important area: our files. Hopefully, you have a back up method for those that involves some form of reliable external hardware. If not, and if you tend to put your work in external repository hosts like GitHub or GitLab, I do have a way to <a target="_blank" href="https://victoria.dev/verbose/how-to-write-bash-one-liners-for-cloning-and-managing-github-and-gitlab-repositories/">automatically clone and back up your GitHub repositories with bash one-liners</a>.</p>
<p>Relying on external repository hosts doesn’t offer 100% coverage, however. Files that you wouldn’t put in an externally hosted repository (private or otherwise) consequently can’t be pulled. Git ignored objects that can’t be generated from included files, like private keys and secrets, will not be recreated. Those files, however, are likely small enough that you could fit a whole bunch on a couple encrypted USB flash drives (and if you don’t have private key backups, maybe you ought to do that first?).</p>
<p>That said, I hope this post has given you at least some inspiration as to how dotfiles and bash scripts can help to automate setting up a fresh desktop. If you come up with some settings you find useful, please help others discover them by sharing your dotfiles, too!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to automate your project and Github repo setup from the command line ]]>
                </title>
                <description>
                    <![CDATA[ This post comes out of an irritation I faced personally, when I was first learning to code - setting up my local repo and syncing with Github. I learned by doing projects (often freeCodeCamp ones!). But I needed to make sure I didn't lose my hard wor... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/automate-project-github-setup-mac/</link>
                <guid isPermaLink="false">66d461c438f2dc3808b7912a</guid>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Scripting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ workflow ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Zubin Pratap ]]>
                </dc:creator>
                <pubDate>Tue, 20 Aug 2019 08:59:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/08/octo.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>This post comes out of an irritation I faced personally, when I was first learning to code - setting up my local repo and syncing with Github.</p>
<p>I learned by doing projects (often freeCodeCamp ones!). But I needed to make sure I didn't lose my hard work, and that others could see the hard work I was putting in, so every project <em>had</em> to go on Github. <a target="_blank" href="https://www.freecodecamp.org/news/learned-to-code-job-ready-and-heres-why/">The more complete projects I had on Github, the easier it would be for recruiters</a>. But the steps required to set up a project, initialise a repo, and sync with Github were really annoying and repetitive, so I decided to solve the problem.</p>
<p>Bad news: this isn't going to be a big, fancy, detailed and technically sexy post. It's going to be very un-sexy.</p>
<p>Good news: you don't need to be a shell scripting god(dess) to do it.</p>
<p>So my typical project setup work flow usually goes like this:</p>
<ol>
<li><p>Go to my <code>../projects</code> folder and run <code>mkdir project-of-some-name</code> to create a folder with the name <code>project-of-some-name</code>.</p>
</li>
<li><p><code>cd</code> into that project folder and do <code>git init</code> to initialise a local git repo in there.</p>
</li>
<li><p>run <code>touch README.MD</code> to create the <code>README</code> file, open it and add some basic descriptions, including links to the resources / tutorials I was implementing in that project. Save the file.</p>
</li>
<li><p>run <code>git add .</code> and then <code>git commit -m ' ...some initial commit message...</code></p>
</li>
<li><p>open a browser, go to Github, login, create a new (remote) repository, copy the url, return to my terminal, make sure I was in the correct project folder <code>project-of-some-name</code>...then run the git scripts needed to set up the remote repo as the 'upstream' repo and connect my local repo to it. Then, finally, I can run a <code>git push</code> and my local commit would get pushed up</p>
</li>
<li><p>lie down and take a nap, exhausted from this repetitive process.</p>
</li>
</ol>
<p>Admittedly, this was my process, but I liked to stay organised and always be able to access my projects so I can refer to them.</p>
<p>Since automation is a great way to practice your coding skills, I decided to write a small shell script that automates these horrible and repetitive steps. The script is at the bottom of this post, and be warned - it's not sophisticated or fancy. But it sure gets the job done, and I don't need to log in to Github and fool around will all those steps!</p>
<p>Before you copy the script, you need to know how to run it on your Mac. So below are the steps you need to implement to be able to use the script to automate your setup workflow.</p>
<ol>
<li><p>I keep my scripts in my root/home folder, in a sub-folder called <code>scripts</code>. I suggest you do the same or similar. To get to the root/home folder, in your terminal type <code>cd ~</code> because the tilda ( <code>~</code> )is the symbol for the home folder. In your Mac Finder app it shows up as the one with a house icon. So all my scripts are stored in <code>~/scripts</code></p>
</li>
<li><p>This matters because to run a shell script from any directory in the terminal, you have to type out the full path. In my case I have to type out <code>~/scripts/git-script.sh</code> to run the script. But we're getting ahead of ourselves.</p>
</li>
<li><p>copy the code chunk at the bottom of this post and then open a text editor, paste it in and then save it as <code>[filename].sh</code>. The <code>.sh</code> is the extension for shell scripts. Save the file in the directory you want to save it at - again I recommend <code>~/scripts</code> as the folder to save your scripts in.</p>
</li>
<li><p>Navigate to that folder in your terminal. To be safe run <code>ls</code> in the terminal to check that you can see the script is there. If it's not you're in the wrong folder or step 3 didn't successfully complete.</p>
</li>
<li><p>Make the shell script executable. To do that you type the following in the terminal: <code>chmod +x &lt;&lt;the-correct-filename.sh&gt;&gt;</code>. This is the unix way to make a shell script "executable". I'm not confident I fully understand what that means, other than it's needed to make any shell scripts you write executable, so don't ask me and I won't lie to you.</p>
</li>
<li><p>navigate to your projects folder and make a new folder that you intend to house your project. Effectively, you've got to do this: <code>mkdir</code> - create a <code>project-of-some-name</code> inside the folder where you keep all your projects. So your project will eventually be placed inside <code>my-computer/my-projects/project-of-some-name</code>. <code>cd</code> into this folder and then type <code>pwd</code> to get the full path. Copy that - you will need to paste it shortly. It should look like <code>my-computer/my-projects/project-of-some-name</code></p>
</li>
<li><p>open your terminal again, and then type <code>~/scripts/</code>&lt;&lt;the-correct-filename.sh&gt;&gt;``. The script runs! You will be guided through some input... The main steps are:</p>
<blockquote>
<p>what do you want to call your Github repo (<strong>don't use spaces-</strong> 'my-awesome-project' is good. Don't use 'my awesome project' as the repo name.</p>
<p>Enter a description that shows up in the Github repo's description. For this it's safe to use spaces.</p>
</blockquote>
</li>
</ol>
<blockquote>
<p>Enter the project path you got in step 6, the one that you get after typing <code>pwd</code> in the terminal and getting something like <code>my-computer/my-projects/project-of-some-name</code></p>
<p>enter your Github username (not email address) and then your Github password. Be careful as you type as these values don't show up on the screen.</p>
<p>....that's it. The script will set up your git repo locally inside <code>my-computer/my-projects/project-of-some-name</code> and then create a <code>README.MD</code> (blank) and then commit it locally, then set up a remote repo in Github (log you in via API) etc and then push everything up!</p>
<p>finally, you will see that the terminal you were interacting with has changed the currently active directory to your project folder. It will now be at <code>my-computer/my-projects/project-of-some-name</code> and you can type in <code>ls</code> and see the <code>README.MD</code> file. If you then type <code>git status</code> you will see your local repo's status (the state of your local project) and if you type in <code>git remote</code> it will show you the Github url for your project!</p>
</blockquote>
<p>Done! Happy Coding!</p>
<p>Annnd.....finally......here is the script! I've commented each step so you can reason your way through it.</p>
<pre><code class="lang-plaintext"># Make executable with chmod +x &lt;&lt;filename.sh&gt;&gt;

CURRENTDIR=${pwd}

# step 1: name of the remote repo. Enter a SINGLE WORD ..or...separate with hyphens
echo "What name do you want to give your remote repo?"
read REPO_NAME

echo "Enter a repo description: "
read DESCRIPTION


# step 2:  the local project folder path
echo "what is the absolute path to your local project directory?"
read PROJECT_PATH

echo "What is your github username?"
read USERNAME

# step 3 : go to path 
cd "$PROJECT_PATH"


# step 4: initialise the repo locally, create blank README, add and commit
git init
touch README.MD
git add README.MD
git commit -m 'initial commit -setup with .sh script'


# step 5 use github API to log the user in
curl -u ${USERNAME} https://api.github.com/user/repos -d "{\"name\": \"${REPO_NAME}\", \"description\": \"${DESCRIPTION}\"}"

#  step 6 add the remote github repo to local repo and push
git remote add origin https://github.com/${USERNAME}/${REPO_NAME}.git
git push --set-upstream origin master

# step 7 change to your project's root directory.
cd "$PROJECT_PATH"

echo "Done. Go to https://github.com/$USERNAME/$REPO_NAME to see." 
echo " *** You're now in your project root. ***"
</code></pre>
<h4 id="heading-thanks-for-reading">Thanks for reading!</h4>
<p>If you would like to learn more about my journey into code, check out <a target="_blank" href="http://podcast.freecodecamp.org/53-zubin-pratap-from-lawyer-to-developer">episode 53</a> of the <a target="_blank" href="http://podcast.freecodecamp.org/">freeCodeCamp podcast</a>, where Quincy (founder of freeCodeCamp) and I share our experiences as career changers that may help you on your journey. You can also access the podcast on <a target="_blank" href="https://itunes.apple.com/au/podcast/ep-53-zubin-pratap-from-lawyer-to-developer/id1313660749?i=1000431046274&amp;mt=2">iTunes</a>, <a target="_blank" href="https://www.stitcher.com/podcast/freecodecamp-podcast/e/59201373?autoplay=true">Stitcher</a>, and <a target="_blank" href="https://open.spotify.com/episode/4lG0RGpzriG5vXRMgza05C">Spotify</a>.</p>
<p>I will also hold a few AMAs and webinars in the coming months. If this is of interest to you please let me know by going <a target="_blank" href="http://www.matchfitmastery.com/">here</a>. And of course, you can also Tweet me at <a target="_blank" href="https://twitter.com/zubinpratap">@ZubinPratap</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Functional and flexible shell scripting tricks ]]>
                </title>
                <description>
                    <![CDATA[ By BinHong Lee Shell scripts vs python or Perl It's 2020 now, who writes shell scripts anymore? Am I right? Well, apparently I do. ¯_(ツ)_/¯ There are some good arguments for that here and here which mainly revolve around 2 things: Shell exists in al... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/functional-and-flexible-shell-scripting-tricks-a2d693be2dd4/</link>
                <guid isPermaLink="false">66c34affa1d481faeda49b54</guid>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Scripting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ shell script ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 07 May 2019 16:15:12 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*ZB2nVJjipU4repVb" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By BinHong Lee</p>
<h3 id="heading-shell-scripts-vs-python-or-perl">Shell scripts vs python or Perl</h3>
<p>It's 2020 now, who writes shell scripts anymore? Am I right? Well, apparently I do. ¯_(ツ)_/¯</p>
<p>There are some good arguments for that <a target="_blank" href="https://stackoverflow.com/questions/796319/strengths-of-shell-scripting-compared-to-python#796343">here</a> and <a target="_blank" href="https://www.linuxquestions.org/questions/linux-newbie-8/what-is-the-difference-between-perl-and-shell-scripting-4175486499/">here</a> which mainly revolve around 2 things:</p>
<ol>
<li>Shell exists in all Unix systems and makes use of system default features.</li>
<li>Shell is an “interactive command function” designed to get user inputs during the process of running them.</li>
</ol>
<p>Also, <a target="_blank" href="https://stackoverflow.com/questions/5725296/difference-between-sh-and-bash">here</a>’s an additional relevant reading about the differences between <code>sh</code> and <code>bash</code>.</p>
<h3 id="heading-arguments">Arguments</h3>
<p>In some occasions, you will need to pass an argument (or expect one) into the script like how you might pass a param into a function. In that case, you will use something like <code>$1</code> for the first argument, <code>$2</code> for the second. Here's an example of how it would look like:</p>
<p>In script <code>run_this.sh</code>:</p>
<pre><code>echo <span class="hljs-string">"The input message was $1."</span>
</code></pre><p>Running the command:</p>
<pre><code>./run_this.sh userInputThe input message was userInput.
</code></pre><p>_Note: The params are separated by spaces so if you want to input a string as a param that contains a space, it might need to be something like <code>./run_this.sh "user input"</code> just so <code>"user input"</code> would be counted as <code>$1</code> entirely._</p>
<p>In the occasion where you are not sure how long the user input might be and you want to capture it all, you would use <code>$@</code> instead. In the following example, I took in the entire string and print it out word by word after breaking them into a string array according to the spaces in between.</p>
<p>In script <code>run_this.sh</code>:</p>
<pre><code>userInputs=($@)<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-string">"${userInputs[@]}"</span>;; <span class="hljs-keyword">do</span>  echo <span class="hljs-string">"$i"</span>done
</code></pre><p>Running the command:</p>
<pre><code>./run_this.sh who knows how long <span class="hljs-built_in">this</span> can gowhoknowshowlongthiscango
</code></pre><h3 id="heading-functions">Functions</h3>
<p>If you have done any sort of programming, you should be familiar with the concept of <em>functions</em>. It's basically a set of commands / operations that you will be repeating over and over again. Instead of repeating it multiple times in your code, you can put them into a function. Then just call the function which effectively reduces the lines of code that need to be written.</p>
<p><em>Side note: If you don’t know already, LOC is a horrible metric for any sort of measurement in terms of programming. Don’t take this from me, take this from <a target="_blank" href="https://www.goodreads.com/quotes/536587-measuring-programming-progress-by-lines-of-code-is-like-measuring">Bill Gates</a>:</em></p>
<blockquote>
<p>“Measuring programming progress by lines of code is like measuring aircraft building progress by weight.”</p>
</blockquote>
<p>Here’s how a normal function looks like:</p>
<pre><code># Declaring the functiondoSomething() {
</code></pre><pre><code>}
</code></pre><pre><code># Calling the functiondoSomething
</code></pre><p>Pretty straightforward and easy to understand. Now, here are a few differences between functions in shell scripts and a normal programming language.</p>
<h3 id="heading-parameters">Parameters</h3>
<p>If you were to pass a parameter / use a parameter into a function in Java, you have to declare them in the function declaration. They look something like this.</p>
<pre><code>public <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> main(<span class="hljs-built_in">String</span>[] args) {    doSomething(<span class="hljs-string">"random String"</span>);}
</code></pre><pre><code>private <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> doSomething (<span class="hljs-built_in">String</span> words) {    System.out.println(words);}
</code></pre><p>In the shell, however, they do not require a declaration of types or names at all. Each of them is like a separate script that lives in the script itself. If you were to use a param, just pass it in and call it like how you would do it if you were taking in input for this script at the top level. Something like this:</p>
<pre><code>doSomething() {    echo $<span class="hljs-number">1</span>}
</code></pre><pre><code>doSomething <span class="hljs-string">"random String"</span>
</code></pre><ol>
<li>Similar to above, if you want to take in everything, you will use <code>$@</code> instead of <code>$1</code> since <code>$1</code> would only use the first input (and <code>$2</code> for the second etc.).</li>
<li>Functions need to be declared ahead of where they are being called. (Usually beginning of the file before any main operations.)</li>
</ol>
<h3 id="heading-return">Return</h3>
<p>Let’s say we create a script like below named <code>run_this.sh</code>:</p>
<pre><code>doSomething() {    echo <span class="hljs-string">"magic"</span>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>}
</code></pre><pre><code>output=<span class="hljs-string">`doSomething`</span>echo $output
</code></pre><p>Now let’s run it and see what is being assigned to the <code>output</code> variable.</p>
<pre><code>$ ./run_this.shmagic
</code></pre><p>Note that instead of <code>0</code>, it shows <code>magic</code> instead. This is because when you do <code>output=</code>doSomething<code>`, it assigns the output message to</code>output` instead of the return value since the output message is how you communicate almost anything in the shell script.</p>
<p>So when does it make sense to use the <code>return</code> call? When you are using it as part of an if statement. Something like this:</p>
<p>In script <code>run_this.sh</code>:</p>
<pre><code>doSomething() {    echo <span class="hljs-string">"magic"</span>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>}
</code></pre><pre><code><span class="hljs-keyword">if</span> doSomething; then    echo <span class="hljs-string">"Its true!"</span>fi
</code></pre><p>Running the command:</p>
<pre><code>./run_this.shIts <span class="hljs-literal">true</span>!
</code></pre><p>In this case, <code>return 0</code> means <code>true</code> while <code>return 1</code> meant <code>false</code> in a traditional <code>boolean</code> sense.</p>
<h3 id="heading-multi-line-echo">Multi-line echo</h3>
<p>There are times when you need to print a multi-line message. There are a few ways to go around this. The easiest way is to use <code>echo</code> multiple times like this:</p>
<pre><code>echo <span class="hljs-string">"line1"</span>echo <span class="hljs-string">"line2"</span>echo <span class="hljs-string">"line3"</span>
</code></pre><p>It works but probably not the most elegant way to get around this. Instead, you can use <code>cat &lt;&amp;l</code>t; EOF instead. Something like this:</p>
<pre><code>cat &lt;&lt; EOFline1line2line3EOF
</code></pre><p>Note that there should not be anything (including spaces or tabs) before <code>EOF</code>. If you want to do it in an <code>if</code>statement, it should look something like this.</p>
<pre><code><span class="hljs-keyword">if</span> [ <span class="hljs-string">"a"</span> == <span class="hljs-string">"a"</span> ]; then  cat &lt;&lt; EOFline1line2line3EOFfi
</code></pre><p>Realize that even the messages themselves are aligned to the left. This is because if you leave them tabbed, the output message shown in the command line will also be tabbed. Also, if <code>EOF</code> is tabbed, the shell will complain about it and usually ends the script there.</p>
<h3 id="heading-flags-options">Flags / Options</h3>
<p>You’ve probably seen some of the scripts or commands that comes with an ability to add flags (and sometimes arguments for the specific flag). Something like <code>git commit -a -m "Some commit message"</code>.</p>
<p>Here’s a quick example of how it looks like (I’ve tried to be as comprehensive as possible with the example.)</p>
<p>In script <code>run_this.sh</code>:</p>
<pre><code><span class="hljs-keyword">while</span> getopts ac: opt; <span class="hljs-keyword">do</span>    <span class="hljs-keyword">case</span> $opt <span class="hljs-keyword">in</span>        a)            echo <span class="hljs-string">"\"a\" was executed."</span>            ;;        c)            echo <span class="hljs-string">"\"c\" was executed with parameter \"$OPTARG\"."</span>            ;;        \?)            echo <span class="hljs-string">"Invalid option: -$opt"</span>            exit <span class="hljs-number">1</span>            ;;        :)            echo <span class="hljs-string">"option -$opt requires an argument."</span>            exit <span class="hljs-number">1</span>            ;;    esacdone
</code></pre><p>Running the command:</p>
<pre><code>./run_this.sh
</code></pre><pre><code>./run_this.sh -a<span class="hljs-string">"a"</span> was executed.
</code></pre><pre><code>./run_this.sh -coption -c requires an argument.
</code></pre><pre><code>./run_this.sh -c abcd<span class="hljs-string">"c"</span> was executed <span class="hljs-keyword">with</span> parameter <span class="hljs-string">"abcd"</span>.
</code></pre><pre><code>./run_this.sh -a -c abc<span class="hljs-string">"a"</span> was executed.<span class="hljs-string">"c"</span> was executed <span class="hljs-keyword">with</span> parameter <span class="hljs-string">"abc"</span>.
</code></pre><pre><code>./run_this.sh -xInvalid option: -x
</code></pre><p>In the above example, the differences between option <code>-a</code> and <code>-c</code> is that in the <code>getopts</code> line, <code>c</code> has a colon (<code>:</code>) following it after therefore telling the program to expect a parameter for the option. Another thing to keep in mind is that the options need to be declared in an alphabetical way. If you declare something like <code>acb</code>, the <code>b</code>declaration would be ignored, and using the <code>-b</code> flag would lead to the error message instead of the <code>b</code> case in the switch condition.</p>
<p>Thanks for reading!</p>
<h3 id="heading-about-me">About me</h3>
<p>I currently work at Facebook as a Software Engineer. I spend some of my free time experimenting and building new things with technologies I find fun and interesting. Follow my exploration journey <a target="_blank" href="https://binhong.me/blog">here</a> or on <a target="_blank" href="https://github.com/binhonglee">GitHub</a>.</p>
<h3 id="heading-references">References</h3>
<ul>
<li><a target="_blank" href="http://wiki.bash-hackers.org/howto/getopts_tutorial">Small getopts tutorial</a></li>
<li><a target="_blank" href="https://stackoverflow.com/questions/10969953/how-to-output-a-multiline-string-in-bash#10970616">How to output a multiline string in Bash</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ ABS 1.2: background commands & the ability to import files ]]>
                </title>
                <description>
                    <![CDATA[ By Alex Nadalin ABS is a programming language that puts together the productivity of bash scripts with the elegance of high-level languages such as Python or Ruby. It lets you issue system commands by simply wrapping them in backticks (very similar t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/abs-1-2-background-commands-the-ability-to-import-files-e5d1e046cb35/</link>
                <guid isPermaLink="false">66c343794f1fc448a3678f8a</guid>
                
                    <category>
                        <![CDATA[ Bash ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Scripting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 09 Apr 2019 19:13:17 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*ph8ChGDlQ8vqoWTp.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Alex Nadalin</p>
<p><a target="_blank" href="https://www.abs-lang.org/">ABS</a> is a programming language that puts together the productivity of bash scripts with the elegance of high-level languages such as Python or Ruby. It lets you issue system commands by simply wrapping them in backticks (very similar to how you would do it in Bash) and lets you use their output with clear and concise syntax.</p>
<p>This is, for example, a script that would try to issue a <code>curl</code> command, and exit if the server at <code>example.org</code> would not reply within 10 seconds:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/dSVYyySoZGbfUcDXgDVHZ674fUKRRG5GGL8n" alt="Image" width="600" height="400" loading="lazy"></p>
<p>A few weeks ago the <a target="_blank" href="https://github.com/abs-lang/abs">ABS team</a> managed to pull together a new minor release of the language, <a target="_blank" href="https://github.com/abs-lang/abs/releases/tag/1.2.0">1.2.0</a>, which includes loads of interesting features — let’s get to them!</p>
<h3 id="heading-absrc">~/.absrc</h3>
<p>ABS will now look for a default <code>~/.absrc</code> file to preload every time you run a script. This is especially useful if you’d like to dump “base” functions you’re likely to re-use across scripts in a common place. Your <code>.absrc</code> could look like:</p>
<pre><code>tenth = f(x) {   <span class="hljs-keyword">return</span> x / <span class="hljs-number">10</span> }
</code></pre><p>so that in any other abs script you can <code>tenth(x)</code>.</p>
<h3 id="heading-abshistory">~/.abs_history</h3>
<p>We also introduced an history file in order to be able to repeat commands easily when using the ABS repl. This is, by default, located at <code>~/.abs_history</code> and gets synced every time you close a repl session:</p>
<pre><code>$ absHello alex, welcome to the ABS (<span class="hljs-number">1.2</span><span class="hljs-number">.0</span>) programming language!Type <span class="hljs-string">'quit'</span> when you<span class="hljs-string">'re done, '</span>help<span class="hljs-string">' if you get lost!⧐  `sleep 1`</span>
</code></pre><pre><code>⧐  quitAdios!$ tail ~/.abs_history<span class="hljs-string">`sleep 1`</span>
</code></pre><h3 id="heading-requirefile">require(file)</h3>
<p>A big one here: you can now <strong>require external files</strong> through <code>require(path/to/file.abs )</code>.</p>
<p>This is a stepping stone that allows creating base libraries that can be re-used across ABS scripts, and organizes ABS code a tad better.</p>
<h3 id="heading-background-commands">Background commands</h3>
<p>Another big feature here: you can now issue “background” commands that won’t block your ABS script (these commands are executed within a <a target="_blank" href="https://tour.golang.org/concurrency/1">Goroutine</a>).</p>
<p>A background command differs from a regular one simply because it employs an <code>&amp;</code> at the end of the command itself — let’s see them in action:</p>
<pre><code><span class="hljs-string">`sleep 10`</span>echo(<span class="hljs-string">"Hello world!"</span>) # This will be printed after <span class="hljs-number">10</span>s
</code></pre><pre><code><span class="hljs-string">`sleep 10 &amp;`</span>echo(<span class="hljs-string">"Hello world!"</span>) # This will be printed immediately
</code></pre><p>You can check whether a background command is done with the <code>.done</code> property:</p>
<pre><code>cmd = <span class="hljs-string">`sleep 10 &amp;`</span>cmd.done # falsewait(<span class="hljs-number">10000</span>)cmd.done # <span class="hljs-literal">true</span>
</code></pre><p>and we’ve added the <code>wait()</code> function if you need to block until the command is done:</p>
<pre><code>cmd = <span class="hljs-string">`sleep 10 &amp;`</span>cmd.wait() # The script will be blocked <span class="hljs-keyword">for</span> <span class="hljs-number">10</span>secho(<span class="hljs-string">"Hello world!"</span>)
</code></pre><h3 id="heading-misc">Misc</h3>
<p>A few more features that made it into this release:</p>
<ul>
<li>number functions such as <code>floor</code>, <code>round</code> and <code>ceil</code></li>
<li><code>cd()</code>, which switches the <code>cwd</code> of a script</li>
<li>you can play around with your prompt by setting the environment variables <code>ABS_PROMPT_LIVE_PREFIX=true</code> and <code>ABS_PROMPT_PREFIX=templated_string</code>. The templated string can use <code>{dir}</code>, <code>{user}</code>, <code>{host}</code> that will be replaced on-the-fly. For further info, have a look at the sample <a target="_blank" href="https://github.com/abs-lang/abs/blob/d1e92899ed0d6b3abb7a0a3fc6ec18d13dbe3ff2/tests/test-absrc.abs">.absrc</a> file</li>
</ul>
<h3 id="heading-bugfixes">Bugfixes</h3>
<p>As usual, we managed to fix some minor bugs along the way:</p>
<ul>
<li>fixed a few random panics when calling built-in functions without enough arguments (<a target="_blank" href="https://github.com/abs-lang/abs/pull/193">#193</a>)</li>
<li>windows commands are now using cmd.exe rather than bash, as bash might not be available on the system (<a target="_blank" href="https://github.com/abs-lang/abs/pull/180">#180</a>)</li>
<li>better error messages when parsing “invalid” numbers (<a target="_blank" href="https://github.com/abs-lang/abs/pull/182">#182</a>)</li>
<li>the ABS installer was not working with wget 1.20.1 (<a target="_blank" href="https://github.com/abs-lang/abs/pull/178">#178</a>)</li>
<li>the ABS parser now supports numbers in scientific notation (eg. 8.366100560806463e-7, <a target="_blank" href="https://github.com/abs-lang/abs/pull/174">#174</a>)</li>
<li>errors on built-in functions would not report the correct error line / column numbers (<a target="_blank" href="https://github.com/abs-lang/abs/pull/168">#168</a>)</li>
</ul>
<h3 id="heading-now-what">Now what?</h3>
<p>Install ABS with a simple one-liner:</p>
<pre><code>bash &lt;(curl https:<span class="hljs-comment">//www.abs-lang.org/installer.sh)</span>
</code></pre><p>…and start scripting like it’s 2019!</p>
<p>PS: Again, many thanks to <a target="_blank" href="https://github.com/ntwrick">Erich</a>, who’s been taking a larger role as the weeks went by. Without him, many of the stuff included in 1.2 wouldn’t be possible!</p>
<p>PPS: <a target="_blank" href="https://github.com/abs-lang/abs/milestone/10">1.3.0 is already well underway</a> — expect it at some point in April. We’ll be introducing extremely interesting features such as the ability to kill background commands, so it’s going to be an exciting release!</p>
<p><em>Originally published at <a target="_blank" href="https://odino.org/abs-1-dot-2-background-commands-and-the-ability-to-import-files/">odino.org</a> (21st March 2019).</em><br>_You can follow me on <a target="_blank" href="https://twitter.com/_odino_">Twitter</a> — rants are welcome!_ ?</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
