<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yoshi Nakamoto</title>
    <description>The latest articles on DEV Community by Yoshi Nakamoto (@yoshimo2o).</description>
    <link>https://dev.to/yoshimo2o</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F174599%2F6fc42ec9-260e-4f7e-a375-d08b1f6f03cf.jpg</url>
      <title>DEV Community: Yoshi Nakamoto</title>
      <link>https://dev.to/yoshimo2o</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yoshimo2o"/>
    <language>en</language>
    <item>
      <title>Pi-hole v6: Setting up wildcard domains</title>
      <dc:creator>Yoshi Nakamoto</dc:creator>
      <pubDate>Fri, 12 Sep 2025 12:57:49 +0000</pubDate>
      <link>https://dev.to/makewithyoshi/pi-hole-v6-setting-up-wildcard-domains-3pbe</link>
      <guid>https://dev.to/makewithyoshi/pi-hole-v6-setting-up-wildcard-domains-3pbe</guid>
      <description>&lt;p&gt;In my previous article, I talked about my &lt;a href="https://dev.to/yoshi_homelab/pi-hole-v6-how-to-actually-set-a-password-and-login-properly-n9k"&gt;password and login troubles on Pi-hole V6&lt;/a&gt;, it seems that this isn't the end of my journey, as I've ran into new ones while trying to set up wildcard domains on Pi-hole v6.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR version:&lt;/strong&gt; Dnsmasq config files no longer loads by default on Pi-hole v6. You will need to enable it manually via &lt;code&gt;All Settings &amp;gt; Miscellaneous &amp;gt; misc.etcdnsmasq_d&lt;/code&gt;. If you just need to insert a few lines, skip creating the config file and add it to &lt;code&gt;&amp;gt; misc.dnsmasq_line&lt;/code&gt; instead on the same page.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Creating a dnsmasq config file for wildcard domains
&lt;/h2&gt;

&lt;p&gt;Pi-hole doesn't have a way to setup wildcard domains via it's web dashboard. This can only be done manually by adding a dnsmasq configuration file under &lt;code&gt;/etc/dnsmasq.d&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You first a create a config file, e.g. &lt;code&gt;/etc/dnsmasq.d/99-myserver.com.conf&lt;/code&gt; (where the &lt;code&gt;99&lt;/code&gt; is just the order of priority if you are managing multiple configuration files), and inside that config file, just put in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server=/myserver.com/#
address=/.myserver.com/100.101.102.103
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line &lt;code&gt;server=/myserver.com/#&lt;/code&gt; prevents Pi-hole from trying to resolve &lt;code&gt;myserver.com&lt;/code&gt; using an upstream DNS.&lt;/p&gt;

&lt;p&gt;The second line &lt;code&gt;address=/.myserver.com/100.101.102.103&lt;/code&gt; tells Pi-hole that the domain &lt;code&gt;myserver.com&lt;/code&gt; + any subdomain &lt;code&gt;*.myserver.com&lt;/code&gt; should be pointed to &lt;code&gt;100.101.102.103&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pi-hole v6 doesn't load dnsmasq config file by default
&lt;/h2&gt;

&lt;p&gt;This is the part that tripped me up. In v6, dnsmasq config files are no longer loaded by default. You will need to enable it via &lt;strong&gt;All Settings &amp;gt; Miscellaneous &amp;gt; misc.etcdnsmasq_d&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkpbr9yzi5p9moqtha9qx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkpbr9yzi5p9moqtha9qx.png" alt=" " width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once enabled, you should be able to get Pi-hole to resolve the hostname to correct IP address. You can test it using &lt;code&gt;dig&lt;/code&gt; by telling it to query the hostname via your Pi-hole's IP address, the IP &lt;code&gt;192.168.1.123&lt;/code&gt; is used in the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dig abc.myserver.com @192.168.1.123 +nocomments

; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.10.6 &amp;lt;&amp;lt;&amp;gt;&amp;gt; abc.myserver.com @192.168.1.123 +nocomments
;; global options: +cmd
;abc.myserver.com.          IN  A
abc.myserver.com.       0   IN  A   100.101.102.103

;; Query time: 4 msec
;; SERVER: 192.168.1.123#53(192.168.1.123)
;; WHEN: Fri Sep 12 21:00:54 +08 2025
;; MSG SIZE  rcvd: 57
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Just a few lines? Skip creating the config file.
&lt;/h2&gt;

