<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Bash on Micha Kops&#39; Tech Notes</title>
    <link>https://www.hascode.com/tags/bash/</link>
    <description>Recent content in Bash on Micha Kops&#39; Tech Notes</description>
    <generator>Hugo -- 0.147.8</generator>
    <language>en</language>
    <copyright>Copyright © 2010 - 2025 Micha Kops. #213243b1d6e8932079e09227d3f3ed0c806cd0c9</copyright>
    <lastBuildDate>Tue, 13 Feb 2024 00:00:00 +0100</lastBuildDate>
    <atom:link href="https://www.hascode.com/tags/bash/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Shell Script Testing with Bats</title>
      <link>https://www.hascode.com/shell-script-testing-with-bats/</link>
      <pubDate>Tue, 13 Feb 2024 00:00:00 +0100</pubDate>
      <guid>https://www.hascode.com/shell-script-testing-with-bats/</guid>
      <description>&lt;div id=&#34;preamble&#34;&gt;
&lt;div class=&#34;sectionbody&#34;&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;We’ve all been there: a small shell script that starts as a quick helper suddenly becomes a critical part of your workflow.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;Then one day, it breaks - maybe because of a missing argument, a typo, or an unexpected input.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;Debugging at 2 AM isn’t fun.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;That’s why adding tests to your shell scripts is a lifesaver. With Bats (Bash Automated Testing System), you can catch these issues early and make your scripts as reliable as any other piece of software.&lt;/p&gt;
&lt;/div&gt;</description>
    </item>
    <item>
      <title>Single Class Java HTTP Client with Proxy and SSL/TLS Keystore Settings</title>
      <link>https://www.hascode.com/single-class-java-http-client-with-proxy-and-ssl/tls-keystore-settings/</link>
      <pubDate>Tue, 02 Aug 2022 00:00:00 +0200</pubDate>
      <guid>https://www.hascode.com/single-class-java-http-client-with-proxy-and-ssl/tls-keystore-settings/</guid>
      <description>&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;Sometimes this is useful for the diagnosis of configuration and network problems of ones Java application.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;This is our single-class HTTP client example without the need for external dependencies:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;listingblock&#34;&gt;
&lt;div class=&#34;title&#34;&gt;HttpTest.java&lt;/div&gt;
&lt;div class=&#34;content&#34;&gt;
&lt;pre class=&#34;highlight&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.time.Duration;
import java.time.temporal.ChronoUnit;

