<?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: dpm_bush</title>
    <description>The latest articles on DEV Community by dpm_bush (@__3381495fd2b).</description>
    <link>https://dev.to/__3381495fd2b</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4107233%2Fb8ff0436-9096-443b-b82c-de3dce98950a.png</url>
      <title>DEV Community: dpm_bush</title>
      <link>https://dev.to/__3381495fd2b</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/__3381495fd2b"/>
    <language>en</language>
    <item>
      <title>Choosing a WinSCP Alternative: Start With the Job, Not the App</title>
      <dc:creator>dpm_bush</dc:creator>
      <pubDate>Thu, 15 Oct 2026 05:15:24 +0000</pubDate>
      <link>https://dev.to/__3381495fd2b/choosing-a-winscp-alternative-start-with-the-job-not-the-app-30mm</link>
      <guid>https://dev.to/__3381495fd2b/choosing-a-winscp-alternative-start-with-the-job-not-the-app-30mm</guid>
      <description>&lt;p&gt;WinSCP already does a lot: SFTP, SCP, FTP/FTPS, WebDAV and S3, plus directory synchronization and Windows scripting. If you’re looking for an alternative, the useful question isn’t “Which app is best?” It’s “Which part of my workflow does WinSCP no longer fit?”&lt;/p&gt;

&lt;p&gt;That distinction matters. A tool that works on macOS may not offer WinSCP’s automation, and an SSH terminal with a file browser may not replace a dedicated transfer client.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, identify the task you need to replace
&lt;/h2&gt;

&lt;p&gt;List what you actually use before comparing apps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Moving files manually:&lt;/strong&gt; Do you need a two-pane browser, transfer queue, or support for several protocols?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connecting from another operating system:&lt;/strong&gt; Are you moving to macOS or Linux, where WinSCP doesn’t offer a native desktop app?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Working with cloud storage:&lt;/strong&gt; Do you need S3, Google Drive, OneDrive, or another provider?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Administering a server:&lt;/strong&gt; Do you want a terminal and file browser together, or are you happy using separate tools?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automating transfers:&lt;/strong&gt; Do existing scripts, scheduled jobs, or .NET integrations depend on WinSCP?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mounting remote files:&lt;/strong&gt; Do you want remote storage to appear in Finder or File Explorer, rather than opening a transfer client?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are different needs. A mount tool, a terminal emulator, and an SFTP client aren’t interchangeable just because each can access remote files.&lt;/p&gt;

&lt;p&gt;It also helps to check the protocol requirement. &lt;strong&gt;SFTP runs over SSH; FTPS is FTP secured with TLS.&lt;/strong&gt; They’re distinct protocols, so a client supporting one doesn’t necessarily support the other. If you’re weighing transfer options, this &lt;a href="https://sshflow.com/blog/best-sftp-clients/" rel="noopener noreferrer"&gt;SFTP client comparison&lt;/a&gt; can help you narrow the category before picking an app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shortlist by operating system and workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  If you want a free, cross-platform file-transfer client
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;FileZilla Client&lt;/strong&gt; is a straightforward starting point for FTP, FTPS, and SFTP on Windows, macOS, and Linux. Its standard client is free. Some cloud integrations are part of FileZilla Pro, so check which edition supports the services you need.&lt;/p&gt;

&lt;p&gt;It’s a practical choice when your core job is browsing local and remote files and transferring them. It isn’t a one-for-one substitute for WinSCP’s Windows scripting and .NET assembly.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you need SFTP plus cloud storage
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cyberduck&lt;/strong&gt; is available for Windows and macOS and supports SFTP alongside a range of cloud storage services. The base app is free and open source. It’s a file and storage browser, not an interactive SSH terminal, and it doesn’t have a native Linux desktop release.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you’re on macOS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ForkLift&lt;/strong&gt; offers a Mac-native, two-pane file-manager workflow with remote connections and cloud services. &lt;strong&gt;Transmit&lt;/strong&gt; is another paid Mac option focused on transfers and synchronization. They’re not Linux or Windows replacements, and their licensing differs. Check current platform requirements and terms before buying.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you want a Windows SSH terminal and graphical SFTP
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bitvise SSH Client&lt;/strong&gt; combines SSH terminal and graphical SFTP workflows on Windows. It’s a reasonable option if you’ve been using WinSCP alongside a separate terminal client.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MobaXterm&lt;/strong&gt; is a broader Windows toolbox that includes SSH, SFTP, and tools for other remote-access workflows such as X11. Its free Home edition has limits; check the current edition details if saved sessions, tunnels, or macros matter to you.&lt;/p&gt;

&lt;h3&gt;
  
  
  If automation is the priority
&lt;/h3&gt;

&lt;p&gt;Don’t switch away from WinSCP just because a different client has a newer interface. WinSCP’s scripting interface and .NET assembly can be important parts of a deployment or backup process. A graphical alternative may not run those jobs, and another command-line tool won’t automatically understand existing WinSCP scripts.&lt;/p&gt;

&lt;p&gt;For cross-platform transfer automation, &lt;strong&gt;rclone&lt;/strong&gt; is worth evaluating. It supports SFTP and many cloud backends, but it’s a command-line tool rather than a two-pane file manager. Be especially careful with synchronization: &lt;code&gt;rclone sync&lt;/code&gt; can delete files at the destination to make it match the source. Test paths and use a dry run before relying on a new job.&lt;/p&gt;

&lt;p&gt;Also distinguish SCP from SFTP when migrating. WinSCP supports both, but many modern workflows use SFTP; this explanation of &lt;a href="https://sshflow.com/blog/scp-vs-sftp/" rel="noopener noreferrer"&gt;how SCP and SFTP differ&lt;/a&gt; is useful if an old script or server specifically requires SCP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrate without breaking a working setup
&lt;/h2&gt;

&lt;p&gt;Treat a client migration like a small infrastructure change—not just an app install.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Inventory your saved sites.&lt;/strong&gt; Record hostnames, ports, usernames, protocols, key locations, proxy or jump-host settings, and default remote directories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find every automation dependency.&lt;/strong&gt; Search for scheduled tasks, scripts, and applications that invoke WinSCP. A site you rarely open manually may still be used by a nightly job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recreate connections using supported import tools.&lt;/strong&gt; Don’t assume a destination client can import WinSCP’s settings. An export file can also contain sensitive connection details, so protect it accordingly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify the server’s identity.&lt;/strong&gt; When the new client first connects, check the host-key fingerprint through a trusted channel. Don’t accept a changed or unfamiliar key just to get past a warning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test a harmless transfer.&lt;/strong&gt; Confirm the remote directory, permissions, file contents, and transfer direction before editing production files or changing a sync job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep a rollback path.&lt;/strong&gt; Retain the old configuration and scripts until the replacement has passed the real tasks you depend on.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A practical decision rule
&lt;/h2&gt;

&lt;p&gt;If you use Windows and mainly need reliable file transfers or mature WinSCP scripts, staying with WinSCP may be the simplest choice. If you need a native Mac or Linux client, start with a cross-platform transfer tool. If you want a Windows terminal alongside graphical SFTP, evaluate an SSH client that includes both. If repeatable automation is the job, test the replacement’s command-line behavior before migrating production tasks.&lt;/p&gt;

&lt;p&gt;The best alternative is the one that covers the specific gap without quietly removing a feature you rely on. If WinSCP still handles your workflow, there’s no technical prize for replacing it.&lt;/p&gt;

&lt;p&gt;I originally published a more detailed version of this guide on the &lt;a href="https://sshflow.com/blog/best-winscp-alternatives/" rel="noopener noreferrer"&gt;SSHFlow blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also building &lt;a href="https://sshflow.com/" rel="noopener noreferrer"&gt;SSHFlow&lt;/a&gt; — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>tutorial</category>
      <category>devops</category>
    </item>
    <item>
      <title>Choosing a Termius Alternative: Match the Client to Your SSH Workflow</title>
      <dc:creator>dpm_bush</dc:creator>
      <pubDate>Thu, 15 Oct 2026 04:31:04 +0000</pubDate>
      <link>https://dev.to/__3381495fd2b/choosing-a-termius-alternative-match-the-client-to-your-ssh-workflow-59ek</link>
      <guid>https://dev.to/__3381495fd2b/choosing-a-termius-alternative-match-the-client-to-your-ssh-workflow-59ek</guid>
      <description>&lt;p&gt;Replacing an SSH client is easy to regret if you compare only terminal screenshots. The real question is which parts of your workflow you need to keep: mobile access, saved hosts, graphical file transfers, port forwarding, or tools for working on the server after you connect.&lt;/p&gt;

&lt;p&gt;Termius already has a free Starter plan with SSH, SFTP, and port forwarding. Its paid plans add features such as a personal cloud vault and device synchronization. If those free features are all you use, switching may not solve a problem. If you rely on synced hosts or mobile access, a desktop-only alternative may be a downgrade even if it costs less.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the job you need the client to do
&lt;/h2&gt;

&lt;p&gt;Write down what you actually do in a typical session. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect to a few servers from one Windows machine.&lt;/li&gt;
&lt;li&gt;Open several terminal sessions and keep them organized.&lt;/li&gt;
&lt;li&gt;Upload files through a graphical SFTP browser.&lt;/li&gt;
&lt;li&gt;Forward a port to reach a service on a remote server.&lt;/li&gt;
&lt;li&gt;Connect from a phone when away from your computer.&lt;/li&gt;
&lt;li&gt;Share hosts or credentials with a team.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These needs point to different tools. “Supports SSH” is only a starting point: it does not tell you whether an app has graphical SFTP, a mobile app, team controls, or a convenient way to manage tunnels.&lt;/p&gt;

