<?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 🍃 - Tools</title>
    <link>https://emresahin.net/categories/tools/</link>
    <description>Posts in the Tools category</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/categories/tools/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>Schedule an Online Meeting with Emre Şahin</title>
      <published>2025-05-02T22:45:00+00:00</published>
      <updated>2025-05-02T22:45:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Fri, 02 May 2025 22:45:00 +0000</pubDate>
      <link>https://emresahin.net/cal/</link>
      <guid isPermaLink="true">https://emresahin.net/cal/</guid>
      <description>You can schedule an online meeting with me by clicking the button below. Please include a topic for our discussion and, if this is our first time meeting, I would appreciate a brief email introduction. Thank you. (function() { var target = document.currentScript; window.addEventListener('load', f...</description>
      <category>Personal</category>
      <category>Tools</category>
      <category>Calendar</category>
      <category>Scheduling</category>
      <category>Meetings</category>
      <content:encoded><![CDATA[<p>You can schedule an online meeting with me by clicking the button below. Please include a topic for our discussion and, if this is our first time meeting, I would appreciate a brief email introduction. Thank you.</p>
<center>
<!-- Google Calendar Appointment Scheduling begin -->
<link href="https://calendar.google.com/calendar/scheduling-button-script.css" rel="stylesheet">
<script src="https://calendar.google.com/calendar/scheduling-button-script.js" async=""></script>
<script>
(function() {
  var target = document.currentScript;
  window.addEventListener('load', function() {
    calendar.schedulingButton.load({
      url: 'https://calendar.google.com/calendar/appointments/schedules/AcZssZ1y-VrX3FRPB-LIoOArOrW-Xl_FK_xbd8zTMEwN1PPonCw6tyJ__f5CPwnqZlnsubMWpjEAIFmH?gv=true',
      color: '#039BE5',
      label: "Book an appointment with Emre",
      target,
    });
  });
})();
</script>
<!-- end Google Calendar Appointment Scheduling -->
</center>]]></content:encoded>
    </item>
    <item>
      <title>Poor man's secrets manager with pass</title>
      <published>2025-01-02T15:21:45+00:00</published>
      <updated>2025-01-02T15:21:45+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Thu, 02 Jan 2025 15:21:45 +0000</pubDate>
      <link>https://emresahin.net/poor-man-s-secrets-manager-with-pass/</link>
      <guid isPermaLink="true">https://emresahin.net/poor-man-s-secrets-manager-with-pass/</guid>
      <description>I recently restarted using pass , the standard Unix password manager, to store my passwords. At work, we use Doppler to manage our secrets. It injects environment variables before running commands. I thought, why can’t I do the same with a simple script? The following script evaluates all passwor...</description>
      <category>Security</category>
      <category>Tools</category>
      <category>pass</category>
      <category>secrets-management</category>
      <category>cli</category>
      <category>shell-script</category>
      <category>zsh</category>
      <content:encoded><![CDATA[<p>I recently restarted using <code>pass</code>, the standard Unix password manager, to store my passwords.</p>
<p>At work, we use Doppler to manage our secrets. It injects environment variables before running commands. I thought, why can’t I do the same with a simple script?</p>
<p>The following script evaluates all password files with <code>env</code> in their names. I keep my environment variables in files named <code>env-aws</code>, etc., in the following format:</p>
<pre><code class="language-bash">export AWS_ACCESS_KEY_ID="123456...."
</code></pre>
<p>The following script, which I called <code>rws</code> (short for <code>run-with-secrets</code>), allows me to run a command like <code>rws s3cmd</code> and use <code>pass</code> to inject the variables. So far, I’m happy with it.</p>
<pre><code class="language-zsh">#!/bin/zsh

cmd="$@"

fd -F env $HOME/.password-store | while read file ; do
  bb="${file:t:r}"
  eval $(pass show ${bb})
done

exec $cmd
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>bits 10</title>
      <published>2024-05-24T21:04:10+00:00</published>
      <updated>2024-05-24T21:04:10+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Fri, 24 May 2024 21:04:10 +0000</pubDate>
      <link>https://emresahin.net/bits-10/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-10/</guid>
      <description>I believe in the value of reducing friction when taking notes. Today, I added a function to my Neovim configuration to create a timestamped file and open it for editing. function CreateTimestampedFile() local zkdir = os.getenv("HOME") .. "/github.com/iesahin/garden/zk/" local timestamp = os.date(...</description>
      <category>tools</category>
      <category>productivity</category>
      <category>neovim</category>
      <category>lazyvim</category>
      <category>zettelkasten</category>
      <category>lua</category>
      <category>note-taking</category>
      <category>workflow</category>
      <content:encoded><![CDATA[<p>I believe in the value of reducing friction when taking notes. Today, I added a function to my Neovim configuration to create a timestamped file and open it for editing.</p>
<pre><code class="language-lua">function CreateTimestampedFile()
  local zkdir = os.getenv("HOME") .. "/github.com/iesahin/garden/zk/"
  local timestamp = os.date("%Y%m%d%H%M%S")
  local filename = zkdir .. "/" .. timestamp .. ".md"
  vim.cmd("edit " .. filename)
end

vim.api.nvim_set_keymap("n", "&lt;leader&gt;F", ":lua CreateTimestampedFile()&lt;CR&gt;", { noremap = true, silent = true })
</code></pre>
<p>Now, I can just hit <code>&lt;leader&gt;F</code> (which is <code>&lt;Space&gt;F</code> in my setup) and immediately start adding a note to my zettelkasten.</p>]]></content:encoded>
    </item>
    <item>
      <title>Bits 9</title>
      <published>2024-05-24T21:02:38+00:00</published>
      <updated>2024-05-24T21:02:38+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Fri, 24 May 2024 21:02:38 +0000</pubDate>
      <link>https://emresahin.net/bits-9/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-9/</guid>
      <description>I have quite a few LazyVim plugins, but this simple keymapping makes my life much easier: vim.keymap.set({ "n", "i" }, "&lt;F2&gt;", '&lt;esc&gt;&lt;esc&gt;yy:r!&lt;c-r&gt;"') I type a command, like ls , on a line and hit &lt;F2&gt; . That line is copied, executed, and the output is read into the buffer just below it. I use i...</description>
      <category>Bits</category>
      <category>Tools</category>
      <category>Neovim</category>
      <category>Shell</category>
      <category>LazyVim</category>
      <category>Productivity</category>
      <category>Workflow</category>
      <content:encoded><![CDATA[<p>I have quite a few LazyVim plugins, but this simple keymapping makes my life much easier:</p>
<pre><code class="language-lua">vim.keymap.set({ "n", "i" }, "&lt;F2&gt;", '&lt;esc&gt;&lt;esc&gt;yy:r!&lt;c-r&gt;"')
</code></pre>
<p>I type a command, like <code>ls</code>, on a line and hit <code>&lt;F2&gt;</code>. That line is copied, executed, and the output is read into the buffer just below it.</p>
<p>I use it for all sorts of tasks, from listing open PRs with <code>gh</code> to fetching website text with <code>w3m</code>. It has significantly improved my workflow, allowing me to use the shell effectively within textual notebooks.</p>]]></content:encoded>
    </item>
    <item>
      <title>Shell function to open URLs in Kagi Summarizer</title>
      <published>2024-04-30T20:07:00+00:00</published>
      <updated>2024-04-30T20:07:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Tue, 30 Apr 2024 20:07:00 +0000</pubDate>
      <link>https://emresahin.net/shell-function-to-open-urls-in-kagi-summarizer/</link>
      <guid isPermaLink="true">https://emresahin.net/shell-function-to-open-urls-in-kagi-summarizer/</guid>
      <description>I’m a fan and an avid user of Kagi Search. I’m an Ultimate plan subscriber and use their Summarizer frequently. I needed a way to quickly open URLs in the Kagi Summarizer from my terminal. I wrote a small shell function to do that: ksum() { local urlenc=$(jq -rn --arg x "$1" '$x|@uri') open "http...</description>
      <category>Tools</category>
      <category>shell</category>
      <category>kagi</category>
      <category>search</category>
      <category>summarizer</category>
      <category>zsh</category>
      <category>automation</category>
      <content:encoded><![CDATA[<p>I’m a fan and an avid user of Kagi Search. I’m an Ultimate plan subscriber and use their Summarizer frequently.</p>
<p>I needed a way to quickly open URLs in the Kagi Summarizer from my terminal. I wrote a small shell function to do that:</p>
<pre><code class="language-zsh">ksum() {
  local urlenc=$(jq -rn --arg x "$1" '$x|@uri')
  open "https://kagi.com/summarizer/index.html?target_language=&amp;summary=takeaway&amp;url=${urlenc}"
}
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>airmux</title>
      <published>2024-01-25T09:01:54+00:00</published>
      <updated>2024-01-25T09:01:54+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Thu, 25 Jan 2024 09:01:54 +0000</pubDate>
      <link>https://emresahin.net/airmux/</link>
      <guid isPermaLink="true">https://emresahin.net/airmux/</guid>
      <description>Airmux is a session manager for tmux that allows defining and managing multiple tmux sessions through project configuration files. Projects are defined in YAML or JSON files that specify settings such as commands to run, window/pane layouts, and other session settings. The start command loads and...</description>
      <category>tools</category>
      <category>tools</category>
      <category>tmux</category>
      <category>cli</category>
      <category>rust</category>
      <category>airmux</category>
      <category>terminal</category>
      <content:encoded><![CDATA[<ul>
<li><a href="https://github.com/dermoumi/airmux">Airmux</a> is a session manager for tmux that allows defining and managing multiple tmux sessions through project configuration files.</li>
<li>Projects are defined in YAML or JSON files that specify settings such as commands to run, window/pane layouts, and other session settings.</li>
<li>The <code>start</code> command loads and attaches to an existing project, while <code>edit</code> creates or modifies project files.</li>
<li>Projects support environment variable expansion, and command-line arguments passed to <code>start</code> are available as <code>$1</code>, <code>$2</code>, etc., in the project file.</li>
<li>Local project files can be used without specifying a name if they are found by searching parent directories.</li>
<li>Commands like <code>list</code>, <code>remove</code>, and <code>freeze</code> can manage existing projects.</li>
<li><code>Debug</code> prints the tmux commands to create a session without attaching, which is useful for troubleshooting.</li>
<li>Fields like <code>on_start</code> and <code>on_stop</code> allow running commands before or after session startup/shutdown.</li>
<li>Windows and panes have their own settings, such as layouts and commands to run, and they can be nested in the project file.</li>
<li>Airmux aims to provide an easy and organized way to define and manage multiple related tmux sessions through project files.</li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>Activate left window when the current one is closed in tmux</title>
      <published>2022-02-16T07:08:25+00:00</published>
      <updated>2022-02-16T07:08:25+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Wed, 16 Feb 2022 07:08:25 +0000</pubDate>
      <link>https://emresahin.net/Activate-left-window-when-the-current-one-is-closed-in-tmux/</link>
      <guid isPermaLink="true">https://emresahin.net/Activate-left-window-when-the-current-one-is-closed-in-tmux/</guid>
      <description>When I close a window in tmux, it typically activates the window to its right. I prefer the previously active window to be activated instead. I couldn’t find a direct setting for this, but tmux provides hooks for various events. By setting the window-unlinked hook to trigger last-window , I can a...</description>
      <category>CLI</category>
      <category>Tools</category>
      <category>tmux</category>
      <category>configuration</category>
      <category>hooks</category>
      <category>productivity</category>
      <content:encoded><![CDATA[<p>When I close a window in tmux, it typically activates the window to its right. I
prefer the previously active window to be activated instead. I couldn’t find a
direct setting for this, but tmux provides <a href="https://man7.org/linux/man-pages/man1/tmux.1.html#HOOKS">hooks</a>
for various events. By setting the <code>window-unlinked</code> hook to trigger <code>last-window</code>,
I can achieve the desired behavior.</p>
<p>Add the following to your <code>.tmux.conf</code>:</p>
<pre><code class="language-conf">set-hook -g window-unlinked 'last-window'
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Configuring Vim Undotree</title>
      <published>2021-01-02T16:16:59+00:00</published>
      <updated>2021-01-02T16:16:59+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sat, 02 Jan 2021 16:16:59 +0000</pubDate>
      <link>https://emresahin.net/configuring-vim-undotree/</link>
      <guid isPermaLink="true">https://emresahin.net/configuring-vim-undotree/</guid>
      <description>To enable persistent undo and configure the undotree plugin, add the following settings to your vimrc file: " Save undo trees in files set undofile set undodir=~/.vim/undo " Number of undo levels saved set undolevels=10000 For more information on intermediate Vim usage, see this guide .</description>
      <category>Vim</category>
      <category>Tools</category>
      <category>Vim</category>
      <category>Undotree</category>
      <category>Productivity</category>
      <category>Configuration</category>
      <category>Vimrc</category>
      <content:encoded><![CDATA[<p>To enable persistent undo and configure the <a href="https://github.com/mbbill/undotree">undotree</a> plugin, add the following settings to your <code>vimrc</code> file:</p>
<pre><code class="language-vim">" Save undo trees in files
set undofile
set undodir=~/.vim/undo

" Number of undo levels saved
set undolevels=10000
</code></pre>
<p>For more information on intermediate Vim usage, see <a href="https://thevaluable.dev/vim-intermediate/">this guide</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Literate Programming with Markdown</title>
      <published>2020-06-11T16:29:14+00:00</published>
      <updated>2020-06-11T16:29:14+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Thu, 11 Jun 2020 16:29:14 +0000</pubDate>
      <link>https://emresahin.net/til-june-11--24126/</link>
      <guid isPermaLink="true">https://emresahin.net/til-june-11--24126/</guid>
      <description>I return to my Ottoman language project from time to time, and most of the time I spend is spent trying to understand what I did previously. I decided that the actual parsing and simple morphological analysis modules should use Literate Programming with Markdown. I am currently using Dart to rewr...</description>
      <category>TIL</category>
      <category>tools</category>
      <category>ottoman</category>
      <category>markdown</category>
      <category>literate-programming</category>
      <category>dart</category>
      <category>knot</category>
      <content:encoded><![CDATA[<p>I return to my Ottoman language project from time to time, and most of the time I spend is spent trying to understand what I did previously. I decided that the actual parsing and simple morphological analysis modules should use <a href="https://en.wikipedia.org/wiki/Literate_programming">Literate Programming</a> with Markdown.</p>
<p>I am currently using <a href="https://dartlang.org">Dart</a> to rewrite the entire application, which is fundamentally simple. It receives words in Turkish or Ottoman and translates them to Ottoman or Turkish. However, the parsing module needs more rules to divide the words (and phrases) into roots and suffixes. There are hundreds of rules, and using only <em>code</em> to describe the reasoning behind parsing seems to be the main reason I spend so much time trying to understand my own work.</p>
<p>I decided that I can just use <a href="https://github.com/mqsoh/knot/blob/master/Dockerfile">knot</a> to write the rules in a document and generate the code from it. It will surely take more time than just writing the code, but in the end, I will have a document describing the analysis and parsing rules that even I can understand.</p>]]></content:encoded>
    </item>
    <item>
      <title>telegram-send</title>
      <published>2020-05-12T18:47:24+00:00</published>
      <updated>2020-05-12T21:47:34+03:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Tue, 12 May 2020 18:47:24 +0000</pubDate>
      <link>https://emresahin.net/telegram-send-6932/</link>
      <guid isPermaLink="true">https://emresahin.net/telegram-send-6932/</guid>
      <description>A little Python CLI app for Telegram messages</description>
      <category>CLI</category>
      <category>Telegram</category>
      <category>Tools</category>
      <category>Python</category>
      <category>Automation</category>
      <category>Messaging</category>
      <category>Bot</category>
      <content:encoded><![CDATA[<p>There is a small Python command-line program called <code>telegram-send</code> that allows you to send messages to your
Telegram account.</p>
<p>First, you need to register a new bot with <a href="https://t.me/BotFather">@BotFather</a> and get an API token. Then,
run <code>pip3 install --user telegram-send</code> and prepare a config file at <code>~/.config/telegram-send.conf</code>:</p>
<pre><code class="language-ini">[telegram]
token = &lt;TOKEN_YOU_GET_FROM_BOT_FATHER&gt;
chat_id = &lt;CHAT_OR_USER_ID&gt;
</code></pre>
<p>You’ll need to start a conversation with the bot and find your user ID (which is identical to the chat
ID for the conversation you start with the bot).</p>
<p>After that, you can send yourself messages from the CLI like this:</p>
<pre><code class="language-bash">telegram-send "Hello Telegram."
</code></pre>
<p>You can also send Markdown-formatted messages, audio, stickers, and more using various command-line options:</p>
<pre><code class="language-text">usage: telegram-send [-h] [--format {text,markdown,html}] [--stdin] [--pre]
                     [--disable-web-page-preview] [--silent] [-c]
                     [--configure-channel] [--configure-group]
                     [-f FILE [FILE ...]] [-i IMAGE [IMAGE ...]]
                     [-s STICKER [STICKER ...]]
                     [--animation ANIMATION [ANIMATION ...]]
                     [--video VIDEO [VIDEO ...]] [--audio AUDIO [AUDIO ...]]
                     [-l LOCATION [LOCATION ...]]
                     [--caption CAPTION [CAPTION ...]] [--config CONF] [-g]
                     [--file-manager] [--clean] [--timeout TIMEOUT]
                     [--version]
                     [message [message ...]]
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>aerc and goneovim</title>
      <published>2020-05-12T18:47:03+00:00</published>
      <updated>2020-05-12T18:47:03+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Tue, 12 May 2020 18:47:03 +0000</pubDate>
      <link>https://emresahin.net/aerc-and-goneovim--3894/</link>
      <guid isPermaLink="true">https://emresahin.net/aerc-and-goneovim--3894/</guid>
      <description>aerc I’ve started using aerc as my command-line email client. Initially, I used its archived GitHub repository, but I found the software to be buggy. Switching to the active repository provided me with the fastest IMAP client I’ve ever used. Its asynchronous operations make the workflow incredibl...</description>
      <category>development</category>
      <category>tools</category>
      <category>aerc</category>
      <category>neovim</category>
      <category>goneovim</category>
      <category>hugo</category>
      <category>miniflux</category>
      <category>Go</category>
      <category>golang</category>
      <category>productivity</category>
      <content:encoded><![CDATA[<h2 id="aerc">aerc</h2>
<p>I’ve started using <a href="https://aerc-mail.org/">aerc</a> as my command-line email client. Initially, I used its archived GitHub repository, but I found the software to be buggy. Switching to the active repository provided me with the fastest IMAP client I’ve ever used. Its asynchronous operations make the workflow incredibly smooth, as you don’t have to wait for the server after deleting or archiving each email.</p>
<h2 id="goneovim">goneovim</h2>
<p>I’ve also begun using a Neovim GUI called <a href="https://github.com/akiyosi/goneovim">goneovim</a>. I previously used <a href="https://github.com/daa84/neovim-gtk">Neovim-GTK</a>, but encountered issues—possibly due to Fira Code’s ligatures or other incompatibilities—that resulted in poor visuals and noticeable lag. I’m giving this new GUI a try, and so far, Fira Code and its ligatures are working perfectly.</p>
<p>Software written in Go is occupying more mindshare than I previously anticipated. Alongside <a href="https://gohugo.io">Hugo</a>, the <a href="https://miniflux.app/">Miniflux</a> self-hosted RSS client, and the <a href="https://github.com/vkuznecovas/mouthful">Mouthful</a> comment server, these tools demonstrate that Go has truly taken off for system and performance-related tasks.</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>
  </channel>
</rss>