&lt;p&gt;It appears that on the same settings page there is also another field &lt;code&gt;misc.dnsmasq_lines&lt;/code&gt;. If you have just a few config lines, just enter it in there saves you the trouble of having jump into the terminal to create the file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzkchzxm3gciywbuf1a2z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzkchzxm3gciywbuf1a2z.png" alt=" " width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Perfect! With wildcard domains working, I can now skip having to manually assign each subdomain for each of the web services I intend to run on my Unraid server.&lt;/p&gt;

</description>
      <category>networking</category>
      <category>tutorial</category>
      <category>pihole</category>
      <category>homelab</category>
    </item>
    <item>
      <title>Pi-hole v6: How to actually set a password and login properly?</title>
      <dc:creator>Yoshi Nakamoto</dc:creator>
      <pubDate>Fri, 12 Sep 2025 12:15:06 +0000</pubDate>
      <link>https://dev.to/makewithyoshi/pi-hole-v6-how-to-actually-set-a-password-and-login-properly-n9k</link>
      <guid>https://dev.to/makewithyoshi/pi-hole-v6-how-to-actually-set-a-password-and-login-properly-n9k</guid>
      <description>&lt;p&gt;My initial experience setting up &lt;strong&gt;Pi-hole v6 on Unraid&lt;/strong&gt; was a real head-banger. I just could not log in at all.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR version:&lt;/strong&gt; Set password using &lt;code&gt;pihole setpasswd&lt;/code&gt; and login to Pi-hole's web dashboard via a hostname, not raw IP address.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What's changed with passwords on Pi-hole V6?
&lt;/h2&gt;

&lt;p&gt;I went down the password rabbit hole, trying to figure what has changed between v5 and v6, and what may or may not apply when running Pi-hole under Unraid's environment.&lt;/p&gt;

&lt;p&gt;Here are some of my learnings:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The initial randomly generated password only show up once.&lt;/strong&gt; If you've restarted the Docker instance (which I did as I was changing a few container configs), this does not show up in the logs anymore. If you've missed the boat, just skip over and set a password instead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The new command to set a password is now &lt;code&gt;pihole setpasswd&lt;/code&gt;.&lt;/strong&gt; While &lt;code&gt;pihole -a -p&lt;/code&gt; still works, &lt;code&gt;pihole setpasswd&lt;/code&gt; is the newer preferred one now. Also, when you set a password, the hash of the password is now stored in &lt;code&gt;/etc/pihole/pihole.toml&lt;/code&gt; for v6 (not &lt;code&gt;/etc/pihole/setupVars.conf&lt;/code&gt; as that is for v5).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;WEBPASSWORD&lt;/code&gt; is now &lt;code&gt;FTLCONF_webserver_api_password&lt;/code&gt;.&lt;/strong&gt; Not a secure practice (as this is visible in plain text in your docker config file), but if you decide to set a password via the Docker container's variable, &lt;code&gt;WEBPASSWORD&lt;/code&gt; no longer works, use &lt;code&gt;FTLCONF_webserver_api_password&lt;/code&gt; instead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;WEBPASSWORD_FILE&lt;/code&gt; still works.&lt;/strong&gt; You can create a file containing the password and assign the path of the file to the &lt;code&gt;WEBPASSWORD_FILE&lt;/code&gt; docker environment variable on v6. Yes, I know what you're thinking, the naming is not consistent anymore.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pointing &lt;code&gt;FTLCONF_webserver_api_password&lt;/code&gt; or &lt;code&gt;WEBPASSWORD_FILE&lt;/code&gt; to a Docker secret entry.&lt;/strong&gt; This is not for Unraid. Using Docker secrets requires you to setup Docker Compose or Docker Swarm on Unraid, which isn't standard practice on Unraid.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In short, just run &lt;code&gt;pihole setpasswd&lt;/code&gt; in the Docker terminal of your Pi-hole instance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Is the login page broken?
&lt;/h2&gt;

&lt;p&gt;I have tried everything above and I still can't login. I'd enter my password on the login page, hit enter, and... nothing. The page would just refresh. My login attempts were just being silently rejected.&lt;/p&gt;

&lt;p&gt;Upon inspecting the &lt;code&gt;/auth&lt;/code&gt; network request, I found a clue - it was returning a &lt;code&gt;no SID provided&lt;/code&gt; message in the response.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnss8pyhcce74ongwqjil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnss8pyhcce74ongwqjil.png" alt="Network inspector tool reveals no SID provided message" width="800" height="148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Taking an educated guess, SID likely means Session ID. This suggests that Pi-hole possibly refused to start an authenticated session.&lt;/p&gt;

