<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>emre şahin's digital garden 🍃 - bash</title>
    <link>https://emresahin.net/tags/bash/</link>
    <description>Posts in the bash tag</description>
    <language>en</language>
    <managingEditor>contact@emresahin.net (Emre Şahin)</managingEditor>
    <lastBuildDate>Tue, 15 Sep 2026 19:46:32 +0000</lastBuildDate>
    <atom:link href="https://emresahin.net/tags/bash/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>devlog 7</title>
      <published>2024-08-06T06:43:01+00:00</published>
      <updated>2024-08-06T06:43:01+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Tue, 06 Aug 2024 06:43:01 +0000</pubDate>
      <link>https://emresahin.net/devlog-7/</link>
      <guid isPermaLink="true">https://emresahin.net/devlog-7/</guid>
      <description>I noticed that I often forget to update the Xvc CHANGELOG. To fix this, I added a pre-push hook that checks the files I’m pushing. If the CHANGELOG is not among them and there are changes to Rust files, it prevents the push. I hope this will remind me to update the logs more frequently. #!/bin/ba...</description>
      <category>devlog</category>
      <category>Software Development</category>
      <category>Git</category>
      <category>git-hooks</category>
      <category>automation</category>
      <category>changelog</category>
      <category>xvc</category>
      <category>bash</category>
      <content:encoded><![CDATA[<p>I noticed that I often forget to update the Xvc CHANGELOG. To fix this, I added
a pre-push hook that checks the files I’m pushing. If the CHANGELOG is not among
them and there are changes to Rust files, it prevents the push. I hope this will
remind me to update the logs more frequently.</p>
<pre><code class="language-bash">#!/bin/bash

# Git pre-push hook to check if CHANGELOG.md is included in the push and if the branch is develop

# Get the current branch name

current_branch=$(git rev-parse --abbrev-ref HEAD)

# Check if the branch is develop

if [ "$current_branch" == "main" ]; then
	echo "You are on the main branch. Skipping CHANGELOG.md check."
	exit 0
fi

# remote="$1"
# url="$2"

# Get the list of commits to be pushed
commits=$(git rev-list '@{u}..HEAD')

has_rust_files=$(false)

# TODO: We can iterate to get file list only once
# Get the list of files that are going to be pushed
for commit in $commits; do
	if git diff-tree --no-commit-id --name-only -r "${commit}" | grep -q "\\.rs$"; then
		has_rust_files=$(true)
	fi
done

if [[ ! $has_rust_files ]]; then
	echo "No .rs files in the push, no need to check CHANGELOG"
	exit 0
fi

# Check if CHANGELOG.md is among the files in the commits
for commit in $commits; do
	if git diff-tree --no-commit-id --name-only -r "${commit}" | grep -q "CHANGELOG.md"; then
		echo "CHANGELOG.md is included in the push."
		exit 0
	fi
done

echo "ERROR: CHANGELOG.md is not included in the push."
exit 1
</code></pre>
<p><strong>Edit (2025-02-08):</strong> Added a check for when only code files are changed.</p>]]></content:encoded>
    </item>
    <item>
      <title>TIL May 1</title>
      <published>2020-05-01T22:19:07+00:00</published>
      <updated>2020-05-01T22:19:07+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Fri, 01 May 2020 22:19:07 +0000</pubDate>
      <link>https://emresahin.net/til-may-1--17222/</link>
      <guid isPermaLink="true">https://emresahin.net/til-may-1--17222/</guid>
      <description>Nota seems like a nice command-line calculator. It converts what you type into ASCII art formulas. In[1]: 10 + 10 Out[1]: 20.0 _____ In[2]: ╲╱ 100 Out[2]: 10.0 ┌ ┐ In[3]: Max │ 10 , 1 , 21 , -3 │ └ ┘ Out[3]: 21.0 In[4]: ⟨Emre's Number⟩ ≡ 79 Out[4]: 79.0 _______________ In[5]: ╲╱ Emre's Number Out...</description>
      <category>TIL</category>
      <category>Nota</category>
      <category>CLI</category>
      <category>Calculator</category>
      <category>Haskell</category>
      <category>Intention</category>
      <category>Productivity</category>
      <category>Git</category>
      <category>Bash</category>
      <category>Zsh</category>
      <content:encoded><![CDATA[<ul>
<li><a href="https://kary.us/nota/">Nota</a> seems like a nice command-line calculator. It converts what you type into ASCII art formulas.</li>
</ul>
<pre><code>  In[1]: 10 + 10

 Out[1]: 20.0


           _____
  In[2]: ╲╱ 100

 Out[2]: 10.0


             ┌                  ┐
  In[3]: Max │ 10 , 1 , 21 , -3 │
             └                  ┘

 Out[3]: 21.0


  In[4]: ⟨Emre's Number⟩ ≡  79

 Out[4]: 79.0


           _______________
  In[5]: ╲╱ Emre's Number

 Out[5]: 8.888194417315589


                      2
  In[6]: Emre's Number

 Out[6]: 6241.0


                      Emre's Number
  In[7]: Emre's Number

 Out[7]: 8.1759873707105095e149

</code></pre>
<pre><code>It looks a bit heavy for a CLI calculator, as it is written in Haskell and downloads 100+ MB of libraries, but when you need ASCII art to display your calculations or want to use spaces in variable names, it may prove useful.
</code></pre>
<ul>
<li>
<p>I began to use <a href="https://www.getintention.com/">Intention</a> to limit my Twitter time. It allows you to set a limited time (1, 5, 10, or 15 minutes) for yourself and tracks the total time you spend on <em>addictive sites.</em> When this total time is lower than your goal for a period, you get a streak. It looks visually and psychologically nicer than LeechBlock.</p>
</li>
<li>
<p>I read the <a href="http://www.git-scm.com/book/en/v2/Git-Tools-Reset-Demystified"><code>git reset</code></a> section in the Git book. It details how <code>git reset</code> behaves with its <code>--soft</code>, <code>--mixed</code>, and <code>--hard</code> parameters. The first resets only the <code>HEAD</code>; the second resets both the index and <code>HEAD</code>; and the third resets the working tree and copies files back from the current <code>HEAD</code> to the working tree.</p>
<p>One important point: Contrasting <code>git checkout master</code> and <code>git reset master</code>: the first moves <code>HEAD</code> to the <code>master</code> branch, while the second moves the current branch to <code>master</code>.</p>
<p><code>git reset</code> can also be used to squash commits. Basically, you <code>git reset --mixed</code> to an earlier commit like <code>HEAD~3</code> and recommit. This creates a new commit, taking <code>HEAD~3</code> as the parent and skipping <code>HEAD~2</code> and <code>HEAD~1</code>, resulting in a new <code>HEAD</code>.</p>
</li>
<li>
<p><a href="https://github.com/dylanaraps/pure-bash-bible">Here</a> are many useful pure Bash functions to be used in scripts. I’m a Zsh person, but writing Bash scripts is more portable, of course.</p>
</li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>Zenity</title>
      <published>2018-12-03T18:54:26+00:00</published>
      <updated>2018-12-03T18:54:26+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Mon, 03 Dec 2018 18:54:26 +0000</pubDate>
      <link>https://emresahin.net/zenity-14386-18029/</link>
      <guid isPermaLink="true">https://emresahin.net/zenity-14386-18029/</guid>
      <description>When you need a simple dialog to get input from a user or display information in a GUI dialog box, zenity is a great tool. It allows shell scripts to interact with users through various types of dialogs. zenity --info --text="Merge complete. Updated 3 of 10 files."</description>
      <category>shell</category>
      <category>tool</category>
      <category>zenity</category>
      <category>bash</category>
      <category>shell</category>
      <category>linux</category>
      <category>gui</category>
      <content:encoded><![CDATA[<p>When you need a simple dialog to get input from a user or display information in a GUI dialog box, <a href="https://help.gnome.org/users/zenity/3.24/"><code>zenity</code></a> is a great tool. It allows shell scripts to interact with users through various types of dialogs.</p>
<pre><code class="language-bash">zenity --info --text="Merge complete. Updated 3 of 10 files."
</code></pre>
<p><img src="https://emresahin.net/images/zenity-ss.png" alt="zenity-ss"></p>]]></content:encoded>
    </item>
    <item>
      <title>Cat for Writers</title>
      <published>2014-07-06T21:00:00+00:00</published>
      <updated>2014-07-06T21:00:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 06 Jul 2014 21:00:00 +0000</pubDate>
      <link>https://emresahin.net/cat-for-writers/</link>
      <guid isPermaLink="true">https://emresahin.net/cat-for-writers/</guid>
      <description>I write every day. Every day, I write. I have a quota of words to fill, and after each sleep session, I sit in front of my keyboard and begin typing. I used to use Emacs for this—the One True Editor. Yet, it has one flaw that disrupted my writing routine: it has too many features. When I encounte...</description>
      <category>Tools</category>
      <category>Writing</category>
      <category>Cat</category>
      <category>Ed</category>
      <category>Emacs</category>
      <category>Org-mode</category>
      <category>Writing</category>
      <category>Bash</category>
      <category>Scripts</category>
      <content:encoded><![CDATA[<p>I write every day. Every day, I write. I have a quota of words to fill, and after each sleep session, I sit in front of my keyboard and begin typing.</p>
<p>I used to use <a href="http://en.wikipedia.org/wiki/GNU_Emacs">Emacs</a> for this—the One True Editor. Yet, it has one flaw that disrupted my writing routine: it has too many features. When I encountered writer’s block or an idea I couldn’t quite articulate, I would start tinkering with the editor instead. Emacs is like a friend, and one should keep friends away from the workplace; otherwise, no work gets done.</p>
<p>For this reason, I began writing with <a href="http://en.wikipedia.org/wiki/Ed_(text_editor)"><code>ed</code></a> some time ago. <code>ed</code> is not my friend; it’s <em>the</em> standard editor. It’s also the grandfather of <a href="http://en.wikipedia.org/wiki/Vi"><code>vi</code></a>, but fathers should not bear the vices of their sons.</p>
<p><strong>Note from 2024:</strong> Ten years after writing this post, I’m editing it with Neovim, the grandson of all these tools.</p>
<p><code>ed</code> was very nice to write with. It doesn’t have many features. It prints a <code>0</code> when it opens, I type <code>i</code> to insert text, and then I write whatever I like. When I put a lonely <code>.</code> on a line, it understands that I’ve finished my words. Then, with <code>w</code>, I save the file to disk and see its length—length in bytes, as every true writer nowadays should know.</p>
<p><code>ed</code> is minimal, and while I occasionally get frustrated by its modal behavior, that’s exactly why I was happy to write with it.</p>
<p>However, I eventually decided that even pressing those few command keys was too much. <code>i</code>, <code>.</code>, and <code>w</code>—one can forget them easily. And it seemed I had a tendency to learn more of its commands, which are passed down to its grandchildren. God save us from the vices of <code>vi</code>.</p>
<p>Then, I decided to concoct a script that uses <code>cat</code>. Everyone knows writers like cats, and I like <code>cat</code> too; it concatenates text into files and is also used to print their content. It’s much simpler than <code>ed</code>—much simpler. It is not an ambiguous dork like <code>ed</code>; it’s a simple animal that writes into files whatever I type into it.</p>
<p>It was easy to check the file size in words after I finished. I wrote a <code>while</code> loop to continue until I reached my daily quota. The script creates a new file, adds an <a href="http://orgmode.org">Org-mode</a> header, and includes the contents of the clipboard just after the title so I can include notes. <code>cat</code> exits with <code>Ctrl-D</code>, and when it finishes, the script checks the word count. If I’m still short of my daily target, it appends a <code>-----</code> separator and opens the file again. <em>You have to finish your words, whether your brain bleeds or your cat dies.</em></p>
<p>The effect? I began using it today, so I’m still not sure about the long-term impact, but there’s certainly nothing to distract me. When I used Emacs, the <code>info</code> pages were highly entertaining—I was just finishing the command-line options of GNU Backgammon when I decided to switch. Modern programs are so customizable that I can’t even finish reading their manuals.</p>
<p>The script is shown below. It asks for a filename; if you don’t provide one, it creates one based on the date. It has some minor changes from the version I use, as I tend to name my files differently and I haven’t tested this specific version thoroughly, but here it is. Copy and paste it into a file named <code>write-with-cat</code>, make it executable with <code>chmod +x write-with-cat</code>, and run it. You’ll need a Unix-like system (even my mom uses Debian, you know).</p>
<p>The second part of the script, which I’ve commented out, copies the written material to a repository and commits it using Mercurial.</p>
<pre><code class="language-bash">#!/bin/bash

WORDSIZE=600

echo "Please set a (tentative) name?"
read response
if [ -z "$response" ] ; then
    dirname=work-`date +%y-%m-%d`-$RANDOM
else
    dirname=work-${response}
fi

tempdirname="/tmp/$dirname"
mkdir $tempdirname
docname="$tempdirname/index.org"

selection=`xsel -ob`
cat &gt; $docname &lt;&lt;EOF
#+TITLE: $response
#+AUTHOR:
#+DATE: `date`

$selection
EOF

clear
cat $docname

while [ `wc -w "$docname" | cut -f 1 -d ' '` -le $WORDSIZE ] ; do
    cat &gt;&gt; $docname
    echo "\n-----" &gt;&gt; "$docname"
    wc -w "$docname"
done
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Backup Script for Recent Files</title>
      <published>2014-02-01T22:00:00+00:00</published>
      <updated>2014-02-01T22:00:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sat, 01 Feb 2014 22:00:00 +0000</pubDate>
      <link>https://emresahin.net/backup-script-for-recent-files/</link>
      <guid isPermaLink="true">https://emresahin.net/backup-script-for-recent-files/</guid>
      <description>I decided to write a script to back up only recent files. There are solutions based on unison that work periodically for all files, but as I change projects, I need to configure new backups for these projects as well. This is cumbersome and error-prone; it is easy to forget to add new artifacts t...</description>
      <category>cli</category>
      <category>tools</category>
      <category>devops</category>
      <category>backup</category>
      <category>rsync</category>
      <category>unison</category>
      <category>bash</category>
      <category>script</category>
      <category>cron</category>
      <category>linux</category>
      <category>automation</category>
      <content:encoded><![CDATA[<p>I decided to write a script to back up only recent files. There are solutions based on <a href="http://www.cis.upenn.edu/~bcpierce/unison/">unison</a> that work periodically for all files, but as I change projects, I need to configure new backups for these projects as well. This is cumbersome and error-prone; it is easy to forget to add new artifacts to backup scripts and lose them in an emergency.</p>
<p>Therefore, I decided that a small Bash script using <a href="http://rsync.samba.org/">rsync</a> and <a href="http://en.wikipedia.org/wiki/Find">find</a> would work better. It monitors my entire home directory and backs up recent files.</p>
<p>The following script does exactly that:</p>
<pre><code class="language-bash">#!/bin/bash

if [ "x$1" = "x" ] ; then
    TARGET=/media/augustus/backup-recent-`hostname`
else
    TARGET=$1
fi

PERIOD=15

mkdir -p $TARGET

# Delete files older than $PERIOD days
find $TARGET -ctime +$PERIOD -print -delete

# Copy files newer than $PERIOD under ~. Ignore files under .hg
for d in ~/*/ ; do
    find $d -path '*/.hg/*' -prune -o -type f -ctime -$PERIOD -print  -exec rsync -aRv {} $TARGET/ \;
done
</code></pre>
<p>It checks whether a command-line option is provided as the target; otherwise, it sets a default target. <code>/media/augustus/</code> is an NFS mount in my case, but you can specify any path.</p>
<p><code>PERIOD</code> is the number of days that the script considers <em>recent</em>. Currently, it backs up files changed in the last 15 days.</p>
<p>The script deletes older files from the backup. Since I use other solutions for long-term storage, I don’t want to keep them here, especially since this script runs via <code>cron</code> every two hours.</p>
<p>Note that the script only checks directories under the home directory, so files located directly in the home directory are not backed up.</p>
<p>It also skips files under <code>.hg</code> directories. You can add more <code>-prune</code> options to the <code>find</code> command to exclude other irrelevant directories from your backup.</p>]]></content:encoded>
    </item>
    <item>
      <title>Shell (Bash and Zsh) Notes</title>
      <published>2013-08-16T21:00:00+00:00</published>
      <updated>2013-08-16T21:00:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Fri, 16 Aug 2013 21:00:00 +0000</pubDate>
      <link>https://emresahin.net/shell-notes/</link>
      <guid isPermaLink="true">https://emresahin.net/shell-notes/</guid>
      <description>Don’t use ~ in scripts; use $HOME I have used ~ several times in scripts, but it may not always expand as expected depending on the environment. Use $HOME to refer to the home directory; it is much more reliable and always works.</description>
      <category>Scripting</category>
      <category>CLI</category>
      <category>shell</category>
      <category>bash</category>
      <category>zsh</category>
      <category>scripting</category>
      <category>best practices</category>
      <category>til</category>
      <content:encoded><![CDATA[<h1 id="dont-use--in-scripts-use-home">Don’t use <code>~</code> in scripts; use <code>$HOME</code></h1>
<p>I have used <code>~</code> several times in scripts, but it may not always expand as expected depending on the environment. Use <code>$HOME</code> to refer to the home directory; it is much more reliable and always works.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