&lt;p&gt;A useful distinction: &lt;strong&gt;SFTP support is not the same as a graphical SFTP file manager.&lt;/strong&gt; OpenSSH provides command-line tools such as &lt;code&gt;sftp&lt;/code&gt;; a client with a visual file browser is a better fit if you want to drag and drop files. Similarly, SSH port forwarding carries traffic through an SSH connection; it is not the same thing as an embedded remote-desktop client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Match the alternative to your setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  You want a free desktop terminal
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tabby&lt;/strong&gt; is a free, open-source terminal for Windows, macOS, and Linux. It offers SSH sessions, tabs, split panes, and customization through plugins. It is a reasonable choice if you want a configurable desktop workflow and do not need Termius-style synced hosts across desktop and mobile devices.&lt;/p&gt;

&lt;p&gt;Tabby is based on Electron, and its maintainers do not describe it as lightweight. Choose it for its features and customization rather than assuming it will use fewer resources than another client.&lt;/p&gt;

&lt;h3&gt;
  
  
  You need SSH, graphical SFTP, and tunnels on Windows
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bitvise SSH Client&lt;/strong&gt; is a free Windows option with SSH, a graphical SFTP interface, and port-forwarding features. The client is free for use in business environments too; its optional paid license concerns support and upgrade commitments. Bitvise SSH Server is separate software, so do not confuse the client’s terms with the server’s.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MobaXterm&lt;/strong&gt; may fit if your Windows toolbox also needs RDP, VNC, or an X server for displaying remote Linux GUI applications. Its free Home edition has limits, so check the current edition terms before adopting it for a team or a larger set of sessions.&lt;/p&gt;

&lt;h3&gt;
  
  
  You mostly transfer files
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;WinSCP&lt;/strong&gt; is designed around file transfers rather than being a full SSH terminal manager. It supports graphical transfers, scripting, and directory synchronization. If you mainly connect to upload releases or manage remote files, that focus may be an advantage. If you also need interactive terminal sessions, pair it with an SSH client or choose a tool that combines both workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  You want a minimal, command-line setup
&lt;/h3&gt;

&lt;p&gt;Windows includes OpenSSH as an optional feature, and it is commonly available on Linux and macOS. You can connect directly from a terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh user@server.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For repeated connections, a host entry in your SSH config can save the hostname, user, port, and identity file. The &lt;a href="https://sshflow.com/blog/ssh-config-example/" rel="noopener noreferrer"&gt;SSH config file examples&lt;/a&gt; show how to organize those settings and connect with a short alias. OpenSSH is a good fit if you prefer ordinary text configuration over a graphical host manager; it does not provide a graphical SFTP browser by itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat migration as a security task
&lt;/h2&gt;

&lt;p&gt;A saved host entry, a private key, an agent-loaded identity, and a team vault are different things. Do not assume one client can import all of them automatically. Record the host, username, port, key choice, jump-host settings, and any tunnels you depend on. Then test a few important connections in the new client before changing your existing setup.&lt;/p&gt;

&lt;p&gt;Keep private keys private. Do not paste them into chat, tickets, or repositories to make migration easier. If you are moving or replacing keys, plan access and revocation deliberately; this overview of &lt;a href="https://sshflow.com/blog/ssh-key-management/" rel="noopener noreferrer"&gt;SSH key management&lt;/a&gt; covers the key lifecycle and the risks of leaving stale credentials in place.&lt;/p&gt;

&lt;p&gt;Also verify server identity when connecting through a new client. If it warns that a host key has changed unexpectedly, investigate the change rather than accepting it just to finish setup. A successful password or key login does not, on its own, establish that you reached the intended server.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical decision checklist
&lt;/h2&gt;

&lt;p&gt;Before settling on an alternative, test it with a non-critical host and answer these questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can it use the authentication method and key you need?&lt;/li&gt;
&lt;li&gt;Does it provide a graphical SFTP interface if you want one?&lt;/li&gt;
&lt;li&gt;Can it reproduce your jump-host and port-forwarding workflow?&lt;/li&gt;
&lt;li&gt;Does it run on every device you use, including your phone if necessary?&lt;/li&gt;
&lt;li&gt;Are host sync and team sharing included, and do they meet your security requirements?&lt;/li&gt;
&lt;li&gt;Can you get your configuration and credentials back if you lose access to the app or device?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you only need occasional terminal access, OpenSSH or PuTTY may be enough. For free Windows SSH with graphical file transfer and tunneling, consider Bitvise. For a customizable cross-platform terminal, try Tabby. For mobile access and synchronized hosts, check that the replacement actually offers those capabilities before leaving Termius.&lt;/p&gt;

&lt;p&gt;The best alternative is not the one with the longest feature list. It is the one that preserves the parts of your SSH workflow you rely on—and does not make you pay for, configure, or trust features you do not need.&lt;/p&gt;

&lt;p&gt;I originally published a more detailed version of this guide on the &lt;a href="https://sshflow.com/blog/termius-alternative/" rel="noopener noreferrer"&gt;SSHFlow blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also building &lt;a href="https://sshflow.com/" rel="noopener noreferrer"&gt;SSHFlow&lt;/a&gt; — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>devops</category>
    </item>
    <item>
      <title>Use the Linux tree Command to Understand a Project’s Structure</title>
      <dc:creator>dpm_bush</dc:creator>
      <pubDate>Wed, 14 Oct 2026 00:35:07 +0000</pubDate>
      <link>https://dev.to/__3381495fd2b/use-the-linux-tree-command-to-understand-a-projects-structure-2nb3</link>
      <guid>https://dev.to/__3381495fd2b/use-the-linux-tree-command-to-understand-a-projects-structure-2nb3</guid>
      <description>&lt;p&gt;&lt;code&gt;ls&lt;/code&gt; is useful when you need to see what’s in one directory. But when you need to understand how a project’s folders fit together, a flat listing can hide the structure. The &lt;code&gt;tree&lt;/code&gt; command displays files and directories as a branching outline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get a focused overview
&lt;/h2&gt;

&lt;p&gt;Run &lt;code&gt;tree&lt;/code&gt; with no arguments to start at your current directory, or pass a path to inspect a specific folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tree
tree ./project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A small project might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project
├── README.md
├── src
│   └── app.py
└── tests
    └── test_app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The branches make the parent-child relationships visible: &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;tests&lt;/code&gt; are inside &lt;code&gt;project&lt;/code&gt;, and each file appears beneath its parent directory.&lt;/p&gt;

&lt;p&gt;A full recursive listing can quickly become unwieldy, especially in a repository with generated files or dependencies. Use &lt;code&gt;-L&lt;/code&gt; to limit how many levels are shown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tree &lt;span class="nt"&gt;-L&lt;/span&gt; 2 ./project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives you a useful first pass without expanding every nested folder. If the result is still noisy, start from a narrower path or combine the depth limit with filters below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install it if the command is missing
&lt;/h2&gt;

&lt;p&gt;Check whether &lt;code&gt;tree&lt;/code&gt; is available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tree &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your shell reports that the command cannot be found, install the &lt;code&gt;tree&lt;/code&gt; package using the package manager for your distribution:&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;# Ubuntu or Debian&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;tree

&lt;span class="c"&gt;# Fedora&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;tree

&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; tree

&lt;span class="c"&gt;# openSUSE&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;zypper &lt;span class="nb"&gt;install &lt;/span&gt;tree
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands install software system-wide and typically require administrator privileges. Afterward, verify the command with &lt;code&gt;tree --version&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shape the output around your task
&lt;/h2&gt;

&lt;p&gt;A few options make the listing more useful without changing anything on disk:&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;# Show directories, not files&lt;/span&gt;
tree &lt;span class="nt"&gt;-d&lt;/span&gt; ./project

&lt;span class="c"&gt;# Include hidden entries, such as .git&lt;/span&gt;
tree &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; 2 ./project

&lt;span class="c"&gt;# Skip bulky or irrelevant directories&lt;/span&gt;
tree &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s1"&gt;'node_modules|.git'&lt;/span&gt; ./project

&lt;span class="c"&gt;# Show matching filenames while keeping their location in the tree&lt;/span&gt;
tree &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s1"&gt;'*.conf'&lt;/span&gt; ./project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;-d&lt;/code&gt; for a quick map of the folder layout. Add &lt;code&gt;-a&lt;/code&gt; when hidden files matter; these are omitted by default. For a large repository, &lt;code&gt;-I&lt;/code&gt; can leave out names you don’t want to inspect. &lt;code&gt;-P&lt;/code&gt; selects names matching a pattern.&lt;/p&gt;

&lt;p&gt;Quote wildcard patterns such as &lt;code&gt;'*.conf'&lt;/code&gt;. The quotes stop your shell from expanding the wildcard before &lt;code&gt;tree&lt;/code&gt; receives it. In the ignore example, patterns are separated with &lt;code&gt;|&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can also show extra file details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tree &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; ./project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This adds permissions, human-readable sizes, and modification dates to the listing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose the right listing tool
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;tree&lt;/code&gt; when the relationships between nested folders matter. For a quick view of just the current directory, &lt;a href="https://sshflow.com/blog/ls-command-in-linux/" rel="noopener noreferrer"&gt;&lt;code&gt;ls&lt;/code&gt; has options for hidden files, long-format details, and sorting&lt;/a&gt;. If you need to locate files by conditions rather than browse the hierarchy, &lt;a href="https://sshflow.com/blog/linux-find-command/" rel="noopener noreferrer"&gt;&lt;code&gt;find&lt;/code&gt; can search by name, type, size, and other criteria&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tree&lt;/code&gt; is also different from &lt;code&gt;pstree&lt;/code&gt;: the former shows files and directories, while &lt;code&gt;pstree&lt;/code&gt; shows running processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Save a listing to share or review later
&lt;/h2&gt;