public class HttpTest {
    public static void main(String[] args)
        throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException, IOException, CertificateException {
        String bodyPayload = &amp;#34;&amp;#34;&amp;#34;
            ourpayload, json, xml, ...
            &amp;#34;&amp;#34;&amp;#34;;

        String keyStorePath = &amp;#34;/opt/keystore.jks&amp;#34;;
        String keyStorePassword = &amp;#34;ABCDEFG&amp;#34;;

        String proxyHost = &amp;#34;ourproxy.proxy&amp;#34;;
        String uriString = &amp;#34;https://some-service/api&amp;#34;;
        int proxyPort = 8080;
        int timeoutInSeconds = 60;

        KeyStore keyStore = KeyStore.getInstance(&amp;#34;JKS&amp;#34;);
        keyStore.load(new FileInputStream(keyStorePath), keyStorePassword.toCharArray());

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(&amp;#34;PKIX&amp;#34;);
        keyManagerFactory.init(keyStore, null);

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(&amp;#34;PKIX&amp;#34;); // using same keystore for both
        trustManagerFactory.init(keyStore);

        SSLContext sslContext = SSLContext.getInstance(&amp;#34;TLS&amp;#34;);
        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

        HttpClient client = HttpClient.newBuilder()
                                      .proxy(
                                          ProxySelector.of(new InetSocketAddress(proxyHost, proxyPort)))
                                      .sslContext(sslContext)
                                      .build();
        URI uri = new URI(uriString);
        HttpRequest request = HttpRequest.newBuilder(uri)
                                         .POST(BodyPublishers.ofString(bodyPayload))
                                         .timeout(Duration.of(timeoutInSeconds, ChronoUnit.SECONDS))
                                         .build();
        System.out.printf(&amp;#34;Sending POST request to %s, timeout: %ds%n&amp;#34;, uri, timeoutInSeconds);
        try {
            HttpResponse response = client.send(request, BodyHandlers.ofString());
            System.out.println(&amp;#34;Response received...&amp;#34;);
            System.out.printf(&amp;#34;\tResponse-Status: %d%n&amp;#34;, response.statusCode());
            System.out.printf(&amp;#34;\tResponse-Body: %s%n&amp;#34;, response.body());
            System.out.println(&amp;#34;-------------------------------------%n&amp;#34;);

        } catch (IOException e) {
            System.err.printf(&amp;#34;IOException caught: %s%n&amp;#34;, e.getMessage());
            e.printStackTrace(System.err);
        } catch (InterruptedException e) {
            System.err.printf(&amp;#34;IOException caught: %s%n&amp;#34;, e.getMessage());
            e.printStackTrace(System.err);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</description>
    </item>
    <item>
      <title>Creating Pre-Commit-Hooks in Git and Mercurial: Prefix Commit Messages for Feature/Story Branches</title>
      <link>https://www.hascode.com/creating-pre-commit-hooks-in-git-and-mercurial-prefix-commit-messages-for-feature/story-branches/</link>
      <pubDate>Sun, 16 Dec 2012 00:00:00 +0100</pubDate>
      <guid>https://www.hascode.com/creating-pre-commit-hooks-in-git-and-mercurial-prefix-commit-messages-for-feature/story-branches/</guid>
      <description>&lt;div id=&#34;preamble&#34;&gt;
&lt;div class=&#34;sectionbody&#34;&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;Managing my projects’ source code I am using Git also as Mercurial. Therefore I often encounter the situation where I am creating a special branch to implement a specific user story or feature request.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;Now when working on such a story branch I often enter the issue-key or a short title as a prefix for each commit message. Doing this by manually is a waste of time and error-prone and luckily for us, each of both DVCS offers us an easy API to add custom hooks to the different life-cycle events.&lt;/p&gt;
&lt;/div&gt;</description>
    </item>
    <item>
      <title>Docker Snippets</title>
      <link>https://www.hascode.com/docker-snippets/</link>
      <pubDate>Mon, 01 Mar 2010 00:00:00 +0100</pubDate>
      <guid>https://www.hascode.com/docker-snippets/</guid>
      <description>&lt;div class=&#34;sect1&#34;&gt;
&lt;h2 id=&#34;_restrict_network&#34;&gt;Restrict Network&lt;/h2&gt;
&lt;div class=&#34;sectionbody&#34;&gt;
&lt;div class=&#34;admonitionblock tip&#34;&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class=&#34;icon&#34;&gt;
&lt;i class=&#34;fa icon-tip&#34; title=&#34;Tip&#34;&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class=&#34;content&#34;&gt;
Can be useful when using a third-party image that we do not trust
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class=&#34;sect2&#34;&gt;
&lt;h3 id=&#34;_run_with_no_network&#34;&gt;Run with no network&lt;/h3&gt;
&lt;div class=&#34;listingblock&#34;&gt;
&lt;div class=&#34;content&#34;&gt;
&lt;pre class=&#34;highlight&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;docker run --network none &amp;lt;image&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&#34;sect2&#34;&gt;
&lt;h3 id=&#34;_run_with_private_isolated_network&#34;&gt;Run with private isolated network&lt;/h3&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;At least containers attached to this network can talk with another&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;listingblock&#34;&gt;
&lt;div class=&#34;content&#34;&gt;
&lt;pre class=&#34;highlight&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;docker network create --internal my_isolated_network
docker run --network my_isolated_network &amp;lt;image&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&#34;sect2&#34;&gt;
&lt;h3 id=&#34;_block_using_firewall&#34;&gt;Block using firewall&lt;/h3&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;e.g. using &lt;code&gt;iptables&lt;/code&gt; or &lt;code&gt;ipfw&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;listingblock&#34;&gt;
&lt;div class=&#34;content&#34;&gt;
&lt;pre class=&#34;highlight&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;# Get container&amp;#39;s IP
docker inspect -f &amp;#39;{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}&amp;#39; &amp;lt;container_name&amp;gt;

# Block all outbound connections from that IP
sudo iptables -I DOCKER-USER -s &amp;lt;container_ip&amp;gt; -j DROP&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</description>
    </item>
    <item>
      <title>Git Snippets</title>
      <link>https://www.hascode.com/git-snippets/</link>
      <pubDate>Mon, 01 Mar 2010 00:00:00 +0100</pubDate>
      <guid>https://www.hascode.com/git-snippets/</guid>
      <description>&lt;div class=&#34;sect1&#34;&gt;
&lt;h2 id=&#34;_show_commits_from_another_branch_not_contained_in_current_branch&#34;&gt;Show commits from another branch not contained in current branch&lt;/h2&gt;
&lt;div class=&#34;sectionbody&#34;&gt;
&lt;div class=&#34;listingblock&#34;&gt;
&lt;div class=&#34;content&#34;&gt;
&lt;pre class=&#34;highlight&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;git cherry -v otherbranch
+ f7d6a569bb6912aac97fce9ac92c4302863fb0d9 thecommit&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&#34;sect1&#34;&gt;
&lt;h2 id=&#34;_create_empty_commit&#34;&gt;Create empty commit&lt;/h2&gt;
&lt;div class=&#34;sectionbody&#34;&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;sometimes necessary for build/deploy pipelines …​&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;listingblock&#34;&gt;
&lt;div class=&#34;content&#34;&gt;
&lt;pre class=&#34;highlight&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;git commit --allow-empty -m &amp;#34;Empty-Commit&amp;#34;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&#34;sect1&#34;&gt;
&lt;h2 id=&#34;_cherry_pick_without_commit&#34;&gt;Cherry pick without commit&lt;/h2&gt;
&lt;div class=&#34;sectionbody&#34;&gt;
&lt;div class=&#34;listingblock&#34;&gt;
&lt;div class=&#34;content&#34;&gt;
&lt;pre class=&#34;highlight&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;git cherry-pick -n HASH&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&#34;sect1&#34;&gt;
&lt;h2 id=&#34;_using_vimdiff_for_diff&#34;&gt;Using vimdiff for diff&lt;/h2&gt;
&lt;div class=&#34;sectionbody&#34;&gt;
&lt;div class=&#34;listingblock&#34;&gt;
&lt;div class=&#34;title&#34;&gt;set it via git config&lt;/div&gt;
&lt;div class=&#34;content&#34;&gt;
&lt;pre class=&#34;highlight&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;git config --global diff.tool vimdiff
git config --global merge.tool vimdiff&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&#34;listingblock&#34;&gt;
&lt;div class=&#34;title&#34;&gt;set it via ~/.gitconfig&lt;/div&gt;
&lt;div class=&#34;content&#34;&gt;
&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;[diff]
    tool = vimdiff
[merge]
    tool = vimdiff&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&#34;sect1&#34;&gt;
&lt;h2 id=&#34;_using_vscode_as_diff_and_mergetool&#34;&gt;Using vscode as diff and mergetool&lt;/h2&gt;
&lt;div class=&#34;sectionbody&#34;&gt;
&lt;div class=&#34;admonitionblock tip&#34;&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class=&#34;icon&#34;&gt;
&lt;i class=&#34;fa icon-tip&#34; title=&#34;Tip&#34;&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class=&#34;content&#34;&gt;
You need to have the shell integration installed (code binary in PATH)
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;</description>
    </item>
    <item>
      <title>Linux Snippets</title>
      <link>https://www.hascode.com/linux-snippets/</link>
      <pubDate>Mon, 01 Mar 2010 00:00:00 +0100</pubDate>
      <guid>https://www.hascode.com/linux-snippets/</guid>
      <description>&lt;div id=&#34;preamble&#34;&gt;
&lt;div class=&#34;sectionbody&#34;&gt;
&lt;div class=&#34;admonitionblock tip&#34;&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class=&#34;icon&#34;&gt;
&lt;i class=&#34;fa icon-tip&#34; title=&#34;Tip&#34;&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class=&#34;content&#34;&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;These are not only linux snippets but also bash snippets and snippets using tools that run under Linux, *nix or sometimes even MacOSX, I should reorder this article someday ;)&lt;/p&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&#34;sect1&#34;&gt;
&lt;h2 id=&#34;_settings_for_more_reliable_bash_scripts&#34;&gt;Settings for more reliable bash scripts&lt;/h2&gt;
&lt;div class=&#34;sectionbody&#34;&gt;
&lt;div class=&#34;listingblock&#34;&gt;
&lt;div class=&#34;content&#34;&gt;
&lt;pre class=&#34;highlight&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;set -euo pipefail&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;this gives us …​&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;ulist&#34;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;-e: exit script if a single command fails&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-u: exit script if an unset variable is used&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-o pipefail: return value of a pipeline is the status of the last command to exit with a non-zero status, or zero if no command exited with a non-zero status&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;</description>
    </item>
    <item>
      <title>XML Snippets</title>
      <link>https://www.hascode.com/xml-snippets/</link>
      <pubDate>Mon, 01 Mar 2010 00:00:00 +0100</pubDate>
      <guid>https://www.hascode.com/xml-snippets/</guid>
      <description>&lt;div class=&#34;sect1&#34;&gt;
&lt;h2 id=&#34;_ignore_namespaces_in_xpath_query&#34;&gt;Ignore Namespaces in XPath Query&lt;/h2&gt;
&lt;div class=&#34;sectionbody&#34;&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;e.g. Query for all &lt;code&gt;xxx&lt;/code&gt; nodes ignoring their namespace:&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;listingblock&#34;&gt;
&lt;div class=&#34;content&#34;&gt;
&lt;pre class=&#34;highlight&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;xmllint --xpath &amp;#39;//*[local-name()=&amp;#34;xxx&amp;#34;]&amp;#39; input.xml&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&#34;admonitionblock tip&#34;&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class=&#34;icon&#34;&gt;
&lt;i class=&#34;fa icon-tip&#34; title=&#34;Tip&#34;&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class=&#34;content&#34;&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;An example parsing URLs from a sitemap XML.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;The URLs are located in //url/loc where all nodes are bound to the namespace &lt;code&gt;&lt;a href=&#34;http://www.sitemaps.org/schemas/sitemap/0.9&#34; class=&#34;bare&#34;&gt;http://www.sitemaps.org/schemas/sitemap/0.9&lt;/a&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;paragraph&#34;&gt;
&lt;p&gt;The following query ignores the namespace&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;listingblock&#34;&gt;
&lt;div class=&#34;content&#34;&gt;
&lt;pre class=&#34;highlight&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;xmllint --xpath &amp;#39;//*[local-name()=&amp;#34;url&amp;#34;]/*[local-name()=&amp;#34;loc&amp;#34;]&amp;#39; sitemap.xml&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&#34;sect1&#34;&gt;
&lt;h2 id=&#34;_pretty_print_xml_in_the_console_using_xmllint&#34;&gt;Pretty Print XML in the Console using xmllint&lt;/h2&gt;
&lt;div class=&#34;sectionbody&#34;&gt;
&lt;div class=&#34;listingblock&#34;&gt;
&lt;div class=&#34;content&#34;&gt;
&lt;pre class=&#34;highlight&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;echo &amp;#39;&amp;lt;blogs&amp;gt;&amp;lt;blog url=&amp;#34;https://www.hascode.com/&amp;#34;&amp;gt;hasCode.com&amp;lt;/blog&amp;gt;&amp;lt;/blogs&amp;gt;&amp;#39; | xmllint --format -
&amp;lt;?xml version=&amp;#34;1.0&amp;#34;?&amp;gt;
&amp;lt;blogs&amp;gt;
    &amp;lt;blog url=&amp;#34;https://www.hascode.com/&amp;#34;&amp;gt;hasCode.com&amp;lt;/blog&amp;gt;
&amp;lt;/blogs&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</description>
    </item>
  </channel>
</rss>