&lt;p&gt;Does Pi-hole strictly requires HTTPS to establish a session? That doesn't make any sense. Why would a service that you're setting up for the first time require HTTPS? Accessing via the HTTP protocol is normally what folks do for initial configuration, as doing cert signing and validation upfront complicates the setup process.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pi-hole Needs a Hostname!
&lt;/h2&gt;

&lt;p&gt;In a fit of frustration, I decided to put the issue aside and set up the Nginx reverse proxy to Pi-hole, as I was planning on doing it anyway. And that's when it happened - the login magically worked!&lt;/p&gt;

&lt;p&gt;It turns out Pi-hole wants to be accessed via a hostname rather than a raw IP address. Therefore, the fix for the broken login is actually &lt;strong&gt;embarrassingly simple&lt;/strong&gt;, just add an entry in your local machine's hosts file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi-hole.lan 12.34.56.78
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesf5xwbelubkrmzkzn74.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesf5xwbelubkrmzkzn74.png" alt="Pi-hole dashboard" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;And just like that, I can finally login! It's frustrating that such a simple, non-obvious step was the cause of so much headache. Hopefully, this saves someone else from the troubles I endured.&lt;/p&gt;

</description>
      <category>pihole</category>
      <category>unraid</category>
      <category>homelab</category>
      <category>dns</category>
    </item>
    <item>
      <title>rcp &amp; rmv: copying &amp; moving using rsync with the simplicity of cp &amp; mv</title>
      <dc:creator>Yoshi Nakamoto</dc:creator>
      <pubDate>Tue, 09 Sep 2025 16:14:09 +0000</pubDate>
      <link>https://dev.to/yoshimo2o/rcp-rmv-shortcuts-for-copying-moving-using-rsync-478i</link>
      <guid>https://dev.to/yoshimo2o/rcp-rmv-shortcuts-for-copying-moving-using-rsync-478i</guid>
      <description>&lt;p&gt;These two commands &lt;code&gt;rcp&lt;/code&gt; and &lt;code&gt;rmv&lt;/code&gt; acts a direct replacement for &lt;code&gt;cp&lt;/code&gt; and &lt;code&gt;mv&lt;/code&gt; for copying or moving files using &lt;code&gt;rsync&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead of doing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-avh&lt;/span&gt; &lt;span class="nt"&gt;--partial&lt;/span&gt; &lt;span class="nt"&gt;--info&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress2 &lt;span class="nb"&gt;source &lt;/span&gt;dest
rsync &lt;span class="nt"&gt;-avh&lt;/span&gt; &lt;span class="nt"&gt;--partial&lt;/span&gt; &lt;span class="nt"&gt;--info&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress2 &lt;span class="nt"&gt;--remove-source-files&lt;/span&gt; &lt;span class="nb"&gt;source &lt;/span&gt;dest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can simply do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rcp &lt;span class="nb"&gt;source &lt;/span&gt;dest
rmv &lt;span class="nb"&gt;source &lt;/span&gt;dest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also include additional &lt;code&gt;rsync&lt;/code&gt; options, here are two examples with &lt;code&gt;-z&lt;/code&gt; for compression (great for faster network transfers) and &lt;code&gt;--dry-run&lt;/code&gt; to see what gets transferred without actually transferring (yet).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rcp -z source dest
rmv --dry-run source dest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The rcp() and rmv() shell functions
&lt;/h2&gt;