&lt;p&gt;Redirect the output to a text file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tree &lt;span class="nt"&gt;-L&lt;/span&gt; 2 ./project &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; project-tree.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;gt;&lt;/code&gt; operator creates the file or replaces its existing contents. To append instead, use &lt;code&gt;&amp;gt;&amp;gt;&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;tree &lt;span class="nt"&gt;-L&lt;/span&gt; 2 ./project &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; project-tree.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don’t want the report to appear in the listing, save it outside the directory you’re inspecting. Be careful with &lt;code&gt;&amp;gt;&lt;/code&gt; if the destination file already contains something you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few common surprises
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The output is too long:&lt;/strong&gt; specify the directory you care about and use &lt;code&gt;-L&lt;/code&gt; to cap the depth. Try &lt;code&gt;-d&lt;/code&gt; for a directory-only view or &lt;code&gt;-I&lt;/code&gt; to skip noisy folders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Some entries are missing:&lt;/strong&gt; hidden names are excluded unless you use &lt;code&gt;-a&lt;/code&gt;. Entries can also be inaccessible if your user cannot read the relevant directories. Check permissions on the path in question rather than automatically running the command with elevated privileges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A filter doesn’t behave as expected:&lt;/strong&gt; &lt;code&gt;-P&lt;/code&gt; selects matching names; &lt;code&gt;-I&lt;/code&gt; ignores matching names. Quote patterns so the shell passes them to &lt;code&gt;tree&lt;/code&gt; unchanged. These filters affect the display, not the files themselves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The command is installed but unavailable:&lt;/strong&gt; you might be in a different system or container than expected, or its executable may not be on your shell’s &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For most project inspections, start small: pick the directory, add a depth limit, then include hidden entries or filters only if the task calls for them. That produces a readable map without turning a quick check into a scroll through every file.&lt;/p&gt;

&lt;p&gt;I originally published a more detailed version of this guide on the &lt;a href="https://sshflow.com/blog/linux-tree-command/" rel="noopener noreferrer"&gt;SSHFlow blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also building &lt;a href="https://sshflow.com/" rel="noopener noreferrer"&gt;SSHFlow&lt;/a&gt; — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>devops</category>
    </item>
    <item>
      <title>Using echo in Linux: Quoting, Redirection, and When to Use printf</title>
      <dc:creator>dpm_bush</dc:creator>
      <pubDate>Wed, 14 Oct 2026 00:34:48 +0000</pubDate>
      <link>https://dev.to/__3381495fd2b/using-echo-in-linux-quoting-redirection-and-when-to-use-printf-3gc1</link>
      <guid>https://dev.to/__3381495fd2b/using-echo-in-linux-quoting-redirection-and-when-to-use-printf-3gc1</guid>
      <description>&lt;p&gt;&lt;code&gt;echo&lt;/code&gt; is handy for printing a quick message or checking a variable, but its behavior can be less predictable when you rely on options or backslash escapes. Knowing where the shell fits in—and when to switch to &lt;code&gt;printf&lt;/code&gt;—helps avoid confusing output in scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shell processes text before echo sees it
&lt;/h2&gt;

&lt;p&gt;A basic command prints its arguments to standard output, usually your terminal, followed by a newline:&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;echo &lt;/span&gt;Hello, Linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello, Linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The shell handles quoting and variable expansion before it runs &lt;code&gt;echo&lt;/code&gt;. That means &lt;code&gt;echo&lt;/code&gt; displays the value of a variable; it doesn't look up the variable itself.&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;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'$HOME'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command prints the value of &lt;code&gt;HOME&lt;/code&gt;. Double quotes allow the shell to expand the variable while keeping its value together as one argument, including if it contains spaces. Single quotes prevent expansion, so the second command prints the literal text &lt;code&gt;$HOME&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Double quotes also allow command substitution. The shell runs &lt;code&gt;date&lt;/code&gt;, substitutes its output, and passes the resulting text to &lt;code&gt;echo&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;echo&lt;/span&gt; &lt;span class="s2"&gt;"Today is &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more on making shell variables available to commands launched from the shell, see this guide to &lt;a href="https://sshflow.com/blog/linux-export-command/" rel="noopener noreferrer"&gt;exporting environment variables in Linux&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redirect output to a file
&lt;/h2&gt;

&lt;p&gt;The shell—not &lt;code&gt;echo&lt;/code&gt;—handles redirection. Use &lt;code&gt;&amp;gt;&lt;/code&gt; to create a file or replace its contents, and &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; to append to it:&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;echo&lt;/span&gt; &lt;span class="s2"&gt;"First line"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; notes.txt
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Another line"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; notes.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be careful with &lt;code&gt;&amp;gt;&lt;/code&gt;: if &lt;code&gt;notes.txt&lt;/code&gt; already exists, its contents are overwritten. The second command adds a line at the end instead. If you're choosing between &lt;code&gt;echo&lt;/code&gt; and other ways to &lt;a href="https://sshflow.com/blog/linux-create-file/" rel="noopener noreferrer"&gt;create a file in Linux&lt;/a&gt;, keep that overwrite behavior in mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do &lt;code&gt;-n&lt;/code&gt; and &lt;code&gt;-e&lt;/code&gt; do?
&lt;/h2&gt;

&lt;p&gt;Two options you may see are &lt;code&gt;-n&lt;/code&gt; and &lt;code&gt;-e&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-n&lt;/code&gt; suppresses the final newline in common implementations.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-e&lt;/code&gt; interprets selected backslash escapes in implementations that support it. For example, &lt;code&gt;\n&lt;/code&gt; represents a newline and &lt;code&gt;\t&lt;/code&gt; a tab.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Bash, this prints two lines:&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;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"one&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;two"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, &lt;code&gt;echo&lt;/code&gt; can be a shell builtin or an external program, and implementations don't handle these options and escapes consistently. A command that works in one shell may behave differently in another. Avoid depending on &lt;code&gt;-e&lt;/code&gt; for scripts that need predictable output.&lt;/p&gt;

&lt;p&gt;You can inspect how your current shell resolves &lt;code&gt;echo&lt;/code&gt; with this Bash command:&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;type&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can show whether &lt;code&gt;echo&lt;/code&gt; is a builtin and whether an executable with the same name is also available. The behavior you get depends on which implementation is used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use printf when the output needs to be predictable
&lt;/h2&gt;

&lt;p&gt;For short messages, &lt;code&gt;echo&lt;/code&gt; is usually enough. Prefer &lt;code&gt;printf&lt;/code&gt; when you need formatted output, need precise control over newlines, or want predictable handling of backslashes across shells.&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;printf&lt;/span&gt; &lt;span class="s1"&gt;'one\ntwo\n'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For variables, use format placeholders such as &lt;code&gt;%s&lt;/code&gt; and pass the values as separate, quoted arguments:&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;printf&lt;/span&gt; &lt;span class="s1"&gt;'User: %s\nHome: %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;\n&lt;/code&gt; in the format string creates a newline, and each &lt;code&gt;%s&lt;/code&gt; is replaced by the corresponding value. Quoting the variable arguments keeps their contents as data rather than letting spaces split them into multiple arguments.&lt;/p&gt;

&lt;p&gt;This distinction matters when text might begin with a hyphen. With &lt;code&gt;echo&lt;/code&gt;, option-like text can be ambiguous because some implementations interpret options. &lt;code&gt;printf&lt;/code&gt; makes the format explicit:&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;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s1"&gt;'-starting with a hyphen'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A quick rule of thumb
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;echo&lt;/code&gt; for a simple message when its default trailing newline is what you want. Use &lt;code&gt;printf&lt;/code&gt; for formatted output or scripts where exact behavior matters. And remember that variable expansion, quoting, and redirection are handled by the shell before the command runs.&lt;/p&gt;

&lt;p&gt;I originally published a more detailed version of this guide on the &lt;a href="https://sshflow.com/blog/echo-command-linux/" rel="noopener noreferrer"&gt;SSHFlow blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also building &lt;a href="https://sshflow.com/" rel="noopener noreferrer"&gt;SSHFlow&lt;/a&gt; — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>devops</category>
    </item>
    <item>
      <title>Linux `export`: How Shell Variables Reach Other Commands</title>
      <dc:creator>dpm_bush</dc:creator>
      <pubDate>Wed, 14 Oct 2026 00:34:28 +0000</pubDate>
      <link>https://dev.to/__3381495fd2b/linux-export-how-shell-variables-reach-other-commands-een</link>
      <guid>https://dev.to/__3381495fd2b/linux-export-how-shell-variables-reach-other-commands-een</guid>
      <description>&lt;p&gt;A variable you set in a terminal is not automatically available to every command you run. In Bash and other POSIX-compatible shells, &lt;code&gt;export&lt;/code&gt; marks a variable so that commands launched afterward receive it in their environment.&lt;/p&gt;

&lt;p&gt;That distinction matters when a script behaves differently from your interactive shell, or when you set a value and expect a separate program to see it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shell variable vs. environment variable
&lt;/h2&gt;

&lt;p&gt;A shell can store a variable for its own use without passing it to child processes:&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;APP_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;development
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check it in the current shell:&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;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP_MODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But a new shell launched from that session does not inherit the value unless it was exported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'printf "%s\n" "${APP_MODE-unset}"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prints &lt;code&gt;unset&lt;/code&gt;. The variable exists in the parent shell, but it is not part of the environment that the parent passes to the child.&lt;/p&gt;

&lt;p&gt;Export it when you assign it:&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;export &lt;/span&gt;&lt;span class="nv"&gt;APP_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;development
sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'printf "%s\n" "$APP_MODE"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the child prints &lt;code&gt;development&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can also export a variable after assigning it:&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;APP_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;development
&lt;span class="nb"&gt;export &lt;/span&gt;APP_MODE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assignment sets the value; &lt;code&gt;export APP_MODE&lt;/code&gt; marks it for inheritance by subsequently launched child processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment variables flow downward
&lt;/h2&gt;

