<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>unix on
A Scripter's Notes</title><link>https://scripter.co/categories/unix/</link><description>Recent content in unix
on A Scripter's Notes</description><language>en-us</language><managingEditor>kaushal.modi@gmail.com (Kaushal Modi)</managingEditor><webMaster>kaushal.modi@gmail.com (Kaushal Modi)</webMaster><lastBuildDate>Wed, 22 Apr 2026 08:24:58 -0400</lastBuildDate><generator>Hugo -- gohugo.io</generator><docs>https://validator.w3.org/feed/docs/rss2.html</docs><atom:link href="https://scripter.co/categories/unix/index.xml" rel="self" type="application/rss+xml"/><item><title>Version controlling Jenkins config</title><link>https://scripter.co/version-controlling-jenkins-config/</link><description>&lt;blockquote>Jenkins is an amazing free and open source continuous integration and
deployment software. But its primary means of configuration is a web
UI, and recently that cost me a lot of debug time. That set me down
the path of figuring out a way to version control the Jenkins config
(the &lt;code>$JENKINS_HOME&lt;/code> directory).&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#what-bit-me">What bit me&lt;/a>&lt;/li>
&lt;li>&lt;a href="#dot-gitignore-for-jenkins-config">&lt;code>.gitignore&lt;/code> for Jenkins config&lt;/a>&lt;/li>
&lt;li>&lt;a href="#jenkins-plugin-manager">Jenkins Plugin Manager&lt;/a>&lt;/li>
&lt;li>&lt;a href="#full-solution">Full solution&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>&lt;a href="https://www.jenkins.io/">Jenkins&lt;/a> is a wonderful piece of software and I use it with my
Bitbucket git repos for &lt;a href="https://en.wikipedia.org/wiki/CI/CD">CI/CD&lt;/a>.&lt;/p>
&lt;p>Jenkins uses a web UI for its configuration. I dislike that because
it&amp;rsquo;s difficult to document the configuration process without
screenshots, and if I need to create a new server, it&amp;rsquo;s a manual
process of clicking through tabs and filling in the text boxes. I
didn&amp;rsquo;t mind this enough to do anything about it .. that is until I
finally got bit by it.&lt;/p>
&lt;h2 id="what-bit-me">What bit me&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#what-bit-me">#&lt;/a>&lt;/h2>
&lt;p>Without going into too much detail, that issue was multi-fold:&lt;/p>
&lt;ol>
&lt;li>I had unknowingly messed up the &lt;em>Project-based Matrix Authorization
Strategy&lt;/em> such that other users in my team were not able to view
the Jenkins jobs.&lt;/li>
&lt;li>I had also updated the Jenkins server that introduced a bug
(&lt;a href="https://issues.jenkins.io/browse/JENKINS-68748">JENKINS-68748&lt;/a>) where the &lt;em>Test LDAP Settings&lt;/em> failed with an
error, but the LDAP authentication actually worked!&lt;/li>
&lt;li>I had also updated all the plugins after updating Jenkins. So if I
rolled back the Jenkins versions, most of the plugins would fail
because of incompatibility with the older Jenkins version. I had
updated Jenkins after months!&lt;/li>
&lt;/ol>
&lt;p>That&amp;rsquo;s when I wished that my whole Jenkins was
version-controlled. That would have allowed me to roll back to the
last working &amp;ldquo;Jenkins image&amp;rdquo; with the Jenkin version, plugins'
versions and my Jenkins config all in sync.&lt;/p>
&lt;p>I had delayed doing this because my &lt;code>$JENKINS_HOME&lt;/code> was more than 1GB
in size and I didn&amp;rsquo;t have time or motivation to figure out what stuff
I should commit and what I should ignore .. But no more &amp;mdash; The time
had finally come.&lt;/p>
&lt;h2 id="dot-gitignore-for-jenkins-config">&lt;code>.gitignore&lt;/code> for Jenkins config&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#dot-gitignore-for-jenkins-config">#&lt;/a>&lt;/h2>
&lt;p>So I did what any good engineer would do .. start looking for a
solution online. I found &lt;a href="https://stackoverflow.com/a/4695615">this StackOverflow answer&lt;/a> for &lt;em>Is there a way
to keep Hudson / Jenkins configuration files in source control?&lt;/em>.&lt;/p>
&lt;p>That answer shares a &lt;code>.gitignore&lt;/code> that ignores files not necessary for
configuring a Jenkins server &amp;mdash; Example: job builds, workspace, log
files, etc. But it didn&amp;rsquo;t work out of the box because the plugin
version info wasn&amp;rsquo;t getting committed correctly. I had committed
everything to git after using the suggested &lt;code>.gitignore&lt;/code> and pushed to
my git remote. But if I cloned that repo to a different area and
attempted to start the Jenkins server from there, it crashed with this
message:&lt;/p>
&lt;p>&lt;a id="code-snippet--jenkins-crash-log">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">2022-07-15 13:07:06.082+0000 [id=31] SEVERE jenkins.InitReactorRunner$1#onTaskFailed: Failed Loading global config
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">com.thoughtworks.xstream.mapper.CannotResolveClassException: hudson.security.ProjectMatrixAuthorizationStrategy
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.DefaultMapper.realClass(DefaultMapper.java:81)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.DynamicProxyMapper.realClass(DynamicProxyMapper.java:55)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.PackageAliasingMapper.realClass(PackageAliasingMapper.java:88)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.ClassAliasingMapper.realClass(ClassAliasingMapper.java:79)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.ArrayMapper.realClass(ArrayMapper.java:74)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.SecurityMapper.realClass(SecurityMapper.java:71)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at hudson.util.XStream2$CompatibilityMapper.realClass(XStream2.java:411)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at hudson.util.xstream.MapperDelegate.realClass(MapperDelegate.java:46)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.CachingMapper.realClass(CachingMapper.java:47)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at hudson.util.RobustReflectionConverter.determineType(RobustReflectionConverter.java:521)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at hudson.util.RobustReflectionConverter.doUnmarshal(RobustReflectionConverter.java:346)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Caused: jenkins.util.xstream.CriticalXStreamException:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">---- Debugging information ----
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cause-exception : com.thoughtworks.xstream.mapper.CannotResolveClassException
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cause-message : hudson.security.ProjectMatrixAuthorizationStrategy
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">class : hudson.model.Hudson
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">required-type : hudson.model.Hudson
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">converter-type : hudson.util.RobustReflectionConverter
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">path : /hudson/authorizationStrategy
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">line number : 12
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">version : not available
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------------------------
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--jenkins-crash-log">Code Snippet 1&lt;/a>:&lt;/span>
Snippet of Jenkins crash when attempting to run the server from the freshly cloned git repo
&lt;/div>
&lt;h2 id="jenkins-plugin-manager">Jenkins Plugin Manager&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#jenkins-plugin-manager">#&lt;/a>&lt;/h2>
&lt;p>So I &lt;a href="https://community.jenkins.io/t/version-controlling-jenkins-config-help-defining-a-gitignore-that-minimizes-the-git-repo-size/3036">reached out for help&lt;/a> on the Jenkins Community. One of the key
contributors to Jenkins, &lt;a href="https://community.jenkins.io/u/MarkEWaite">Mark Waite&lt;/a>, was tremendously helpful. He
suggested using his &lt;a href="https://github.com/jenkinsci/plugin-installation-manager-tool">&lt;code>jenkins-plugin-manager&lt;/code>&lt;/a> tool. After trying it out
for a bit, I realized that this tool had everything I needed for
version controlling the plugin versions:&lt;/p>
&lt;ul>
&lt;li>Ability to save a list of installed Jenkins plugins and their
versions to a file.&lt;/li>
&lt;li>Ability to batch install all the plugins of the versions listed in a
file.&lt;/li>
&lt;/ul>
&lt;p>This was like doing Python&amp;rsquo;s &lt;a href="https://scripter.co/saving-python-pip-dependencies/">plugin management using
&lt;code>requirements.txt&lt;/code>&lt;/a>, except that this was for Jenkins.&lt;/p>
&lt;h2 id="full-solution">Full solution&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#full-solution">#&lt;/a>&lt;/h2>
&lt;p>With a combination of the &lt;code>.gitignore&lt;/code> that I started with from that
SO answer, managing plugins using &lt;code>jenkins-plugin-manager&lt;/code>, tweaking
the &lt;code>.gitignore&lt;/code> to my liking, and adding helper Bash scripts for
downloading and running Jenkins server binaries, and doing the plugin
management, I finally got what I needed:&lt;/p>
&lt;div class="org-center">
&lt;p>&lt;a href="https://github.com/kaushalmodi/jenkins-minimal">https://github.com/kaushalmodi/jenkins-minimal&lt;/a>&lt;/p>
&lt;/div>
&lt;p>The README on the repo has all the instructions.&lt;/p></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/unix">unix</category><category domain="https://scripter.co/tags/git">git</category><category domain="https://scripter.co/tags/jenkins">jenkins</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><guid>https://scripter.co/version-controlling-jenkins-config/</guid><pubDate>Wed, 20 Jul 2022 00:18:00 -0400</pubDate></item><item><title>Disarming the 'tar' bomb in 10 seconds</title><link>https://scripter.co/disarming-the-tar-bomb-in-10-seconds/</link><description>&lt;blockquote>Use &lt;code>tar -caf &amp;lt;file&amp;gt; &amp;lt;dir&amp;gt;&lt;/code> to create an archive, &lt;code>tar -xf &amp;lt;file&amp;gt;&lt;/code> to
extract one, and more.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#mnemonics">Mnemonics&lt;/a>&lt;/li>
&lt;li>&lt;a href="#creating-an-archive">Creating an archive&lt;/a>&lt;/li>
&lt;li>&lt;a href="#extracting-an-archive">Extracting an archive&lt;/a>&lt;/li>
&lt;li>&lt;a href="#listing-contents-of-an-archive">Listing contents of an archive&lt;/a>&lt;/li>
&lt;li>&lt;a href="#summary">Summary&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>I had come across &lt;a href="https://fosstodon.org/@garritfra/108409516362853350">this post by user &lt;em>Garrit&lt;/em>&lt;/a> on Mastodon, and that
inspired this post.&lt;/p>
&lt;div class="mf2 reply">In reply to: &lt;p>&lt;a class="u-in-reply-to h-cite" rel="in-reply-to" href="https://garrit.xyz/posts/2022-06-02-tar-commands">https://garrit.xyz/posts/2022-06-02-tar-commands&lt;/a>&lt;/p>&lt;/div>
&lt;p>A post on the Unix command &lt;a href="https://www.gnu.org/software/tar/manual/html_node/index.html" title="Emacs Lisp: (info &amp;quot;(tar) Top&amp;quot;)">&lt;code>tar&lt;/code>&lt;/a> cannot leave out the obligatory &lt;em>xkcd
&lt;strong>tar&lt;/strong>&lt;/em> comic 😄, so here it is:&lt;/p>
&lt;p>&lt;a id="figure--xkcd-1168">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/disarming-the-tar-bomb-in-10-seconds/xkcd-1168.png" title="I don't know what's worse--the fact that after 15 years of using tar I still can't keep the flags straight, or that after 15 years of technological advancement I'm still mucking with tar flags that were 15 years old when I started."/> &lt;figcaption>
&lt;p>
&lt;a href="https://xkcd.com/1168/">
xkcd.com
&lt;/a>&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;h2 id="mnemonics">Mnemonics&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#mnemonics">#&lt;/a>&lt;/h2>
&lt;p>These few mnemonics help me remember the basic and my most frequently
used &lt;code>tar&lt;/code> options:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>c&lt;/strong> to (c)reate&lt;/li>
&lt;li>&lt;strong>a&lt;/strong> to (a)uto compress the archive based on the file name&lt;/li>
&lt;li>&lt;strong>x&lt;/strong> to e(x)tract&lt;/li>
&lt;li>&lt;strong>f&lt;/strong> for the archive (f)ile name we are dealing with (whether
creating, listing or extracting an archive)&lt;/li>
&lt;/ul>
&lt;p>.. and a few not so frequent options (for me):&lt;/p>
&lt;ul>
&lt;li>&lt;strong>t&lt;/strong> to lis(t) contents of an archive&lt;/li>
&lt;li>&lt;strong>v&lt;/strong> for (v)erbose output&lt;/li>
&lt;/ul>
&lt;h2 id="creating-an-archive">Creating an archive&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#creating-an-archive">#&lt;/a>&lt;/h2>
&lt;div class="org-center">
&lt;p>&lt;strong>tar -caf &amp;lt;file&amp;gt; &amp;lt;dir&amp;gt;&lt;/strong>&lt;/p>
&lt;/div>
&lt;p>A keen user might have noticed that I am using &lt;code>tar -caf ..&lt;/code> instead
of &lt;code>tar caf ..&lt;/code> i.e. I am using a hyphen before the &lt;code>tar&lt;/code>
options. Both approaches work and they look similar, but the approach
with the hyphen is the &lt;strong>newer&lt;/strong> &lt;a href="https://www.gnu.org/software/tar/manual/html_node/Short-Options.html" title="Emacs Lisp: (info &amp;quot;(tar) Short Options&amp;quot;)">Short Option style&lt;/a> while the other is
the &lt;a href="https://www.gnu.org/software/tar/manual/html_node/Old-Options.html" title="Emacs Lisp: (info &amp;quot;(tar) Old Options&amp;quot;)">Old Option style&lt;/a>.&lt;/p>
&lt;div class="note">
&lt;p>I prefer the &lt;em>short option style&lt;/em> because .. well.. the other style is
old.. and also because the &lt;em>short option style&lt;/em> is &lt;strong>stricter&lt;/strong> e.g. the
&lt;code>-f&lt;/code> switch has to be followed by the file name.&lt;/p>
&lt;/div>
&lt;p>Whether you are creating a regular &lt;strong>.tar&lt;/strong> archive, or a compressed
archive like &lt;strong>.tar.gz&lt;/strong>, always use the auto-compresion switch
&lt;strong>-a&lt;/strong>. That relieves you from deciding &lt;em>if&lt;/em> you need that switch, or
which compression algorithm switch should be used 😄.&lt;/p>
&lt;p>The &lt;strong>-a&lt;/strong> switch makes the decision for you based on the file
extension. For example, &lt;code>tar -caf foo.tar foo/&lt;/code> will create a regular
archive, while &lt;code>tar -caf foo.tar.gz foo/&lt;/code> will create a compressed
archive using &lt;code>gzip&lt;/code>. You can read more about it in &lt;a href="https://www.gnu.org/software/tar/manual/html_node/gzip.html" title="Emacs Lisp: (info &amp;quot;(tar) gzip&amp;quot;)">Creating and
Reading Compressed Archives&lt;/a>.&lt;/p>
&lt;h2 id="extracting-an-archive">Extracting an archive&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#extracting-an-archive">#&lt;/a>&lt;/h2>
&lt;div class="org-center">
&lt;p>&lt;strong>tar -xf &amp;lt;file&amp;gt;&lt;/strong>&lt;/p>
&lt;/div>
&lt;p>At times, it might be useful to add the verbosity switch &lt;strong>-v&lt;/strong> to this
command and do &lt;code>tar -xvf &amp;lt;file&amp;gt;&lt;/code>.&lt;/p>
&lt;div class="note">
&lt;p>Just to reiterate, the &lt;strong>-f&lt;/strong> switch must be followed by the archive
file name.&lt;/p>
&lt;/div>
&lt;h2 id="listing-contents-of-an-archive">Listing contents of an archive&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#listing-contents-of-an-archive">#&lt;/a>&lt;/h2>
&lt;div class="org-center">
&lt;p>&lt;strong>tar -tf &amp;lt;file&amp;gt;&lt;/strong>&lt;/p>
&lt;/div>
&lt;p>Once you are done creating an archive, you might feel the need to
check if the archive contains everything you expect. Or you might want
to check what&amp;rsquo;s inside the archive before you extract it.&lt;/p>
&lt;p>This command is often paired with &lt;code>grep&lt;/code> or &lt;a href="https://github.com/BurntSushi/ripgrep">&lt;code>rg&lt;/code> (ripgrep)&lt;/a> like so:
&lt;code>tar -tf foo.tar.xz | rg 'some_file_name_in_archive'&lt;/code>.&lt;/p>
&lt;h2 id="summary">Summary&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#summary">#&lt;/a>&lt;/h2>
&lt;p>If you simply glossed over the whole article, or didn&amp;rsquo;t read through
the all linked manual pages
&lt;span class="sidenote-number">&lt;small class="sidenote">
I know you didn&amp;rsquo;t 😉
&lt;/small>&lt;/span>
, just remember this &amp;mdash;&lt;/p>
&lt;div class="org-center">
&lt;p>&lt;strong>tar -caf&lt;/strong> to &lt;strong>c&lt;/strong>​reate and &lt;strong>tar -xf&lt;/strong> to e​&lt;strong>x&lt;/strong>​tract&lt;/p>
&lt;/div></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/unix">unix</category><category domain="https://scripter.co/categories/replies">replies</category><category domain="https://scripter.co/tags/tar">tar</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><guid>https://scripter.co/disarming-the-tar-bomb-in-10-seconds/</guid><pubDate>Mon, 13 Jun 2022 01:14:00 -0400</pubDate></item><item><title>grep -Po</title><link>https://scripter.co/grep-po/</link><description>&lt;blockquote>Using &lt;code>grep&lt;/code> to do substring extraction in shell scripts.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#grep-po-problem-statement">Problem statement&lt;/a>&lt;/li>
&lt;li>&lt;a href="#solution-using-grep-po">Solution using &lt;code>grep -Po&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#arriving-to-this-solution">Arriving to this solution&lt;/a>&lt;/li>
&lt;li>&lt;a href="#summary">Summary&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>I like &lt;a href="https://en.wikipedia.org/wiki/Regular_expression">regular expressions&lt;/a>
&lt;span class="sidenote-number">&lt;small class="sidenote">
I recommend using &lt;a href="https://regex101.com/">https://regex101.com/&lt;/a> to practice regular
expressions of different flavors (PCRE2, PCRE, Python, etc.) whether
or not you are new to using &lt;abbr aria-label=" regular expression" tabindex=0>regex&lt;/abbr>.
&lt;/small>&lt;/span>
as they allow me to be concise and specific about what I need to
search.&lt;/p>
&lt;p>And I have liked using regular expressions for many years, ever since
I learned Perl about fifteen years back. I am writing this post as I
am remembering the delight I felt when I realized that I can use the
familiar Perl regular expressions to do string parsing in shell
scripts. I am not exactly sure, but I probably learned about this
&lt;code>grep -Po&lt;/code> trick from &lt;em>stackexchange&lt;/em> (&lt;a href="#citeproc_bib_item_1">camh, 2011&lt;/a>).&lt;/p>
&lt;h2 id="grep-po-problem-statement">Problem statement&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#grep-po-problem-statement">#&lt;/a>&lt;/h2>
&lt;p>I could be parsing a log file with a line like &lt;code>web report: https://foo.bar/detail.html&lt;/code> and I need to extract the
&lt;code>https://foo.bar&lt;/code> part to a shell script variable.&lt;/p>
&lt;h2 id="solution-using-grep-po">Solution using &lt;code>grep -Po&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#solution-using-grep-po">#&lt;/a>&lt;/h2>
&lt;div class="note">
&lt;p>This solution requires a GNU &lt;code>grep&lt;/code> version supporting &lt;code>-P&lt;/code>, that&amp;rsquo;s
compiled with &lt;code>libpcre&lt;/code>.
&lt;span class="sidenote-number">&lt;small class="sidenote">
&lt;em>GNU grep&lt;/em> gained the PCRE (&lt;code>-P&lt;/code>) feature back &lt;a href="https://git.savannah.gnu.org/cgit/grep.git/commit/?id=05860b2d966701a5a9f70a650d32b30ae2612eeb">in 2000&lt;/a>.
&lt;/small>&lt;/span>
Also I have never come across a system or
used one that did not have such a &lt;code>grep&lt;/code> version installed.&lt;/p>
&lt;/div>
&lt;p>I&amp;rsquo;ll throw the solution out here and then dig into the details.&lt;/p>
&lt;p>&lt;a id="code-snippet--grepPo-example">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;def\nabc&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> grep -Po &lt;span class="s1">&amp;#39;a\K.(?=c)&amp;#39;&lt;/span> &lt;span class="c1"># =&amp;gt; b&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--grepPo-example">Code Snippet 1&lt;/a>:&lt;/span>
Extracting "b" from "abc" using &lt;code>grep -Po&lt;/code>
&lt;/div>
&lt;p>The &lt;em>grep&lt;/em> switches used here are:&lt;/p>
&lt;dl>
&lt;dt>&lt;code>-P&lt;/code>&lt;/dt>
&lt;dd>Use (P)erl regular expressions. This allows us to use the
&lt;a href="https://www.regular-expressions.info/lookaround.html">&lt;em>look around&lt;/em> regex&lt;/a> syntax like &lt;code>(?=..)&lt;/code> and special characters like
&lt;code>\K&lt;/code> (&lt;a href="#citeproc_bib_item_2">“perlre - Perl regular expressions,” n.d.&lt;/a>).&lt;/dd>
&lt;dt>&lt;code>-o&lt;/code>&lt;/dt>
&lt;dd>Print only the matched portion to the (o)utput&lt;/dd>
&lt;/dl>
&lt;h2 id="arriving-to-this-solution">Arriving to this solution&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#arriving-to-this-solution">#&lt;/a>&lt;/h2>
&lt;p>Now I&amp;rsquo;ll start with a basic example and build up to the &lt;a href="#code-snippet--grepPo-example">above
solution&lt;/a>.&lt;/p>
&lt;dl>
&lt;dt>Problem&lt;/dt>
&lt;dd>Let&amp;rsquo;s say I have this text with two lines &amp;ldquo;def&amp;rdquo; and &amp;ldquo;abc&amp;rdquo;
and I want&lt;span class="org-target" id="org-target--wanted-grep-output">&lt;/span> to output whatever character is between &amp;ldquo;a&amp;rdquo; and &amp;ldquo;c&amp;rdquo;.&lt;/dd>
&lt;/dl>
&lt;!--listend-->
&lt;ul>
&lt;li>
&lt;p>Below, the regular expression for matching any character between &amp;ldquo;a&amp;rdquo;
and &amp;ldquo;c&amp;rdquo; ( &lt;code>'a.c'&lt;/code> ) is correct, but that will output the whole input
because the &lt;em>grep&lt;/em> of that regex succeeded.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;def\nabc&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> grep &lt;span class="s1">&amp;#39;a.c&amp;#39;&lt;/span> &lt;span class="c1"># =&amp;gt; def\nabc&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Now we add the &lt;em>grep&lt;/em> &lt;code>-o&lt;/code> switch so that it outputs only the
matched portion. As the regex is &lt;code>'a.c'&lt;/code>​, the &lt;code>-o&lt;/code> switch will
output every part of the input that matched that. So the output is
&amp;ldquo;abc&amp;rdquo;. It&amp;rsquo;s still not what we &lt;a href="#org-target--wanted-grep-output">wanted&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;def\nabc&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> grep -o &lt;span class="s1">&amp;#39;a.c&amp;#39;&lt;/span> &lt;span class="c1"># =&amp;gt; abc&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Now we bring in the powerful Perl regex feature &lt;em>positive
lookahead&lt;/em>.
&lt;span class="sidenote-number">&lt;small class="sidenote">
Positive lookahead is used when you want to match something &lt;span class="underline">only
if&lt;/span> it&amp;rsquo;s followed by something else. It&amp;rsquo;s syntax looks like &lt;code>q(?=u)&lt;/code>
where that expression matches if a &lt;code>q&lt;/code> is followed by a &lt;code>u&lt;/code>, without
making the &lt;code>u&lt;/code> part of the match &amp;ndash; &lt;a href="https://www.regular-expressions.info/lookaround.html">reference&lt;/a>.
&lt;/small>&lt;/span>
But this is still not exactly what we want because &amp;ldquo;a&amp;rdquo; is still
considered as part of the match. Now the output is &amp;ldquo;ab&amp;rdquo;.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;abc&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> grep -Po &lt;span class="s1">&amp;#39;a.(?=c)&amp;#39;&lt;/span> &lt;span class="c1"># =&amp;gt; ab&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>We only need a special character that marks a point in the regex
that tells &amp;ldquo;don&amp;rsquo;t consider anything before this as part of the
match&amp;rdquo;. The &lt;code>\K&lt;/code> special construct described in the &lt;a href="https://perldoc.perl.org/perlre#Lookaround-Assertions">Perl regular
expressions doc&lt;/a> as:&lt;/p>
&lt;blockquote>
&lt;p>There is a special form of this construct, called &lt;code>\K&lt;/code> (available
since Perl 5.10.0), which causes the regex engine to &amp;ldquo;keep&amp;rdquo;
everything it had matched prior to the &lt;code>\K&lt;/code> and not include it in
matched string. This effectively provides non-experimental
variable-length lookbehind of any length.&lt;/p>
&lt;/blockquote>
&lt;p>And, thus we have the final solution:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;abc&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> grep -Po &lt;span class="s1">&amp;#39;a\K.(?=c)&amp;#39;&lt;/span> &lt;span class="c1"># =&amp;gt; b&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h2 id="summary">Summary&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#summary">#&lt;/a>&lt;/h2>
&lt;p>Taking the example from the &lt;a href="#grep-po-problem-statement">problem statement&lt;/a>, this will work:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">string&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;web report: https://foo.bar/detail.html&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">substring&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>grep -Po &lt;span class="s1">&amp;#39;web report:\s*\K.*?(?=/detail\.html)&amp;#39;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&amp;lt;&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">string&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">substring&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">https://foo.bar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;h2 id="references">References&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#references">#&lt;/a>&lt;/h2>
&lt;div class="csl-bib-body">
&lt;div class="csl-entry">&lt;a id="citeproc_bib_item_1">&lt;/a>camh. (2011). Can grep output only specified groupings that match? [Website]. In &lt;i>Unix stackexchange&lt;/i>. &lt;a href="https://unix.stackexchange.com/a/13472/57923">https://unix.stackexchange.com/a/13472/57923&lt;/a>&lt;/div>
&lt;div class="csl-entry">&lt;a id="citeproc_bib_item_2">&lt;/a>perlre - Perl regular expressions. (n.d.). [Website]. In &lt;i>Perldoc 5.34.0&lt;/i>. Retrieved February 16, 2022, from &lt;a href="https://perldoc.perl.org/perlre">https://perldoc.perl.org/perlre&lt;/a>&lt;/div>
&lt;/div></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/unix">unix</category><category domain="https://scripter.co/categories/shell">shell</category><category domain="https://scripter.co/tags/grep">grep</category><category domain="https://scripter.co/tags/regex">regex</category><category domain="https://scripter.co/tags/string">string</category><category domain="https://scripter.co/tags/perl">perl</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><guid>https://scripter.co/grep-po/</guid><pubDate>Wed, 16 Feb 2022 21:34:00 -0500</pubDate></item><item><title>Git diff Minified JS and CSS</title><link>https://scripter.co/git-diff-minified-js-and-css/</link><description>&lt;blockquote>Make the &lt;code>git diff&lt;/code> output be more useful when diffing minified &lt;em>.js&lt;/em>
and &lt;em>.css&lt;/em> files.&lt;/blockquote>&lt;div class="ox-hugo-toc toc has-section-numbers">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;span class="section-num">1&lt;/span> &lt;a href="#install-js-beautify-using-npm">Install &lt;code>js-beautify&lt;/code> using &lt;code>npm&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;span class="section-num">2&lt;/span> &lt;a href="#configure-git-to-use-that-tool">Configure &lt;code>git&lt;/code> to use that tool&lt;/a>&lt;/li>
&lt;li>&lt;span class="section-num">3&lt;/span> &lt;a href="#add-update-dot-gitattributes-file-to-the-project-repo">Add/update &lt;code>.gitattributes&lt;/code> file to the project repo&lt;/a>&lt;/li>
&lt;li>&lt;a href="#beautiful-result">Beautiful Result&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>While working on a &lt;a href="https://github.com/gohugoio/gohugoioTheme/pull/84">little PR for the Hugo doc site theme&lt;/a>, I learned
that if I needed to make changes to JS/CSS, I had to commit my changes
in both unminified and minified versions.&lt;/p>
&lt;p>I have a habit to always look at the diffs at the time of staging and
committing. So it felt very unnatural to commit a minified JS where
the diff would show just &lt;strong>one&lt;/strong> changed line with thousands of
characters of minified+uglified JS.&lt;/p>
&lt;p>So I started looking for solutions, and found &lt;a href="https://cweiske.de/tagebuch/git-diff-minified-js.htm">this post&lt;/a> by &lt;em>Christian
Weiske&lt;/em> where he suggests using &lt;a href="https://github.com/beautify-web/js-beautify">&lt;code>js-beautify&lt;/code>&lt;/a> to &lt;em>beautify&lt;/em> minified
JS diffs.&lt;/p>
&lt;div class="verse">
&lt;p>    &lt;em>And that tool works beautifully!&lt;/em>&lt;br />&lt;/p>
&lt;/div>
&lt;ul>
&lt;li>I later found out that the same tool can also be used to &lt;em>beautify&lt;/em>
minified CSS.&lt;/li>
&lt;li>.. and I installed that tool using &lt;code>npm&lt;/code> as the &lt;code>pip3&lt;/code> approach
failed with &lt;em>&amp;ldquo;Collecting js-beautify.. Could not find a version that
satisfies the requirement js-beautify (from versions: ) No matching
distribution found for js-beautify&amp;rdquo;&lt;/em>.&lt;/li>
&lt;/ul>
&lt;p>So here&amp;rsquo;s how you can do useful &lt;code>git diff&lt;/code> for minified JS and CSS.&lt;/p>
&lt;h2 id="install-js-beautify-using-npm">&lt;span class="section-num">1&lt;/span> Install &lt;code>js-beautify&lt;/code> using &lt;code>npm&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#install-js-beautify-using-npm">#&lt;/a>&lt;/h2>
&lt;p>I see myself using &lt;code>js-beautify&lt;/code> in many other projects too. So I
installed it globally.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">npm install --global js-beautify
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;h2 id="configure-git-to-use-that-tool">&lt;span class="section-num">2&lt;/span> Configure &lt;code>git&lt;/code> to use that tool&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#configure-git-to-use-that-tool">#&lt;/a>&lt;/h2>
&lt;p>Add below to your &lt;code>~/.gitconfig&lt;/code>:&lt;/p>
&lt;ol>
&lt;li>Use &lt;code>js-beautify&lt;/code> to first beautify the minified JS for the &lt;code>minjs&lt;/code>
&lt;em>diff configuration&lt;/em>, and then do a diff of those beautified files.&lt;/li>
&lt;li>Enable caching of those beautified files to speed up the diff, so
that &lt;em>re-beautification&lt;/em> of unmodified minified files can be
skipped.&lt;/li>
&lt;li>Similarly for minified CSS, use &lt;code>js-beautify --css&lt;/code> to first
beautify the minified CSS for the &lt;code>mincss&lt;/code> &lt;em>diff configuration&lt;/em>.&lt;/li>
&lt;/ol>
&lt;!--listend-->
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="k">[diff &amp;#34;minjs&amp;#34;]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">textconv&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">js-beautify
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> cachetextconv = true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[diff &amp;#34;mincss&amp;#34;]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">textconv&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">js-beautify --css
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> cachetextconv = true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;h2 id="add-update-dot-gitattributes-file-to-the-project-repo">&lt;span class="section-num">3&lt;/span> Add/update &lt;code>.gitattributes&lt;/code> file to the project repo&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#add-update-dot-gitattributes-file-to-the-project-repo">#&lt;/a>&lt;/h2>
&lt;p>Now, in your project repo&amp;rsquo;s &lt;code>.gitattributes&lt;/code> file, you need to
associate files with the &lt;em>diff configurations&lt;/em> set above.&lt;/p>
&lt;p>Below will use the &lt;code>minjs&lt;/code> configuration for &lt;em>*.min.js&lt;/em> and
&lt;em>*.bundle.js&lt;/em> files, and &lt;code>mincss&lt;/code> configuration for &lt;em>*.min.css&lt;/em> and
&lt;em>main.css&lt;/em>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">*.min.js diff&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">minjs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">*.bundle.js diff&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">minjs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">*.min.css diff&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">mincss&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">main.css diff&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">mincss&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;h2 id="beautiful-result">Beautiful Result&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#beautiful-result">#&lt;/a>&lt;/h2>
&lt;p>&lt;a id="figure--git-diff-min-js">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/git-diff-minified-js-and-css/git-diff-minified-js.png" alt="Figure 1: git diff of minified JS as seen in Magit"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 1: &lt;/span>&lt;code>git diff&lt;/code> of minified JS as seen in &lt;em>Magit&lt;/em>
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;p>Isn&amp;rsquo;t that better than how GitHub shows the &lt;em>exact same commit
diff&lt;/em>? 😎&lt;/p>
&lt;p>&lt;a id="figure--github-diff-min-js">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/git-diff-minified-js-and-css/github-diff-minified-js.png" alt="Figure 2: Same commit diff shown on GitHub"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 2: &lt;/span>Same commit &lt;code>diff&lt;/code> shown on &lt;em>GitHub&lt;/em>
&lt;/p>
&lt;/figcaption>&lt;/figure></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/web">web</category><category domain="https://scripter.co/categories/unix">unix</category><category domain="https://scripter.co/tags/minified">minified</category><category domain="https://scripter.co/tags/javascript">javascript</category><category domain="https://scripter.co/tags/git">git</category><category domain="https://scripter.co/tags/diff">diff</category><category domain="https://scripter.co/tags/css">css</category><category domain="https://scripter.co/tags/magit">magit</category><guid>https://scripter.co/git-diff-minified-js-and-css/</guid><pubDate>Mon, 19 Mar 2018 18:13:00 -0400</pubDate></item><item><title>Installing go toolchain</title><link>https://scripter.co/installing-go-toolchain/</link><description>&lt;blockquote>&amp;ldquo;Installing&amp;rdquo; &lt;code>go&lt;/code> is simply extracting its release archive, putting it
somewhere in you &lt;code>$HOME&lt;/code> and pointing &lt;code>GOROOT&lt;/code> and &lt;code>PATH&lt;/code> env vars to
it.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#installing-go">Installing &lt;code>go&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#updating-go">Updating &lt;code>go&lt;/code>&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>There are &lt;strong>two&lt;/strong> reasons why I suggest installing &lt;code>go&lt;/code> to anyone,
whether they are Go developers, or not (like me).&lt;/p>
&lt;ol>
&lt;li>You can then build amazing utilities like &lt;a href="https://github.com/peco/peco">peco&lt;/a>, &lt;a href="https://github.com/gohugoio/hugo">hugo&lt;/a> and &lt;a href="https://github.com/variadico/noti">noti&lt;/a>.&lt;/li>
&lt;li>&lt;strong>It&amp;rsquo;s easy!&lt;/strong>&lt;/li>
&lt;/ol>
&lt;h2 id="installing-go">Installing &lt;code>go&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#installing-go">#&lt;/a>&lt;/h2>
&lt;p>Below instructions are for installing &lt;code>go&lt;/code> on a 64-bit GNU/Linux
machine, and using &lt;code>tcsh&lt;/code> shell. But similar steps should work for any
other OS and shell.&lt;/p>
&lt;ol>
&lt;li>Download the &lt;em>tar.gz&lt;/em> for the latest &lt;em>linux-amd64&lt;/em> binaries from
&lt;a href="https://golang.org/dl/">https://golang.org/dl/&lt;/a>.&lt;/li>
&lt;li>Extract it to some place in your &lt;code>$HOME&lt;/code>. I extract it to
&lt;code>${HOME}/go/&lt;/code>&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>.&lt;/li>
&lt;li>Create a directory where you would want to install the &lt;code>go&lt;/code>
packages.
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">mkdir -p ~/go.apps
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>Set the following environment variables&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>, and also save them to
your shell config:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tcsh" data-lang="tcsh">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">setenv &lt;/span>GOROOT &lt;span class="k">${&lt;/span>&lt;span class="nv">HOME&lt;/span>&lt;span class="k">}&lt;/span>/go &lt;span class="c"># go root&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">setenv &lt;/span>GOPATH &lt;span class="k">${&lt;/span>&lt;span class="nv">HOME&lt;/span>&lt;span class="k">}&lt;/span>/go.apps &lt;span class="c"># for go applications&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>Add the &lt;code>${GOROOT}/bin&lt;/code> and &lt;code>${GOPATH}/bin&lt;/code> directories to your
&lt;code>$PATH&lt;/code>.&lt;/li>
&lt;/ol>
&lt;p>Now you can install any &lt;code>go&lt;/code> application!&lt;/p>
&lt;p>For instance, &lt;code>noti&lt;/code> is a nice little utility that triggers an alert
(desktop popup, &lt;em>Pushbullet&lt;/em> notification, etc.) when a process
finishes. From its &lt;a href="https://github.com/variadico/noti#installation">installation notes&lt;/a>, you just run the below to
install it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">go get -u github.com/variadico/noti/cmd/noti
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Apart from the &lt;code>go&lt;/code> applications I suggested here, &lt;em>go&lt;/em> out and explore
more &amp;ndash; &lt;code>go get&lt;/code> them 😁&lt;/p>
&lt;h2 id="updating-go">Updating &lt;code>go&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#updating-go">#&lt;/a>&lt;/h2>
&lt;ol>
&lt;li>Delete the existing &lt;code>$GOROOT&lt;/code> directory (&lt;strong>not &lt;code>GOPATH&lt;/code>!&lt;/strong>)
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">rm -rf ~/go &lt;span class="c1"># as that is my GOROOT&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>Download the &lt;em>tar.gz&lt;/em> for the latest &lt;em>linux-amd64&lt;/em> binaries.&lt;/li>
&lt;li>Extract it to the same &lt;code>$GOROOT&lt;/code> (&lt;code>~/go&lt;/code> in my case).&lt;/li>
&lt;/ol>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>I prefer to not add the version number to my &lt;code>go&lt;/code> installation
folder. That way, when I want to update it, I simply &lt;code>rm -rf&lt;/code> it and
put in the new version.. and I don&amp;rsquo;t need to update &lt;code>GOROOT&lt;/code> or
&lt;code>PATH&lt;/code>.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>You can refer to these official &lt;code>go&lt;/code> references [ &lt;a href="https://golang.org/doc/install#tarball">1&lt;/a> ], [ &lt;a href="https://golang.org/doc/install#testing">2&lt;/a> ] for
further information on these variables.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/unix">unix</category><category domain="https://scripter.co/tags/toolchain">toolchain</category><category domain="https://scripter.co/tags/golang">golang</category><guid>https://scripter.co/installing-go-toolchain/</guid><pubDate>Fri, 24 Feb 2017 01:33:47 -0500</pubDate></item><item><title>Count Down Timer in Shell</title><link>https://scripter.co/count-down-timer-in-shell/</link><description>&lt;blockquote>&lt;p>I was working on a &lt;code>tcsh&lt;/code> script that did some cool stuff. But if a
user ran that script not knowing the true impact of the script, it
could make some bad irreversible changes.&lt;/p>
&lt;p>While I could simply echo a warning statement and put a &lt;code>sleep 10&lt;/code>, I
wanted the wait time to be shown &lt;strong>live&lt;/strong>.&lt;/p>
&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#explanation">Explanation&lt;/a>&lt;/li>
&lt;li>&lt;a href="#result">Result&lt;/a>&lt;/li>
&lt;li>&lt;a href="#bash-implementation">Bash implementation&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>So here&amp;rsquo;s what worked pretty nicely &amp;mdash; The warning message is shown to
the user, and the actual wait time countdown is also displayed.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tcsh" data-lang="tcsh">&lt;span class="line">&lt;span class="cl">&lt;span class="c">#!/usr/bin/env tcsh&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">set &lt;/span>&lt;span class="nv">wait_time&lt;/span> &lt;span class="o">=&lt;/span> 10 &lt;span class="c"># seconds&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Are you sure you meant to run this script?&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;This script does something drastic that you would severely regret if you happened to run this script by mistake!&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">set &lt;/span>&lt;span class="nv">temp_cnt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">${&lt;/span>&lt;span class="nv">wait_time&lt;/span>&lt;span class="k">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># https://www.cyberciti.biz/faq/csh-shell-scripting-loop-example/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">while&lt;/span> &lt;span class="o">(&lt;/span> &lt;span class="k">${&lt;/span>&lt;span class="nv">temp_cnt&lt;/span>&lt;span class="k">}&lt;/span> &amp;gt;&lt;span class="o">=&lt;/span> 1 &lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> printf &lt;span class="s2">&amp;#34;\rYou have %2d second(s) remaining to hit Ctrl+C to cancel that operation!&amp;#34;&lt;/span> &lt;span class="k">${&lt;/span>&lt;span class="nv">temp_cnt&lt;/span>&lt;span class="k">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sleep 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> @ temp_cnt--
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">end
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">&lt;/span>&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;h2 id="explanation">Explanation&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#explanation">#&lt;/a>&lt;/h2>
&lt;ul>
&lt;li>The &lt;code>while&lt;/code> loop runs for &lt;code>$wait_time&lt;/code> times; each time waiting for
a second (&lt;code>sleep 1&lt;/code>) and then decrementing the temporary counter
&lt;code>$temp_cnt&lt;/code>.&lt;/li>
&lt;li>&lt;code>printf&lt;/code> is chosen instead of &lt;code>echo -n&lt;/code> because I wanted to have the
seconds number always hold 2 character places (&lt;code>%2d&lt;/code>).&lt;/li>
&lt;li>The &lt;code>\r&lt;/code> character in &lt;code>printf&lt;/code> makes the magic here. It represents
&lt;em>carriage return&lt;/em> i.e. The cursor will return to the beginning of
the line, and then print the following string, &lt;strong>overwriting&lt;/strong>
whatever there was on that line earlier.
&lt;ul>
&lt;li>&lt;code>printf&lt;/code> acts like &lt;code>echo -n&lt;/code> i.e. a newline is not inserted
automatically at the end of the printed message. In order to add a
newline at the end for &lt;code>printf&lt;/code>, you need to do so explicitly by
adding a &lt;code>\n&lt;/code> character.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="result">Result&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#result">#&lt;/a>&lt;/h2>
&lt;p>&lt;em>&lt;a href="https://asciinema.org/a/4vk5dayfbj4k19ghra6k67mmw">Click here&lt;/a> to see the animation on asciinema.org.&lt;/em>&lt;/p>
&lt;h2 id="bash-implementation">Bash implementation&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#bash-implementation">#&lt;/a>&lt;/h2>
&lt;p>Below is a re-implementation of the above in bash.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/usr/bin/env bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="nv">wait_time&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">10&lt;/span> &lt;span class="c1"># seconds&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Are you sure you meant to run this script?&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;This script does something drastic that you would severely regret if you happened to run this script by mistake!&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">temp_cnt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">wait_time&lt;/span>&lt;span class="si">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">while&lt;/span> &lt;span class="o">[[&lt;/span> &lt;span class="si">${&lt;/span>&lt;span class="nv">temp_cnt&lt;/span>&lt;span class="si">}&lt;/span> -gt &lt;span class="m">0&lt;/span> &lt;span class="o">]]&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">do&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">printf&lt;/span> &lt;span class="s2">&amp;#34;\rYou have %2d second(s) remaining to hit Ctrl+C to cancel that operation!&amp;#34;&lt;/span> &lt;span class="si">${&lt;/span>&lt;span class="nv">temp_cnt&lt;/span>&lt;span class="si">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sleep &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">((&lt;/span>temp_cnt--&lt;span class="o">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">done&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/unix">unix</category><category domain="https://scripter.co/categories/shell">shell</category><category domain="https://scripter.co/tags/bash">bash</category><category domain="https://scripter.co/tags/tcsh">tcsh</category><category domain="https://scripter.co/tags/countdown">countdown</category><category domain="https://scripter.co/tags/timer">timer</category><guid>https://scripter.co/count-down-timer-in-shell/</guid><pubDate>Mon, 09 Jan 2017 08:02:25 -0500</pubDate></item><item><title>Check If a Command/Executable Exists from Shell Script</title><link>https://scripter.co/check-if-a-command-exists-from-shell-script/</link><description>&lt;blockquote>Shell script snippets to check if you have an executable or binary
installed in &lt;code>PATH&lt;/code>.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#bash-shell">Bash Shell&lt;/a>&lt;/li>
&lt;li>&lt;a href="#tcsh-shell">Tcsh Shell&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>I often need to check if a particular executable is present in the
&lt;code>PATH&lt;/code> before I can proceed with what I am doing in a shell
script. Also, I need to work with both &lt;code>tcsh&lt;/code> and &lt;code>bash&lt;/code>
scripts. Below presents the solution that has worked for these shell
scripts for me.&lt;/p>
&lt;h2 id="bash-shell">Bash Shell&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#bash-shell">#&lt;/a>&lt;/h2>
&lt;p>The below solution using &lt;code>hash&lt;/code> was with the help of &lt;a href="https://stackoverflow.com/a/677212/1219634">this SO solution&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> ! &lt;span class="nb">hash&lt;/span> some_exec 2&amp;gt;/dev/null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&amp;#39;some_exec&amp;#39; was not found in PATH&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here is the &lt;em>tl;dr&lt;/em> from the above SO solution:&lt;/p>
&lt;blockquote>
&lt;p>Where bash is your shell/hashbang, consistently use &lt;code>hash&lt;/code> (for
commands) or &lt;code>type&lt;/code> (to consider built-ins &amp;amp; keywords). When writing a
POSIX script, use &lt;code>command -v&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;h2 id="tcsh-shell">Tcsh Shell&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#tcsh-shell">#&lt;/a>&lt;/h2>
&lt;p>As it turns out, the &lt;code>tcsh&lt;/code> shell does not have the same &lt;code>hash&lt;/code>
command as the &lt;code>bash&lt;/code> shell.&lt;/p>
&lt;p>But the below solution using &lt;code>where&lt;/code> which I found with the help of
&lt;a href="https://stackoverflow.com/a/22058620/1219634">this SO solution&lt;/a> works fine.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tcsh" data-lang="tcsh">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="o">(&lt;/span> &lt;span class="sb">`&lt;/span>&lt;span class="nb">where &lt;/span>some_exec&lt;span class="sb">`&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span> &lt;span class="o">)&lt;/span> &lt;span class="k">then
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k"> &lt;/span>&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&amp;#39;some_exec&amp;#39; was not found in PATH&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">endif&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/unix">unix</category><category domain="https://scripter.co/categories/shell">shell</category><category domain="https://scripter.co/tags/bash">bash</category><category domain="https://scripter.co/tags/tcsh">tcsh</category><category domain="https://scripter.co/tags/executable">executable</category><category domain="https://scripter.co/tags/exists">exists</category><category domain="https://scripter.co/tags/binary">binary</category><guid>https://scripter.co/check-if-a-command-exists-from-shell-script/</guid><pubDate>Wed, 23 Nov 2016 17:07:26 -0500</pubDate></item><item><title>Second Argument to basename</title><link>https://scripter.co/second-argument-to-basename/</link><description>&lt;p>It is quite common knowledge that the &lt;code>basename&lt;/code> command is used to get just the file name without its full path.&lt;/p>
&lt;pre tabindex="0">&lt;code>&amp;gt; basename /home/$USER/file.txt
file.txt
&lt;/code>&lt;/pre>&lt;p>But what wasn&amp;rsquo;t common knowledge, at least to me, was that &lt;code>basename&lt;/code> also accepts a &lt;em>second&lt;/em> argument ..&lt;/p>
&lt;p>That argument is used to specify the trailing string to be removed from first argument.&lt;/p>
&lt;p>From &lt;code>man basename&lt;/code>, we get&lt;/p>
&lt;pre tabindex="0">&lt;code>DESCRIPTION
Print NAME with any leading directory components removed. If
specified, also remove a trailing SUFFIX.
EXAMPLES
basename /usr/bin/sort
Output &amp;#34;sort&amp;#34;.
basename include/stdio.h .h
Output &amp;#34;stdio&amp;#34;.
&lt;/code>&lt;/pre>&lt;p>In other words, with the second argument set to the file&amp;rsquo;s extension, &lt;code>basename&lt;/code> returns the file name without the full path &lt;strong>and&lt;/strong> without the extension.&lt;/p>
&lt;pre tabindex="0">&lt;code>&amp;gt; basename /home/$USER/file.txt .txt
file
&lt;/code>&lt;/pre>&lt;hr>
&lt;p>I came across this feature of &lt;code>basename&lt;/code> when I wanted to create this tcsh alias:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tcsh" data-lang="tcsh">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Usage: md2 html file.md # Converts file.md (markdown) to file.html&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># md2 pdf file.md # Converts file.md (markdown) to file.pdf&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># md2 docx file.md # Converts file.md (markdown) to file.docx (Word)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">alias &lt;/span>md2 &lt;span class="s1">&amp;#39;pandoc \!:3* \!:2 -o `basename \!:2 .md`.\!:1&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description><category domain="https://scripter.co/categories/unix">unix</category><guid>https://scripter.co/second-argument-to-basename/</guid><pubDate>Wed, 23 Nov 2016 13:24:45 -0500</pubDate></item><item><title>tmux Shift + Mouse</title><link>https://scripter.co/tmux-shift-plus-mouse/</link><description>&lt;blockquote>Using mouse to copy/paste in &lt;code>tmux&lt;/code> &lt;em>panes&lt;/em>.&lt;/blockquote>&lt;p>I had been missing the &lt;em>&amp;ldquo;select and middle-click&amp;rdquo;&lt;/em> method for copying
and pasting stuff in &lt;code>tmux&lt;/code> panes.&lt;/p>
&lt;p>Thanks to &lt;a href="https://superuser.com/questions/598718/how-do-i-select-entire-words-with-tmuxs-mouse-mode">this&lt;/a> post, I learned that I can use the &lt;kbd>Shift&lt;/kbd> key and
bypass &lt;code>tmux&lt;/code>&amp;rsquo;s own copy and paste method.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Key/Mouse Binding&lt;/th>
&lt;th>Action&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;kbd>Shift&lt;/kbd> + Mouse left button double-click&lt;/td>
&lt;td>Copies the double-clicked word&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;kbd>Shift&lt;/kbd> + Select using mouse&lt;/td>
&lt;td>Copies the selection&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;kbd>Shift&lt;/kbd> + Mouse middle button click&lt;/td>
&lt;td>Pastes the copied text using above method in the &lt;code>tmux&lt;/code> pane&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/unix">unix</category><category domain="https://scripter.co/tags/tmux">tmux</category><category domain="https://scripter.co/tags/mouse">mouse</category><category domain="https://scripter.co/tags/copy">copy</category><category domain="https://scripter.co/tags/paste">paste</category><guid>https://scripter.co/tmux-shift-plus-mouse/</guid><pubDate>Thu, 28 Aug 2014 16:47:46 -0400</pubDate></item><item><title>Using sed</title><link>https://scripter.co/using-sed/</link><description>&lt;p>&lt;em>sed&lt;/em> stands for &lt;strong>s&lt;/strong>tream &lt;strong>ed&lt;/strong>itor.&lt;/p>
&lt;p>This is the most common way of my sed usage:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tcsh" data-lang="tcsh">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="o">[&lt;/span>SOMETHING&lt;span class="o">]&lt;/span> | sed &lt;span class="s1">&amp;#39;s/old/NEW/g&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Based on that, I have this tcsh alias&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> to get timestamps that I use to append to quick tar backups.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tcsh" data-lang="tcsh">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">alias &lt;/span>gettimestamp &lt;span class="s1">&amp;#39;date | tr &amp;#34; :&amp;#34; &amp;#34;__&amp;#34; | sed &amp;#39;&lt;/span>&lt;span class="s2">&amp;#34;&amp;#39;&amp;#34;&lt;/span>&lt;span class="s1">&amp;#39;s/_[0-9]*_EDT.*//g&amp;#39;&lt;/span>&lt;span class="s2">&amp;#34;&amp;#39;&amp;#34;&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Learn about sed from [here][s1].&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Note how single quotes are escaped inside single-quoted alias definitions in tcsh.
[s1]: &lt;a href="http://www.grymoire.com/Unix/Sed.html">http://www.grymoire.com/Unix/Sed.html&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description><category domain="https://scripter.co/categories/sed">sed</category><category domain="https://scripter.co/categories/unix">unix</category><category domain="https://scripter.co/categories/tcsh">tcsh</category><category domain="https://scripter.co/categories/alias">alias</category><guid>https://scripter.co/using-sed/</guid><pubDate>Mon, 17 Mar 2014 09:31:29 -0400</pubDate></item><item><title>Send a command to every pane/window/session in tmux</title><link>https://scripter.co/command-to-every-pane-window-session-in-tmux/</link><description>&lt;blockquote>Faster way to send the same command to each and every &lt;em>pane&lt;/em> in your
tmux &lt;em>session&lt;/em>.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#send-command-to-all-panes-in-all-sessions">Send command to all panes in &lt;strong>all&lt;/strong> sessions&lt;/a>
&lt;ul>
&lt;li>&lt;a href="#usage">Usage&lt;/a>&lt;/li>
&lt;li>&lt;a href="#about-the-double-hashes">About the &lt;code>##&lt;/code>&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a href="#send-command-to-all-panes-in-current-session">Send command to all panes in &lt;strong>current&lt;/strong> session&lt;/a>&lt;/li>
&lt;li>&lt;a href="#tmux-send-cmd-to-all-panes-old">Older version (circa 2014)&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>Ever wondered how you would send the &lt;code>clear&lt;/code> command to &lt;em>each pane&lt;/em>,
in &lt;em>each window&lt;/em>, in &lt;em>each session&lt;/em> in &lt;code>tmux&lt;/code>, or how you would do
source your shell config file in each after each tweak?&lt;/p>
&lt;p>Here are few excerpts from my &lt;a href="https://github.com/kaushalmodi/dotfiles/blob/master/tmux/dot-tmux.conf">&lt;code>.tmux.conf&lt;/code>&lt;/a> that allow doing just
that.&lt;/p>
&lt;h2 id="send-command-to-all-panes-in-all-sessions">Send command to all panes in &lt;strong>all&lt;/strong> sessions&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#send-command-to-all-panes-in-all-sessions">#&lt;/a>&lt;/h2>
&lt;p>Thanks to the tip in comments from &lt;em>Bob Fleming&lt;/em>, I learned that &lt;code>tmux&lt;/code> has a &lt;code>-a&lt;/code>
switch for the &lt;code>list-panes&lt;/code> command.&lt;/p>
&lt;p>From &lt;code>man tmux&lt;/code>,&lt;/p>
&lt;blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">list-panes [-as] [-F format] [-t target]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (alias: lsp)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> If -a is given, target is ignored and all panes on the server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> are listed. If -s is given, target is a session (or the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> current session). If neither is given, target is a window (or
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> the current window). For the meaning of the -F flag, see the
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> FORMATS section.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/blockquote>
&lt;p>With that knowledge, the &lt;a href="#tmux-send-cmd-to-all-panes-old">older version&lt;/a> of the &lt;kbd>E&lt;/kbd> binding now reduces
to,&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-docker" data-lang="docker">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Send the same command to all panes/windows/sessions&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">bind&lt;/span> E command-prompt -p &lt;span class="s2">&amp;#34;Command:&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="s2">&amp;#34;run \&amp;#34;tmux list-panes -a -F &amp;#39;##{session_name}:##{window_index}.##{pane_index}&amp;#39; \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> | xargs -I PANE tmux send-keys -t PANE &amp;#39;%1&amp;#39; Enter\&amp;#34;&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;h3 id="usage">Usage&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#usage">#&lt;/a>&lt;/h3>
&lt;ul>
&lt;li>Type the following binding in any &lt;code>tmux&lt;/code> pane: &lt;kbd>C-z E&lt;/kbd>&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/li>
&lt;li>Enter a command that you would want to send to all the panes, like
&lt;code>source ~/.alias; clear&lt;/code> &lt;em>(this is entered in the tmux command
prompt)&lt;/em>.&lt;/li>
&lt;li>That will source the &lt;code>~/.alias&lt;/code> in &lt;strong>all&lt;/strong> panes, and then clear the
terminals as well.&lt;/li>
&lt;/ul>
&lt;h3 id="about-the-double-hashes">About the &lt;code>##&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#about-the-double-hashes">#&lt;/a>&lt;/h3>
&lt;div class="note">
&lt;p>The &lt;code>#&lt;/code> character needs to be escaped by another &lt;code>#&lt;/code> and typed as
&lt;code>##&lt;/code>, only when used inside the &lt;code>run-shell&lt;/code> command.&lt;/p>
&lt;/div>
&lt;p>.. because otherwise, &lt;code>tmux run-shell&lt;/code> command will replace the
unescaped &lt;code>#{session_name}&lt;/code>, &lt;code>#{window_index}&lt;/code> and &lt;code>#{pane_index}&lt;/code> with
their current values &lt;strong>before&lt;/strong> executing the command.&lt;/p>
&lt;p>With the hashes escaped, those variables will be evaluated &lt;em>at run
time&lt;/em>.&lt;/p>
&lt;p>But if you were to type the above command directly in the terminal,
without the &lt;code>run-shell&lt;/code> command wrapper, you would use only single
&lt;code>#&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">tmux list-panes -s -F &amp;#34;#{session_name}:#{window_index}.#{pane_index}&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;h2 id="send-command-to-all-panes-in-current-session">Send command to all panes in &lt;strong>current&lt;/strong> session&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#send-command-to-all-panes-in-current-session">#&lt;/a>&lt;/h2>
&lt;p>The &lt;code>list-panes&lt;/code> command has another useful switch: &lt;code>-s&lt;/code>, which takes
an optional argument, a &lt;em>session name&lt;/em>. If that argument is not
supplied, it takes the current session name by default.&lt;/p>
&lt;p>Below &lt;kbd>C-e&lt;/kbd> binding is used to send a command to all panes, in all
windows, but &lt;strong>only in the current session&lt;/strong>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-docker" data-lang="docker">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">bind&lt;/span> C-e command-prompt -p &lt;span class="s2">&amp;#34;Command:&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="s2">&amp;#34;run \&amp;#34;tmux list-panes -s -F &amp;#39;##{session_name}:##{window_index}.##{pane_index}&amp;#39; \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> | xargs -I PANE tmux send-keys -t PANE &amp;#39;%1&amp;#39; Enter\&amp;#34;&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;h2 id="tmux-send-cmd-to-all-panes-old">Older version (circa 2014)&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#tmux-send-cmd-to-all-panes-old">#&lt;/a>&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-docker" data-lang="docker">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Send the same command to all panes/windows/sessions&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">bind&lt;/span> E command-prompt -p &lt;span class="s2">&amp;#34;Command:&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="s2">&amp;#34;run \&amp;#34;tmux list-sessions -F &amp;#39;##{session_name}&amp;#39; \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> | xargs -I SESS tmux list-windows -t SESS -F &amp;#39;SESS:##{window_index}&amp;#39; \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> | xargs -I SESS_WIN tmux list-panes -t SESS_WIN -F &amp;#39;SESS_WIN.##{pane_index}&amp;#39; \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> | xargs -I SESS_WIN_PANE tmux send-keys -t SESS_WIN_PANE &amp;#39;%1&amp;#39; Enter\&amp;#34;&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>I have set my tmux prefix to &lt;kbd>C-z&lt;/kbd>.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/unix">unix</category><category domain="https://scripter.co/tags/tmux">tmux</category><category domain="https://scripter.co/tags/pane">pane</category><category domain="https://scripter.co/tags/window">window</category><category domain="https://scripter.co/tags/session">session</category><guid>https://scripter.co/command-to-every-pane-window-session-in-tmux/</guid><pubDate>Thu, 06 Mar 2014 09:50:21 -0500</pubDate></item></channel></rss>