&lt;p&gt;You can copy and paste the following lines in your &lt;code&gt;.zshrc&lt;/code&gt; or &lt;code&gt;.bashrc&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Function to copy files using rsync&lt;/span&gt;
&lt;span class="c"&gt;# Usage: rcp [rsync_options] source [source2...] destination&lt;/span&gt;
rcp&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  rsync &lt;span class="nt"&gt;-avh&lt;/span&gt; &lt;span class="nt"&gt;--partial&lt;/span&gt; &lt;span class="nt"&gt;--info&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress2 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Function to move files using rsync (and delete empty directories after)&lt;/span&gt;
&lt;span class="c"&gt;# Usage: rmv [rsync_options] source [source2...] destination&lt;/span&gt;
rmv&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;default_options&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="nt"&gt;-avh&lt;/span&gt; &lt;span class="nt"&gt;--partial&lt;/span&gt; &lt;span class="nt"&gt;--info&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress2 &lt;span class="nt"&gt;--remove-source-files&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;rsync_options&lt;/span&gt;&lt;span class="o"&gt;=()&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;is_dry_run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

  &lt;span class="c"&gt;# Separate options from paths&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"$#"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"--dry-run"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"-n"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nv"&gt;is_dry_run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
      rsync_options+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"--"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;shift
      break
    &lt;/span&gt;&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"-"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;rsync_options+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else
      &lt;/span&gt;&lt;span class="nb"&gt;break
    &lt;/span&gt;&lt;span class="k"&gt;fi
    &lt;/span&gt;&lt;span class="nb"&gt;shift
  &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;

  &lt;span class="c"&gt;# Check if at least two arguments remain (sources and destination)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"$#"&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 2 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: rmv [rsync_options] source [source2...] destination"&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;1
  &lt;span class="k"&gt;fi

  &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;all_paths&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;:1:&lt;span class="nv"&gt;$#}&lt;/span&gt;&lt;span class="s2"&gt;")
  local source_paths=("&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;all_paths&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;:0:&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;all_paths&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;-1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;")
  local dest_path="&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;all_paths&lt;/span&gt;&lt;span class="p"&gt;[-1]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"

  # Execute rsync by passing options from the arrays
  rsync "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;default_options&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;rsync_options&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;source_paths&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="nv"&gt;$dest_path&lt;/span&gt;&lt;span class="s2"&gt;" &amp;amp;&amp;amp; {
    for source_dir in "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;source_paths&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"; do
      if [[ -d "&lt;/span&gt;&lt;span class="nv"&gt;$source_dir&lt;/span&gt;&lt;span class="s2"&gt;" ]]; then
        if [[ &lt;/span&gt;&lt;span class="nv"&gt;$is_dry_run&lt;/span&gt;&lt;span class="s2"&gt; -eq 0 ]]; then
          echo "&lt;/span&gt;&lt;span class="nv"&gt;Removing&lt;/span&gt;&lt;span class="p"&gt; empty directory &lt;/span&gt;&lt;span class="s1"&gt;'$source_dir'&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="s2"&gt;"
          find "&lt;/span&gt;&lt;span class="nv"&gt;$source_dir&lt;/span&gt;&lt;span class="s2"&gt;" -type d -empty -delete
        else
          echo "&lt;/span&gt;&lt;span class="nv"&gt;Dry&lt;/span&gt;&lt;span class="p"&gt; run&lt;/span&gt;:&lt;span class="p"&gt; would remove empty directory &lt;/span&gt;&lt;span class="s1"&gt;'$source_dir'&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="s2"&gt;"
          find "&lt;/span&gt;&lt;span class="nv"&gt;$source_dir&lt;/span&gt;&lt;span class="s2"&gt;" -type d -empty -print
        fi
      fi
    done
  }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  macOS users: Upgrade your rsync
&lt;/h2&gt;

&lt;p&gt;The default &lt;code&gt;rsync&lt;/code&gt; tool that comes bundled with macOS is &lt;code&gt;v2.6.9&lt;/code&gt; does not support the &lt;code&gt;--info=progress2&lt;/code&gt; command, so run &lt;code&gt;brew install rsync&lt;/code&gt; to get the latest rsync.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;/usr/bin/rsync &lt;span class="nt"&gt;--version&lt;/span&gt;
openrsync: protocol version 29
rsync version 2.6.9 compatible

&lt;span class="nv"&gt;$ &lt;/span&gt;/opt/homebrew/bin/rsync &lt;span class="nt"&gt;--version&lt;/span&gt;
rsync  version 3.4.1  protocol version 32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, add this to your &lt;code&gt;.zshrc&lt;/code&gt; or &lt;code&gt;.bashrc&lt;/code&gt; file to replace the default &lt;code&gt;rsync&lt;/code&gt; with homebrew's &lt;code&gt;rsync&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;rsync&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/homebrew/bin/rsync"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Handling the removal of empty directories
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;--remove-source-files&lt;/code&gt; option in &lt;code&gt;rsync&lt;/code&gt; does not remove empty directories after all the files has been moved. Therefore, in the &lt;code&gt;rmv()&lt;/code&gt; shell function above, you will notice that it will run additional commands to remove the empty directories using &lt;code&gt;find&lt;/code&gt; after &lt;code&gt;rsync&lt;/code&gt; has finished running.&lt;/p&gt;




&lt;p&gt;So there you have it! These two little shell functions, &lt;code&gt;rcp&lt;/code&gt; and &lt;code&gt;rmv&lt;/code&gt;, wrapping up the power of &lt;code&gt;rsync&lt;/code&gt; into something as easy to use as &lt;code&gt;cp&lt;/code&gt; and &lt;code&gt;mv&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No more remembering long strings of options, just a simple command that gets the job done, with a nice progress update to boot.&lt;/p&gt;

</description>
      <category>rsync</category>
      <category>cli</category>
      <category>bash</category>
      <category>zsh</category>
    </item>
  </channel>
</rss>