&lt;p&gt;Exporting is about process inheritance. A child process receives the exported environment when it starts. If that child changes a variable, the change does not flow back into the parent shell. And exporting a value now does not update processes that were already running.&lt;/p&gt;

&lt;p&gt;For a one-off command, you can set a variable just for that command without changing the parent shell:&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;APP_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;development sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'printf "%s\n" "$APP_MODE"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful for testing a program under a particular setting. By contrast, &lt;code&gt;export APP_MODE=development&lt;/code&gt; makes the variable available to later child commands launched from the current shell, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common uses: &lt;code&gt;PATH&lt;/code&gt; and application settings
&lt;/h2&gt;

&lt;p&gt;A common reason to export a variable is to add a directory to &lt;code&gt;PATH&lt;/code&gt; so commands launched from the shell can find programs there:&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;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prepends &lt;code&gt;$HOME/bin&lt;/code&gt; while preserving the existing &lt;code&gt;PATH&lt;/code&gt;. Only add directories you trust and can use. Running the command repeatedly in the same shell adds the directory repeatedly, so take care before putting it in a startup file that may be read more than once.&lt;/p&gt;

&lt;p&gt;Applications may also read environment variables for configuration. For example, if your network provides an HTTP proxy, you might set:&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;export &lt;/span&gt;&lt;span class="nv"&gt;HTTP_PROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"http://proxy.example:8080"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That address is a placeholder; use the endpoint supplied for your environment. Applications vary in which proxy variable names they support, so check the application’s documentation. Avoid putting credentials in commands if they could be saved in your shell history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check, remove, or stop exporting a variable
&lt;/h2&gt;

&lt;p&gt;In Bash and many POSIX shells, &lt;code&gt;export -p&lt;/code&gt; lists variables marked for export:&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;export&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Review the output before sharing it: environment variables can contain sensitive values. To print just one value, use:&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;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP_MODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An empty result does not tell you whether the variable is unset or set to an empty string. To distinguish those cases:&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="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="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_MODE&lt;/span&gt;&lt;span class="p"&gt;+x&lt;/span&gt;&lt;span class="k"&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;printf&lt;/span&gt; &lt;span class="s1"&gt;'APP_MODE is set\n'&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'APP_MODE is not set\n'&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To remove a variable entirely from the current shell, use &lt;code&gt;unset&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;unset &lt;/span&gt;APP_MODE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In shells that support it, &lt;code&gt;export -n APP_MODE&lt;/code&gt; removes the export attribute but retains the shell variable’s value. Future child processes will no longer inherit it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make a setting available in future shells
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;export&lt;/code&gt; changes the current shell environment; it does not make a setting permanent or system-wide. It also does not affect unrelated terminal windows. To have a variable set in future sessions, put an appropriate assignment in a startup file that the shell actually reads. Which file is right depends on the shell and whether it starts as a login or interactive shell.&lt;/p&gt;

&lt;p&gt;After changing a startup file, open a new matching shell or source the file to apply the change to your current shell. Then check the value with &lt;code&gt;printf&lt;/code&gt;. Be especially careful with startup-file changes to &lt;code&gt;PATH&lt;/code&gt;: a typo can affect commands in every shell that reads that file.&lt;/p&gt;

&lt;p&gt;If a script uses shell syntax, its interpreter matters: the shebang determines which shell runs it. See &lt;a href="https://sshflow.com/blog/run-sh-file-linux/" rel="noopener noreferrer"&gt;how to run a shell script in Linux&lt;/a&gt; for the distinction between invoking a script with a shell and running it directly. For Windows setups, &lt;a href="https://sshflow.com/blog/bash-for-windows/" rel="noopener noreferrer"&gt;choosing Bash or WSL&lt;/a&gt; can help clarify which shell is interpreting your commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  When &lt;code&gt;export&lt;/code&gt; is not recognized
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;export&lt;/code&gt; is a shell builtin in Bash and POSIX-compatible shells such as &lt;code&gt;sh&lt;/code&gt; and &lt;code&gt;dash&lt;/code&gt;; it is not a separate Linux package to install. If you see &lt;code&gt;export: command not found&lt;/code&gt;, check that you entered the command at a shell prompt and that the expected shell is interpreting it. In Bash, &lt;code&gt;type export&lt;/code&gt; identifies it as a builtin.&lt;/p&gt;

&lt;p&gt;The practical rule is simple: assign a variable for use in the current shell; export it when future child processes need it. That small distinction explains many cases where a value appears in your terminal but not in the program you launch.&lt;/p&gt;

&lt;p&gt;I originally published a more detailed version of this guide on the &lt;a href="https://sshflow.com/blog/linux-export-command/" rel="noopener noreferrer"&gt;SSHFlow blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also building &lt;a href="https://sshflow.com/" rel="noopener noreferrer"&gt;SSHFlow&lt;/a&gt; — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>devops</category>
    </item>
    <item>
      <title>Use Linux `watch` for Live Command Snapshots</title>
      <dc:creator>dpm_bush</dc:creator>
      <pubDate>Wed, 14 Oct 2026 00:34:06 +0000</pubDate>
      <link>https://dev.to/__3381495fd2b/use-linux-watch-for-live-command-snapshots-33jj</link>
      <guid>https://dev.to/__3381495fd2b/use-linux-watch-for-live-command-snapshots-33jj</guid>
      <description>&lt;p&gt;Need to check whether disk space is changing, or see which process is using CPU without rerunning a command by hand? Linux &lt;code&gt;watch&lt;/code&gt; repeatedly runs a command and refreshes its output in the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;watch &lt;span class="nt"&gt;-n&lt;/span&gt; 5 &lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs &lt;code&gt;df -h&lt;/code&gt; every five seconds. By default, &lt;code&gt;watch&lt;/code&gt; refreshes every two seconds. Press &lt;code&gt;Ctrl+C&lt;/code&gt; to stop it and return to your shell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Think snapshots, not streams
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;watch&lt;/code&gt; starts a command again at each interval; it does not leave one command running and display its ongoing output. That makes it useful for checking changing system state, but not for reading new lines as they are appended to a log.&lt;/p&gt;

&lt;p&gt;For example, use &lt;code&gt;watch&lt;/code&gt; for a refreshed process listing or filesystem summary. For a growing log file, use &lt;a href="https://sshflow.com/blog/linux-tail-command/" rel="noopener noreferrer"&gt;&lt;code&gt;tail -f&lt;/code&gt; or &lt;code&gt;tail -F&lt;/code&gt;&lt;/a&gt; instead. The right tool depends on whether you want repeated snapshots or a stream of new output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical checks
&lt;/h2&gt;

&lt;p&gt;Refresh memory usage every five seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;watch &lt;span class="nt"&gt;-n&lt;/span&gt; 5 free &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refresh uptime and load averages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;watch &lt;span class="nt"&gt;-n&lt;/span&gt; 5 &lt;span class="nb"&gt;uptime&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Show a short process snapshot, sorted by CPU use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;watch &lt;span class="nt"&gt;-n&lt;/span&gt; 2 &lt;span class="s1"&gt;'ps -eo pid,comm,%cpu --sort=-%cpu | head'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The quotes around the last command matter: they pass the whole pipeline to &lt;code&gt;watch&lt;/code&gt;, so it reruns both &lt;code&gt;ps&lt;/code&gt; and &lt;code&gt;head&lt;/code&gt;. Without them, your current shell may interpret the pipe outside &lt;code&gt;watch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These examples redraw the terminal; they do not save a history of earlier results. If you need interactive process inspection, &lt;code&gt;top&lt;/code&gt; is usually a better fit because it lets you navigate and sort a continuously updated view.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make changes easier to spot
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;-d&lt;/code&gt; option highlights differences between successive outputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;watch &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 2 free &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To hide the header that shows the command, interval, and time, use &lt;code&gt;-t&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;watch &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 5 &lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some implementations support &lt;code&gt;-c&lt;/code&gt; to interpret color sequences from the command output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;watch &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 2 &lt;span class="s1"&gt;'ls --color=always'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Color depends on both &lt;code&gt;watch&lt;/code&gt; and the command producing it. If you see garbled characters, remove color-forcing options or try without &lt;code&gt;-c&lt;/code&gt;. Options can vary by implementation and version, so check &lt;code&gt;watch --help&lt;/code&gt; if an option is rejected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quoting and shell behavior
&lt;/h2&gt;

&lt;p&gt;Pipelines and shell operators need to reach &lt;code&gt;watch&lt;/code&gt; as part of the command it repeats. Put a pipeline in quotes, as in the process example above.&lt;/p&gt;

&lt;p&gt;Also, do not assume that aliases or shell functions from your interactive session are available to the command &lt;code&gt;watch&lt;/code&gt; runs. Prefer the executable name and its full options. If a command depends on a particular shell's syntax, invoke that shell explicitly and test the command by itself first.&lt;/p&gt;

&lt;h2&gt;
  
  
  When &lt;code&gt;watch&lt;/code&gt; is the wrong tool
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;watch&lt;/code&gt; when you want to rerun a command and see its latest output. Choose something else when the task differs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Follow appended log lines:&lt;/strong&gt; use &lt;code&gt;tail -f&lt;/code&gt; or &lt;code&gt;tail -F&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Follow systemd journal messages:&lt;/strong&gt; use &lt;code&gt;journalctl -f&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspect processes interactively:&lt;/strong&gt; use &lt;code&gt;top&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run a task on a schedule:&lt;/strong&gt; use a scheduler rather than treating &lt;code&gt;watch&lt;/code&gt; as a background monitoring service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;watch&lt;/code&gt; is an interactive terminal utility. It redraws the available screen, so long output may be truncated or difficult to read. Narrow the command's output or enlarge the terminal if needed. For more options when inspecting processes, see this guide to &lt;a href="https://sshflow.com/blog/linux-list-processes/" rel="noopener noreferrer"&gt;listing Linux processes with &lt;code&gt;ps&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, and &lt;code&gt;pgrep&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick troubleshooting checklist
&lt;/h2&gt;

&lt;p&gt;If you get &lt;code&gt;watch: command not found&lt;/code&gt;, check whether it is available:&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;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; watch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it is missing, install the package that provides it using your distribution's package manager; both the package name and install command depend on the distribution.&lt;/p&gt;

&lt;p&gt;If the repeated command behaves unexpectedly, run it once without &lt;code&gt;watch&lt;/code&gt; first. That makes command errors easier to see. Then check that pipelines are quoted and that the command does not rely on an interactive alias or function.&lt;/p&gt;

&lt;p&gt;For an unrecognized option, consult &lt;code&gt;watch --help&lt;/code&gt; on that system. And when all you need is one current result, run the command directly—there is no benefit to refreshing a snapshot you only need once.&lt;/p&gt;

&lt;p&gt;I originally published a more detailed version of this guide on the &lt;a href="https://sshflow.com/blog/watch-command-linux/" rel="noopener noreferrer"&gt;SSHFlow blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also building &lt;a href="https://sshflow.com/" rel="noopener noreferrer"&gt;SSHFlow&lt;/a&gt; — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>devops</category>
    </item>
    <item>
      <title>Getting Started with tmux on macOS</title>
      <dc:creator>dpm_bush</dc:creator>
      <pubDate>Wed, 14 Oct 2026 00:33:42 +0000</pubDate>
      <link>https://dev.to/__3381495fd2b/getting-started-with-tmux-on-macos-40j8</link>
      <guid>https://dev.to/__3381495fd2b/getting-started-with-tmux-on-macos-40j8</guid>
      <description>&lt;p&gt;A terminal window closes, an SSH connection drops, or you need more than one shell in the same workspace. &lt;code&gt;tmux&lt;/code&gt; helps by keeping terminal sessions organized—and, when it runs on a server, alive after your SSH client disconnects.&lt;/p&gt;

&lt;p&gt;On a Mac, the setup is straightforward. The important distinction is where tmux runs: a local tmux session belongs to your Mac, while a tmux session started after SSHing into a server belongs to that server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install tmux
&lt;/h2&gt;

&lt;p&gt;If you use Homebrew, install and verify tmux from Terminal or iTerm2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;tmux
tmux &lt;span class="nt"&gt;-V&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use MacPorts instead, install it with:&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;sudo &lt;/span&gt;port &lt;span class="nb"&gt;install &lt;/span&gt;tmux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use one package manager for the installation rather than installing duplicate copies through both. You do not need to run tmux itself with &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To see which executable your shell will use, run:&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;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; tmux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is useful if you have more than one installation on your Mac. Your shell runs the first matching executable in its &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start a session and come back to it
&lt;/h2&gt;

&lt;p&gt;Create a named session so it is easy to find later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmux new-session &lt;span class="nt"&gt;-s&lt;/span&gt; work
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are now working inside tmux. Run commands as usual. To leave the session running and return to the regular terminal, press the default prefix &lt;code&gt;Ctrl+b&lt;/code&gt;, release it, then press &lt;code&gt;d&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These are sequential keystrokes—not a three-key chord. To reconnect, list sessions and attach to the one you want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmux list-sessions
tmux attach-session &lt;span class="nt"&gt;-t&lt;/span&gt; work
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there is only one session, &lt;code&gt;tmux attach&lt;/code&gt; is also enough.&lt;/p&gt;

&lt;p&gt;A local session stays available while your Mac is running, but it does not survive a Mac reboot. A session running on a remote server can remain available after your SSH connection drops, as long as the server and the session keep running. If you’re setting up that remote connection for the first time, this &lt;a href="https://sshflow.com/blog/ssh-from-mac-to-linux/" rel="noopener noreferrer"&gt;Mac-to-Linux SSH walkthrough&lt;/a&gt; covers the connection side.&lt;/p&gt;

&lt;h2&gt;
  
  
  The few keys worth learning first
&lt;/h2&gt;

&lt;p&gt;Most tmux actions use the prefix &lt;code&gt;Ctrl+b&lt;/code&gt;: press and release it, then press the action key.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Keys&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Detach and leave the session running&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl+b&lt;/code&gt;, then &lt;code&gt;d&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create a window&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl+b&lt;/code&gt;, then &lt;code&gt;c&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next or previous window&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl+b&lt;/code&gt;, then &lt;code&gt;n&lt;/code&gt; or &lt;code&gt;p&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split a window side by side&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl+b&lt;/code&gt;, then &lt;code&gt;%&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split a window into stacked panes&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl+b&lt;/code&gt;, then &lt;code&gt;"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Move between panes&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl+b&lt;/code&gt;, then an arrow key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Close the active pane&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl+b&lt;/code&gt;, then &lt;code&gt;x&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A &lt;strong&gt;window&lt;/strong&gt; is a workspace inside a session. A &lt;strong&gt;pane&lt;/strong&gt; divides a window into multiple terminals. Closing a pane ends the shell running in it, so check which pane is active before confirming the prompt. You can also type &lt;code&gt;exit&lt;/code&gt; in a pane when you are finished with its shell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Copying text to the Mac clipboard
&lt;/h2&gt;

&lt;p&gt;On macOS, tmux’s copy buffer and the system clipboard are separate. Copying a selection in tmux does not necessarily make it available to paste into another Mac app.&lt;/p&gt;

&lt;p&gt;To select text in the default copy mode, press &lt;code&gt;Ctrl+b&lt;/code&gt;, then &lt;code&gt;[&lt;/code&gt;; move with the arrow keys, press &lt;code&gt;Space&lt;/code&gt; to start selecting, and press &lt;code&gt;Enter&lt;/code&gt; to copy into tmux’s buffer. To send that buffer to the Mac clipboard, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmux show-buffer | pbcopy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pbcopy&lt;/code&gt; writes standard input to the macOS clipboard. You can then paste into another app normally.&lt;/p&gt;

&lt;p&gt;There’s a location detail to watch for: if tmux is running on a remote server, a command inside that session runs on the server. &lt;code&gt;pbcopy&lt;/code&gt; is a macOS utility, so running it remotely does not directly write to your Mac’s clipboard. The tmux buffer and the Mac clipboard are on different machines unless you set up a clipboard-forwarding method.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting a couple of common snags
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;tmux: command not found&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check whether Homebrew lists tmux and whether your shell can locate it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew list tmux
&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; tmux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it is installed but &lt;code&gt;command -v&lt;/code&gt; prints nothing, open a new terminal window and check again. If the problem remains, check your Homebrew shell setup and &lt;code&gt;PATH&lt;/code&gt; rather than guessing where the executable lives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The wrong tmux version starts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;command -v tmux&lt;/code&gt; to see which executable is selected. This can reveal an older copy earlier in your &lt;code&gt;PATH&lt;/code&gt;, or different installations managed by Homebrew and MacPorts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSH agent access behaves differently in an existing session&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Long-running tmux sessions can retain environment variables from when they started. If SSH key access is the issue, first identify which machine and shell are running the command; &lt;a href="https://sshflow.com/blog/ssh-auth-sock/" rel="noopener noreferrer"&gt;&lt;code&gt;SSH_AUTH_SOCK&lt;/code&gt; and agent sockets&lt;/a&gt; are a useful next step for understanding how an SSH agent is reached.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical way to think about it
&lt;/h2&gt;

&lt;p&gt;Use local tmux when you want multiple terminal workspaces on your Mac. Use tmux on a remote machine when you want the server-side session to keep running through a dropped SSH connection. In either case, name sessions, detach instead of closing them, and reconnect with &lt;code&gt;tmux attach-session -t name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I originally published a more detailed version of this guide on the &lt;a href="https://sshflow.com/blog/tmux-mac/" rel="noopener noreferrer"&gt;SSHFlow blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also building &lt;a href="https://sshflow.com/" rel="noopener noreferrer"&gt;SSHFlow&lt;/a&gt; — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>devops</category>
    </item>
    <item>
      <title>pwd in Linux: Know Which Directory Your Shell Is In</title>
      <dc:creator>dpm_bush</dc:creator>
      <pubDate>Wed, 14 Oct 2026 00:33:18 +0000</pubDate>
      <link>https://dev.to/__3381495fd2b/pwd-in-linux-know-which-directory-your-shell-is-in-5c6m</link>
      <guid>https://dev.to/__3381495fd2b/pwd-in-linux-know-which-directory-your-shell-is-in-5c6m</guid>
      <description>&lt;p&gt;A command can behave differently depending on the directory your shell is in. Before you edit a file, run a script, or use a relative path, check your location with &lt;code&gt;pwd&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;pwd&lt;/code&gt; tells you
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pwd&lt;/code&gt; means “print working directory.” Run it without arguments:&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;pwd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/alex/projects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the current working directory for this shell session. It is usually shown as an absolute path, starting at the filesystem root (&lt;code&gt;/&lt;/code&gt;). A relative path such as &lt;code&gt;src/app.js&lt;/code&gt; is interpreted from this location.&lt;/p&gt;

&lt;p&gt;Your current directory is not necessarily your home directory. You can navigate elsewhere and return to your home directory later. To change locations, use &lt;a href="https://sshflow.com/blog/cd-command-in-linux/" rel="noopener noreferrer"&gt;&lt;code&gt;cd&lt;/code&gt; and its directory-navigation shortcuts&lt;/a&gt;; &lt;code&gt;pwd&lt;/code&gt; only reports where you are.&lt;/p&gt;

&lt;p&gt;Each terminal or shell session tracks its own location. If one terminal is in &lt;code&gt;/home/alex/projects&lt;/code&gt; and another is in &lt;code&gt;/tmp&lt;/code&gt;, their &lt;code&gt;pwd&lt;/code&gt; output will differ.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use it before working with relative paths
&lt;/h2&gt;

&lt;p&gt;Suppose you expect a configuration file to be in your project, but you are not sure where the terminal started. Check first:&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;pwd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then inspect the current directory's contents:&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;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands answer different questions: &lt;code&gt;pwd&lt;/code&gt; shows your location, while &lt;code&gt;ls&lt;/code&gt; shows what is there. If you need to inspect hidden files or change how entries are displayed, see these &lt;a href="https://sshflow.com/blog/ls-command-in-linux/" rel="noopener noreferrer"&gt;practical &lt;code&gt;ls&lt;/code&gt; examples&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This check is especially useful before commands that use relative paths. For example, &lt;code&gt;./script.sh&lt;/code&gt; refers to a script in the current directory—not necessarily the directory where the script is stored. If a command reports “No such file or directory,” an unexpected working directory is one possible cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logical and physical paths
&lt;/h2&gt;

&lt;p&gt;A directory may be reachable through a symbolic link. In that case, there can be two useful ways to describe your location:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;logical path&lt;/strong&gt; preserves the path you used, including a symbolic link.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;physical path&lt;/strong&gt; resolves symbolic links to the underlying directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On shells and implementations that support these options, request the logical or physical path explicitly:&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;pwd&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt;
&lt;span class="nb"&gt;pwd&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, if &lt;code&gt;/var/www&lt;/code&gt; points to &lt;code&gt;/srv/site&lt;/code&gt;, the logical path may be &lt;code&gt;/var/www&lt;/code&gt;, while the physical path may be &lt;code&gt;/srv/site&lt;/code&gt;. The actual output depends on your filesystem and shell.&lt;/p&gt;

&lt;p&gt;Plain &lt;code&gt;pwd&lt;/code&gt; commonly reports a logical path in popular shells, but defaults can vary between shell built-ins and standalone utilities. If the distinction matters—for example, when diagnosing a path issue—use &lt;code&gt;-L&lt;/code&gt; or &lt;code&gt;-P&lt;/code&gt; and check the behavior of the shell you are running.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;pwd&lt;/code&gt; on Windows depends on the shell
&lt;/h2&gt;

&lt;p&gt;The command name does not mean exactly the same thing in every Windows environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;PowerShell&lt;/strong&gt;, &lt;code&gt;pwd&lt;/code&gt; is an alias for &lt;code&gt;Get-Location&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;Command Prompt&lt;/strong&gt;, use &lt;code&gt;cd&lt;/code&gt; or &lt;code&gt;echo %CD%&lt;/code&gt; to display the current directory.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;WSL&lt;/strong&gt; or &lt;strong&gt;Git Bash&lt;/strong&gt;, you are using a Unix-like shell where &lt;code&gt;pwd&lt;/code&gt; is available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if &lt;code&gt;pwd&lt;/code&gt; works in one Windows terminal but not another, check which shell that terminal is running.&lt;/p&gt;

&lt;h2&gt;
  
  
  A useful habit
&lt;/h2&gt;

&lt;p&gt;When a command seems to be looking in the wrong place, verify the shell's current directory before changing files or retrying with different paths:&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;pwd
ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command tells you where relative paths start; the second helps confirm what is in that location. That small check can prevent mistakes caused by assuming the terminal is somewhere it isn't.&lt;/p&gt;

&lt;p&gt;I originally published a more detailed version of this guide on the &lt;a href="https://sshflow.com/blog/pwd-command/" rel="noopener noreferrer"&gt;SSHFlow blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also building &lt;a href="https://sshflow.com/" rel="noopener noreferrer"&gt;SSHFlow&lt;/a&gt; — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>devops</category>
    </item>
    <item>
      <title>Renaming Files in Linux: Safe mv Patterns and Batch Previews</title>
      <dc:creator>dpm_bush</dc:creator>
      <pubDate>Wed, 14 Oct 2026 00:32:59 +0000</pubDate>
      <link>https://dev.to/__3381495fd2b/renaming-files-in-linux-safe-mv-patterns-and-batch-previews-4186</link>
      <guid>https://dev.to/__3381495fd2b/renaming-files-in-linux-safe-mv-patterns-and-batch-previews-4186</guid>
      <description>&lt;p&gt;Renaming a file in Linux usually means moving it to a new path with &lt;code&gt;mv&lt;/code&gt;. That works for a simple name change, but it also means the original path disappears—and an existing destination file may be replaced unless you take care.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rename one file
&lt;/h2&gt;

&lt;p&gt;Use the current name first and the new name second:&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;mv &lt;/span&gt;report-draft.txt report-final.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because both paths are in the current directory, this renames the file in place. &lt;code&gt;mv&lt;/code&gt; does not leave a second copy behind.&lt;/p&gt;

&lt;p&gt;If you’re unsure where your terminal is, check with &lt;code&gt;pwd&lt;/code&gt;, then list the current directory with &lt;code&gt;ls&lt;/code&gt;. The &lt;a href="https://sshflow.com/blog/ls-command-in-linux/" rel="noopener noreferrer"&gt;&lt;code&gt;ls&lt;/code&gt; command’s options&lt;/a&gt; can help you inspect names and file details before making a change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filenames with spaces or leading hyphens
&lt;/h2&gt;

&lt;p&gt;Quote names containing spaces so the shell passes each filename as one argument:&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;mv&lt;/span&gt; &lt;span class="s1"&gt;'quarterly report.txt'&lt;/span&gt; &lt;span class="s1"&gt;'quarterly report final.txt'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without quotes, the shell splits a name at its spaces, and &lt;code&gt;mv&lt;/code&gt; receives extra arguments.&lt;/p&gt;

&lt;p&gt;A filename that starts with &lt;code&gt;-&lt;/code&gt; can be mistaken for an option. Prefix it with &lt;code&gt;./&lt;/code&gt; to make it clear that it’s a path:&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;mv&lt;/span&gt; ./-draft.txt final.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On GNU/Linux, you can also use &lt;code&gt;--&lt;/code&gt; to mark the end of options:&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;mv&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-draft&lt;/span&gt;.txt final.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;./&lt;/code&gt; form is a straightforward way to avoid the ambiguity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid an accidental replacement
&lt;/h2&gt;

&lt;p&gt;If the destination name already exists, common Linux &lt;code&gt;mv&lt;/code&gt; implementations may replace it without prompting. Use &lt;code&gt;-i&lt;/code&gt; to ask for confirmation before an overwrite:&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;mv&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; old-name.txt new-name.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Review the prompt before answering. If you decline, the existing destination remains and the source is not moved over it.&lt;/p&gt;

&lt;p&gt;You can check whether a destination currently exists with:&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;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; new-name.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Treat this as a quick inspection, not a guarantee: another process could create the destination after the check. If replacing a file would be especially harmful, check your system’s &lt;code&gt;mv&lt;/code&gt; manual for the stronger protections available in that implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Move and rename in one step
&lt;/h2&gt;

&lt;p&gt;A destination path can include both a different directory and a new filename:&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;mv&lt;/span&gt; ./report-draft.txt ~/Documents/report-final.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This moves the file into &lt;code&gt;~/Documents&lt;/code&gt; and gives it the name &lt;code&gt;report-final.txt&lt;/code&gt;. If you want to keep the original name, use the destination directory alone:&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;mv &lt;/span&gt;report-draft.txt ~/Documents/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In either case, &lt;code&gt;mv&lt;/code&gt; relocates the original; it does not create a copy. If you need the old file to remain where it is, use &lt;code&gt;cp&lt;/code&gt; instead. This &lt;a href="https://sshflow.com/blog/copy-file-linux/" rel="noopener noreferrer"&gt;guide to copying files with &lt;code&gt;cp&lt;/code&gt;&lt;/a&gt; covers creating a copy under a different name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preview a batch rename before changing anything
&lt;/h2&gt;

&lt;p&gt;For a repeatable batch operation, a Bash loop can show the proposed names before it renames files. This example previews adding &lt;code&gt;archived-&lt;/code&gt; to &lt;code&gt;.txt&lt;/code&gt; files in the current directory:&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="k"&gt;for &lt;/span&gt;file &lt;span class="k"&gt;in&lt;/span&gt; ./&lt;span class="k"&gt;*&lt;/span&gt;.txt&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;continue
  &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s -&amp;gt; %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"./archived-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;file&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;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Review the output. If the paths look right, the same loop can perform the rename with GNU/Linux &lt;code&gt;mv -n&lt;/code&gt;, which skips existing destinations:&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="k"&gt;for &lt;/span&gt;file &lt;span class="k"&gt;in&lt;/span&gt; ./&lt;span class="k"&gt;*&lt;/span&gt;.txt&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;continue
  &lt;/span&gt;&lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"./archived-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;file&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;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These examples assume Bash and GNU coreutils. Try batch commands in a small test directory first, especially if files could produce conflicting destination names. The &lt;code&gt;-n&lt;/code&gt; option and exact &lt;code&gt;mv&lt;/code&gt; behavior can vary across implementations.&lt;/p&gt;

&lt;p&gt;There is also a separate utility called &lt;code&gt;rename&lt;/code&gt;, but multiple incompatible programs use that name. Its syntax depends on which implementation is installed, so check the local manual before using an example from another system.&lt;/p&gt;

&lt;h2&gt;
  
  
  One last distinction
&lt;/h2&gt;

&lt;p&gt;Changing a filename extension changes only the name. Renaming &lt;code&gt;report.txt&lt;/code&gt; to &lt;code&gt;report.csv&lt;/code&gt; does not convert the file’s contents into CSV. Use &lt;code&gt;mv&lt;/code&gt; when you intend to change a path or name; use a conversion tool when you need to change the file format.&lt;/p&gt;

&lt;p&gt;I originally published a more detailed version of this guide on the &lt;a href="https://sshflow.com/blog/rename-file-linux/" rel="noopener noreferrer"&gt;SSHFlow blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also building &lt;a href="https://sshflow.com/" rel="noopener noreferrer"&gt;SSHFlow&lt;/a&gt; — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>devops</category>
    </item>
    <item>
      <title>Unix vs. Linux: What the Names Actually Mean</title>
      <dc:creator>dpm_bush</dc:creator>
      <pubDate>Wed, 14 Oct 2026 00:32:37 +0000</pubDate>
      <link>https://dev.to/__3381495fd2b/unix-vs-linux-what-the-names-actually-mean-28m6</link>
      <guid>https://dev.to/__3381495fd2b/unix-vs-linux-what-the-names-actually-mean-28m6</guid>
      <description>&lt;p&gt;A shell command that works on Linux may also work on macOS or a commercial Unix system—but similar command-line interfaces don’t mean the operating systems are identical. The distinction matters when choosing a platform, writing portable software, or figuring out what someone means by “Unix.”&lt;/p&gt;

&lt;p&gt;The quick version: &lt;strong&gt;Linux is a kernel commonly used as part of a complete operating system. Unix describes a historical family of systems, and UNIX certification is a specific designation.&lt;/strong&gt; Linux systems are often called Unix-like because they share conventions and design ideas, not because Linux is the original Unix.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, separate the terms
&lt;/h2&gt;

&lt;p&gt;These labels describe different things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Linux kernel:&lt;/strong&gt; The core software that manages hardware and provides services to applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux distribution:&lt;/strong&gt; A usable operating-system environment built around the Linux kernel, together with tools, libraries, applications, and a package-management approach. Ubuntu, Debian, and Fedora are distributions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unix:&lt;/strong&gt; A name associated with the operating-system lineage that began at Bell Labs and later developed into multiple systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unix-like:&lt;/strong&gt; A descriptive term for systems that share Unix traditions, interfaces, or conventions. It isn’t a certification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UNIX-certified:&lt;/strong&gt; A status granted to specific products and versions that meet the relevant Single UNIX Specification requirements and are permitted to use the UNIX trademark.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;People often say “Linux” when they mean a full distribution rather than the kernel alone. That shorthand is common, but it can make comparisons confusing: are we comparing a kernel with an operating system, or a Linux distribution with a particular Unix product?&lt;/p&gt;

&lt;p&gt;Linux began as an independent kernel project in 1991. It was influenced by Unix ideas, but it wasn’t released as a version of the original Unix source code. Many complete Linux systems are therefore Unix-like without being UNIX-certified.&lt;/p&gt;

&lt;h2&gt;
  
  
  Similar conventions don’t guarantee identical behavior
&lt;/h2&gt;

&lt;p&gt;Unix systems and Linux distributions commonly share familiar concepts: users, processes, hierarchical filesystems, permissions, shells, and command-line utilities. If you know your way around one Unix-style shell, many everyday tasks will feel recognizable on another.&lt;/p&gt;

&lt;p&gt;But resemblance isn’t a compatibility guarantee. A command can exist on both systems and still have different options, defaults, or implementations. Applications may also depend on system-specific APIs, libraries, or hardware support. POSIX standards define common interfaces that can help with portability, but meeting or targeting POSIX isn’t the same as UNIX certification—and doesn’t make every program portable without testing.&lt;/p&gt;

&lt;p&gt;For example, a script using broadly supported shell syntax may transfer easily, while one relying on a utility option found only in a particular implementation may need changes. Check the target system’s manual and test on the platforms you intend to support rather than assuming matching command names behave exactly alike.&lt;/p&gt;

&lt;p&gt;When working on a Linux host, it’s also useful to know which distribution you’re actually using; &lt;a href="https://sshflow.com/blog/check-linux-version/" rel="noopener noreferrer"&gt;these commands for identifying a Linux distribution and kernel&lt;/a&gt; help distinguish those two details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Licensing and development aren’t one-size-fits-all
&lt;/h2&gt;

&lt;p&gt;There isn’t one license shared by every operating system called Unix. Licensing and source availability depend on the particular system and its components.&lt;/p&gt;

&lt;p&gt;The Linux kernel is licensed under GPL version 2, but a Linux distribution brings together many projects, each with its own license. Distributions also differ in their policies and included components. Don’t infer a system’s redistribution terms, support model, or source availability just from the words “Unix” or “Linux”; check the specific product and version.&lt;/p&gt;

&lt;p&gt;Development models differ too. Linux is developed collaboratively, with distributions integrating the kernel and other software into a complete system. Unix systems have their own histories, implementations, vendors, and release models. “Unix” doesn’t refer to one identical codebase or one uniform set of operating-system details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a platform: start with the requirement
&lt;/h2&gt;

&lt;p&gt;There’s no universal winner. The useful question is what your software, hardware, support contract, and team need.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Learning server administration or shell basics?&lt;/strong&gt; A widely used Linux distribution is a practical place to start. Expect core ideas to transfer, but verify system-specific commands and behavior elsewhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Building software for multiple operating systems?&lt;/strong&gt; Prefer documented, portable interfaces where possible, avoid unnecessary assumptions about utility options, and test on every supported target. POSIX can help, but it doesn’t replace testing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Working in an organization with an established platform?&lt;/strong&gt; Use the system required by the application vendor, operations team, or support agreement. A particular commercial Unix product or Linux distribution may be a hard requirement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choosing between Linux distributions?&lt;/strong&gt; Compare release lifecycle, software availability, support, hardware requirements, and administration practices. For one common decision, this &lt;a href="https://sshflow.com/blog/fedora-vs-ubuntu/" rel="noopener noreferrer"&gt;Fedora and Ubuntu comparison&lt;/a&gt; covers differences between two Linux distributions—not between Linux and Unix as a whole.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Linux and Unix share enough history and conventions that their command lines can feel familiar. But &lt;strong&gt;Linux is a kernel, Unix is a historical system family, and UNIX-certified is a product-specific status&lt;/strong&gt;. “Unix-like” describes resemblance; it doesn’t certify a system or promise that every command and application will work unchanged.&lt;/p&gt;

&lt;p&gt;When compatibility or support matters, identify the exact operating system and version, check the interfaces your software depends on, and test on the target platform.&lt;/p&gt;

&lt;p&gt;I originally published a more detailed version of this guide on the &lt;a href="https://sshflow.com/blog/unix-vs-linux/" rel="noopener noreferrer"&gt;SSHFlow blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also building &lt;a href="https://sshflow.com/" rel="noopener noreferrer"&gt;SSHFlow&lt;/a&gt; — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>devops</category>
    </item>
    <item>
      <title>SSH Keys Explained: Public Keys, Private Keys, and Host Keys</title>
      <dc:creator>dpm_bush</dc:creator>
      <pubDate>Tue, 13 Oct 2026 04:51:07 +0000</pubDate>
      <link>https://dev.to/__3381495fd2b/ssh-keys-explained-public-keys-private-keys-and-host-keys-27o8</link>
      <guid>https://dev.to/__3381495fd2b/ssh-keys-explained-public-keys-private-keys-and-host-keys-27o8</guid>
      <description>&lt;p&gt;SSH keys can be confusing because “my SSH key” might mean a credential you use to log in—or a key the server uses to prove its identity. Those keys have different owners and jobs. Mixing them up can lead to authentication failures or, worse, trusting the wrong server.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two halves of a login key
&lt;/h2&gt;

&lt;p&gt;For user authentication, an SSH key is a public/private pair:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The private key&lt;/strong&gt; stays under your control. Your SSH client uses it to prove that you possess the credential.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The public key&lt;/strong&gt; is shared with a server or service that should allow your access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On a typical OpenSSH setup, a private key might be named &lt;code&gt;~/.ssh/id_ed25519&lt;/code&gt;, with its matching public key at &lt;code&gt;~/.ssh/id_ed25519.pub&lt;/code&gt;. These names and locations are common defaults, not requirements; keys can use other filenames or credential stores.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.pub&lt;/code&gt; suffix is a useful clue, but check the file’s contents and context rather than relying on a name alone. Public keys are intended to be distributed. Private keys are secrets: don’t paste them into websites, send them to a server, or commit them to a repository.&lt;/p&gt;

&lt;p&gt;If you need to create a pair, follow a &lt;a href="https://sshflow.com/blog/how-to-generate-ssh-key/" rel="noopener noreferrer"&gt;practical SSH key generation guide&lt;/a&gt; and keep the private and public files straight.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens during key authentication?
&lt;/h2&gt;

&lt;p&gt;The server has to authorize your public key for the account you’re trying to access. In a common OpenSSH setup, that public key is listed in the account’s &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt; file on the server.&lt;/p&gt;

&lt;p&gt;When you connect, your client offers a public key. If the server recognizes it as authorized, the client proves possession of the matching private key by creating a cryptographic signature. The server checks that proof with the public key. The private key itself is not sent over the network.&lt;/p&gt;

&lt;p&gt;A successful key match is only one part of login. The username must be right, the key must be authorized for that account, and server policy must allow the authentication method. That’s why having a key on your laptop does not automatically grant access to every server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Host keys answer a different question
&lt;/h2&gt;

&lt;p&gt;A user key answers: &lt;strong&gt;“Is this client authorized to log in?”&lt;/strong&gt; A host key answers: &lt;strong&gt;“Is this the server I meant to reach?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The SSH server keeps its host private key. Your client checks the corresponding public host key and commonly remembers it in &lt;code&gt;known_hosts&lt;/code&gt;. This verification helps detect when a server’s identity differs from what your client previously trusted.&lt;/p&gt;

&lt;p&gt;A host-key warning is not the same as a rejected user key. A server rebuild or other legitimate change can produce a new host key, but an unexpected change can also mean you are connecting to a different machine. Verify the new fingerprint through a trusted channel before accepting it. Don’t disable host-key checking just to make the warning go away. If you encounter the specific “REMOTE HOST IDENTIFICATION HAS CHANGED” message, use this &lt;a href="https://sshflow.com/blog/remote-host-identification-has-changed/" rel="noopener noreferrer"&gt;safe host-key warning workflow&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A passphrase protects the local key file
&lt;/h2&gt;

&lt;p&gt;A passphrase can encrypt a private-key file at rest. When you use that key, you may need to unlock it directly or through an SSH agent or another credential tool.&lt;/p&gt;

&lt;p&gt;It is not your remote account password, and public-key authentication does not send it to the server. The passphrase protects the local key file; it doesn’t change which keys the server authorizes. Protect the device and private-key file too. If someone can use an already-unlocked key, the passphrase may not stop them.&lt;/p&gt;

&lt;p&gt;Key-based login also doesn’t necessarily mean there are no passwords involved: the server might still allow account-password authentication, and the private key may require a passphrase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick checks when login fails
&lt;/h2&gt;

&lt;p&gt;Start by identifying which part of the exchange is failing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check the account and destination.&lt;/strong&gt; A valid key won’t help if you’re connecting as the wrong user or to the wrong host.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check which key your client is using.&lt;/strong&gt; You might have more than one key, or a key stored under a non-default filename.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check server authorization.&lt;/strong&gt; The public key must be authorized for the account you’re logging into; having it on a different account is not enough.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep warning types separate.&lt;/strong&gt; A rejected user key concerns login authorization. A changed host key concerns the identity of the server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The core distinction is simple: keep the user’s private key private, install or register its public counterpart where access is needed, and treat the server’s host key as a separate identity check. Knowing which key is involved makes SSH errors much easier to reason about.&lt;/p&gt;

&lt;p&gt;If a private key is exposed, assume it may be compromised. Remove or revoke its public key wherever it is authorized, then create a replacement pair and grant access only where needed. Adding a passphrase after exposure does not undo the exposure.&lt;/p&gt;

&lt;p&gt;I originally published a more detailed version of this guide on the &lt;a href="https://sshflow.com/blog/what-is-an-ssh-key/" rel="noopener noreferrer"&gt;SSHFlow blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also building &lt;a href="https://sshflow.com/" rel="noopener noreferrer"&gt;SSHFlow&lt;/a&gt; — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>devops</category>
    </item>
    <item>
      <title>A Practical Guide to `tr`: Character-Level Text Processing in Linux</title>
      <dc:creator>dpm_bush</dc:creator>
      <pubDate>Tue, 13 Oct 2026 02:04:43 +0000</pubDate>
      <link>https://dev.to/__3381495fd2b/a-practical-guide-to-tr-character-level-text-processing-in-linux-1bf8</link>
      <guid>https://dev.to/__3381495fd2b/a-practical-guide-to-tr-character-level-text-processing-in-linux-1bf8</guid>
      <description>&lt;p&gt;&lt;code&gt;tr&lt;/code&gt; is a small command-line tool for changing text one character at a time. It can translate characters, delete them, or collapse repeated characters—but it does not replace words or edit files in place.&lt;/p&gt;

&lt;p&gt;That makes it useful in pipelines: pass text to standard input, transform it, and send the result to the terminal or another command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with a translation
&lt;/h2&gt;

&lt;p&gt;The basic form is:&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;tr&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;OPTION]... SET1 &lt;span class="o"&gt;[&lt;/span&gt;SET2]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To map characters in &lt;code&gt;SET1&lt;/code&gt; to the corresponding characters in &lt;code&gt;SET2&lt;/code&gt;, provide both sets. For example, convert lowercase letters to uppercase:&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;printf&lt;/span&gt; &lt;span class="s1"&gt;'hello, linux\n'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'[:lower:]'&lt;/span&gt; &lt;span class="s1"&gt;'[:upper:]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HELLO, LINUX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The POSIX character classes &lt;code&gt;[:lower:]&lt;/code&gt; and &lt;code&gt;[:upper:]&lt;/code&gt; express the intent more clearly than a range such as &lt;code&gt;a-z&lt;/code&gt;. Quote sets so the shell passes them to &lt;code&gt;tr&lt;/code&gt; as intended.&lt;/p&gt;

&lt;p&gt;Translation is positional and character-by-character. This example maps &lt;code&gt;a&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt; to &lt;code&gt;2&lt;/code&gt;, and &lt;code&gt;c&lt;/code&gt; to &lt;code&gt;3&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;printf&lt;/span&gt; &lt;span class="s1"&gt;'cab\n'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'abc'&lt;/span&gt; &lt;span class="s1"&gt;'123'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is &lt;code&gt;312&lt;/code&gt;. For replacing longer strings or matching patterns, use a tool designed for that job; &lt;code&gt;tr&lt;/code&gt; only works on individual characters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read from a file without changing it
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;tr&lt;/code&gt; does not accept a filename as an input operand. Use shell input redirection to feed a file into it:&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;tr&lt;/span&gt; &lt;span class="s1"&gt;'[:upper:]'&lt;/span&gt; &lt;span class="s1"&gt;'[:lower:]'&lt;/span&gt; &amp;lt; input.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prints the transformed contents; &lt;code&gt;input.txt&lt;/code&gt; stays unchanged. To save the result, redirect to a different 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="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'[:upper:]'&lt;/span&gt; &lt;span class="s1"&gt;'[:lower:]'&lt;/span&gt; &amp;lt; input.txt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; output.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not redirect output to the same file you are reading. The shell can truncate the destination before &lt;code&gt;tr&lt;/code&gt; gets a chance to read it. If you need a refresher on inspecting file contents or combining files, see this guide to the &lt;a href="https://sshflow.com/blog/linux-cat-command/" rel="noopener noreferrer"&gt;Linux &lt;code&gt;cat&lt;/code&gt; command&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delete or keep selected characters
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;-d&lt;/code&gt; to delete every character that belongs to a set. This removes digits but leaves punctuation and spaces in place:&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;printf&lt;/span&gt; &lt;span class="s1"&gt;'order 58: item 204\n'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'[:digit:]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order : item
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To do the opposite—keep digits and remove everything else—combine &lt;code&gt;-d&lt;/code&gt; with &lt;code&gt;-c&lt;/code&gt;, which complements the set:&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;printf&lt;/span&gt; &lt;span class="s1"&gt;'order 58: item 204\n'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-cd&lt;/span&gt; &lt;span class="s1"&gt;'[:digit:]\n'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;58204
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The newline is included in the set so the output retains a line ending. Here, &lt;code&gt;[:digit:]&lt;/code&gt; matches individual digit characters, not a whole number as a unit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Squeeze repeated characters
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;-s&lt;/code&gt; option reduces each run of repeated characters in a set to one character. To collapse multiple spaces:&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;printf&lt;/span&gt; &lt;span class="s1"&gt;'too    many spaces\n'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;too many spaces
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also translate tabs and spaces to a single space, then squeeze the result:&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;printf&lt;/span&gt; &lt;span class="s1"&gt;'red\t\tblue\n'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'[:blank:]'&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;[:blank:]&lt;/code&gt; matches horizontal blanks such as spaces and tabs. With translation enabled, &lt;code&gt;-s&lt;/code&gt; squeezes repeated characters from the translated set (&lt;code&gt;SET2&lt;/code&gt;). The newline is not a blank, so it remains intact.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick way to choose an operation
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Translate&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tr 'abc' '123'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map characters by position&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tr -d '[:digit:]'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove characters in the set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keep only a set&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tr -cd '[:digit:]\n'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete characters outside the set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Squeeze&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tr -s ' '&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Collapse repeated spaces&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These operations compose naturally with other filters. For example, you might normalize or remove characters before searching; the &lt;a href="https://sshflow.com/blog/grep-command-in-linux/" rel="noopener noreferrer"&gt;Linux &lt;code&gt;grep&lt;/code&gt; guide&lt;/a&gt; covers the search step and common pattern-matching options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Portability and text encoding notes
&lt;/h2&gt;

&lt;p&gt;Common character classes include &lt;code&gt;[:digit:]&lt;/code&gt;, &lt;code&gt;[:lower:]&lt;/code&gt;, &lt;code&gt;[:upper:]&lt;/code&gt;, and &lt;code&gt;[:blank:]&lt;/code&gt;. Quoted ranges such as &lt;code&gt;'a-z'&lt;/code&gt; are also common, but range ordering can depend on locale. Prefer character classes for case conversion when they express what you mean.&lt;/p&gt;

&lt;p&gt;Behavior around locales, complements, and multibyte characters can differ between implementations. GNU &lt;code&gt;tr&lt;/code&gt; provides &lt;code&gt;-C&lt;/code&gt; to complement characters rather than bytes; do not assume that option is available everywhere. If byte-versus-character behavior matters, check the local &lt;code&gt;tr&lt;/code&gt; manual page.&lt;/p&gt;

&lt;p&gt;For predictable ASCII-oriented processing, you can run a command under the C locale:&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;LC_ALL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;C &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'[:lower:]'&lt;/span&gt; &lt;span class="s1"&gt;'[:upper:]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That changes how the utility interprets input, so use it when ASCII-oriented behavior is appropriate—not as a universal fix for arbitrary text.&lt;/p&gt;

&lt;p&gt;One final distinction is worth remembering: &lt;code&gt;tr&lt;/code&gt; writes transformed output to standard output. It is a filter, not an in-place editor. Use it in a pipeline or redirect its output deliberately, and choose a separate destination file when preserving the original matters.&lt;/p&gt;

&lt;p&gt;I originally published a more detailed version of this guide on the &lt;a href="https://sshflow.com/blog/tr-command-in-linux/" rel="noopener noreferrer"&gt;SSHFlow blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also building &lt;a href="https://sshflow.com/" rel="noopener noreferrer"&gt;SSHFlow&lt;/a&gt; — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
