this post was submitted on 28 Apr 2025
60 points (96.9% liked)

Linux

53664 readers
1573 users here now

From Wikipedia, the free encyclopedia

Linux is a family of open source Unix-like operating systems based on the Linux kernel, an operating system kernel first released on September 17, 1991 by Linus Torvalds. Linux is typically packaged in a Linux distribution (or distro for short).

Distributions include the Linux kernel and supporting system software and libraries, many of which are provided by the GNU Project. Many Linux distributions use the word "Linux" in their name, but the Free Software Foundation uses the name GNU/Linux to emphasize the importance of GNU software, causing some controversy.

Rules

Related Communities

Community icon by Alpár-Etele Méder, licensed under CC BY 3.0

founded 5 years ago
MODERATORS
 

For one user account, I want to have some bash scripts, which of course would be under version control.

The obvious solution is just to put the scripts in a git repository and make ~/bin a symlink to the scripts directory.

Now, it seems on systemd systems ~/.local/bin is supposedly the directory for user scripts.

My question, is mostly, what are the tradeoffs between using ~/bin and ~/.local/bin as directory for my own bash scripts?

One simple scenario I can come up with are 3rd party programs which might modify ~/.local/bin and put their own scripts/starters there, similar to 3rd party applications which put their *.desktop files in ~/.local/applications.

Any advice on this? Is ~/.local/bin safe to use for my scripts or should I stick to the classic ~/bin? Anyone has a better convention?

(Btw.: I am running Debian everywhere, so I do not worry about portability to non systemd Linux systems.)

Solved: Thanks a lot for all the feedback and answering my questions! I'll settle with having my bash scripts somewhere under ~/my_git_monorepo and linking them to ~/.local/bin to stick to the XDG standard.

all 33 comments
sorted by: hot top controversial new old
[–] [email protected] 6 points 17 hours ago

I have ~/.local/bin added to my PATH for things i want in my PATH, and ~/scripts for things I don't want in my PATH. Both managed by chezmoi. I'm surprised if there's anyone who wants most of their bash scripts in PATH. I only have like 5 scripts in ~/.local/bin; the others get executed on an automated basis (eg on startup or by a cronjob), or so infrequently that I don't want them in my PATH.

[–] [email protected] 51 points 1 day ago (5 children)

I use ~/.local/bin since by linux standard, ~/.local is a user-level /usr/local, which is a override level of /usr

~/bin ends up cluttering the home folder

[–] [email protected] 28 points 1 day ago (2 children)

Another reason to use ~/.local is you can do things like

./configure --prefix=$HOME/.local
make -j$(ncpu)
make install

And then you get your .local/bin, .local/share, .local/include, .local/lib and such, just like /usr but scoped to your user.

and it should mostly just work as well.

[–] [email protected] 7 points 1 day ago

And if there's other users in the machine, it doesn't fuck things up for others Or if it ends up messing something up, it is user-scoped, so its a lot easier to fix than a bricked system

[–] [email protected] 0 points 17 hours ago

Prefix can be just $HOME as well.

[–] [email protected] 7 points 1 day ago (2 children)

If I hand write bash scripts, or for those single binary downloads, they'll go into ~/bin. ~/.local is already used by a ton of packages. This helps a ton when it comes to backups or for just finding where I put stuff.

My ~/.local is 283 GB, it's where podman/docker/etc put containers, it may as well be a system managed folder at that point. My ~/bin is only 120 MB and is a lot simpler to backup/restore/sync to other desktops.

[–] [email protected] 2 points 14 hours ago* (last edited 14 hours ago)

it may as well be a system managed folder at that point.

In a way it is. But user-level system, as opposed to root-level system.

[–] [email protected] 2 points 1 day ago (1 children)

At that point I'd poke around what's in there, cuz there's absolutely a mess in there

[–] [email protected] 3 points 20 hours ago

It's really not. Python virtualenv, Steam, libvirt, composer, krita, vulkan, zed, zoxide, systemd, etc. ~/.local is the domain of various installed packages, not my hand crafted scripts.

[–] [email protected] 5 points 1 day ago (1 children)

Another follow up question: Is there any documentation for the linux standard/convention of ~/.local/bin? My initial search about this resulted in nothing which I would call authoritative/definitive.

[–] [email protected] 14 points 1 day ago (1 children)

freedesktop.org defines environment variables that should be used by applications to store their stuff;
[archlinux.org] has a (non-authoritative) summary, but it also provides a [link to the actual specification].

[–] [email protected] 8 points 1 day ago

Thank you so much, bookmarked all of your links! :-)

[–] [email protected] 3 points 1 day ago (1 children)

Mostly this, but also, if you're going to manage many scripts in a system for many users, revision control doesn't help that. Either look at packaging them properly for your distro, or using something Ansible to distribute and manage their versioning on the system to make things easier on yourself.

[–] [email protected] 2 points 1 day ago (1 children)

Me, packaging company software to Alpine Packages so that I can just apk add stuff

[–] [email protected] 1 points 1 day ago

Good practice though. It's pretty much a necessity anymore with supply chain attacks becoming such a thing.

[–] [email protected] 2 points 1 day ago

Thanks! Do you just put the whole .local/bin under source control, do you link your scripts from somewhere else?

[–] [email protected] 6 points 1 day ago* (last edited 1 day ago) (2 children)

Neither ~/bin or ~/.local/bin are part of most shell's default $PATH so you're going to have to modify the user's shell profile (or rc) to include it. It's possible that your favorite distro includes it but not mine. For example(s):

﬌ unset PATH                                             

﬌ /bin/bash --noprofile --norc         
bash-5.2$ echo $PATH
/usr/local/bin:/usr/bin

or

﬌ unset PATH

﬌ /bin/zsh --no-rcs --no-global-rcs
Sinthesis% echo $PATH
/bin:/usr/bin:/usr/ucb:/usr/local/bin

﬌ ls -l /bin
lrwxrwxrwx. 1 root root 7 Jan 23  2024 /bin -> usr/bin

That was on Fedora. The funny thing is /bin is soft linked to usr/bin, weeeee.

This is on Debian

Sinthesis@debian:~$ /bin/bash --noprofile --norc
bash-5.2$ echo $PATH
/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:.

I'm not sure why you're bringing the XDG or systemd "standard" into this. POSIX standard would be more appropriate but they don't say anything on the matter, nor should they really. The most important thing is, be predictable. If the user has a problem with one of your scripts, what do they do first? which wolf_bin will show them the full path to the script. So really, the location does not matter much.

That said I would go with one of these two options:

  1. Make a package for your distro. This may be overkill for a couple scripts but you did say they're in a git repository so you could automate it. The package would install to /usr/bin which would require sudo or root. If the scripts are only allowed to be run by one user, set the rwx and group permissions.

  2. A pattern I like, especially for lightweight things such as scripts that don't require compiling or OS management and also are using git; a "hidden" or "dot" directory in the user's home where the repo lives e.g. ~/.lemmywolf/ Then add scripts directory to the user's $PATH e.g. PATH=$PATH:~/.lemmywolf/scripts. This is what some fairly large projects like pyenv or volta do. You could take it a step farther and modify this installer script to your liking https://github.com/pyenv/pyenv-installer/blob/master/bin/pyenv-installer

/edit 20 year Linux user (Redhat AS2.1) and 5 years of Unix (HPUX & Solaris) before that.

/edit2 I just noticed the pyenv-installer does not modify the user's shell profile. That could easily be added to the script though.

[–] [email protected] 1 points 15 hours ago* (last edited 15 hours ago)

I’m not sure why you’re bringing the XDG or systemd “standard” into this.

Probably because in their "basedir" specification they do recommend ~/.local/bin to be in $PATH. I'm sure there's more than one distro following that spec, whether we'd want to consider it standard or not. I also believe there's some software (like flatpak) that may place scripts there too, when configured to offer commands for user-level instalations.

Here's a quote from the spec:

User-specific executable files may be stored in $HOME/.local/bin. Distributions should ensure this directory shows up in the UNIX $PATH environment variable, at an appropriate place.

[–] [email protected] 1 points 16 hours ago

a distro package is way overkill for this, and its also better to not litter the home directory with yet anothet dotdir, that's why .local/bin is a good place, also recommended by ~~systemd~~ the freedesktop base directory standard: https://specifications.freedesktop.org/basedir-spec/latest/

[–] [email protected] 9 points 1 day ago (2 children)

Put them wherever you want, don't let Poettering dictate what you do with YOUR system. It is better NOT to put them in system directories since those will get overwritten by upgrades.

[–] [email protected] 1 points 16 hours ago

thats fair, but that shouldn't make you avoid sensible freedesktop dirs just out of spite.

[–] [email protected] 1 points 23 hours ago (2 children)

It is better NOT to put them in system directories since those will get overwritten by upgrades.

That's a purely Atomic thing, isn't it?

[–] [email protected] 1 points 10 hours ago* (last edited 10 hours ago)

Package managers tend to assume they are the only ones touching files in /usr/share. You will find if you try to change any files there, the next update may delete or download a new version of the file, stomping your changes. Instead your local changes should go in /usr/local (if you want something system-wide) or ~/.local (if it only applies to a specific user).

Ex. If you made a custom .desktop file to show up in your app launcher, or a custom .xsession file to show up in a login manager.

[–] [email protected] 1 points 16 hours ago

not necessarily, package managers tend to overwrite existing files on the same path, if you end up having installing such a package. but that shouldn't apply to /usr/local

[–] [email protected] 8 points 1 day ago* (last edited 1 day ago)

i have ~/bin as a syncthing folder because i manage several machines and if i update a script on one machine i want that synced to all of them. Then i just use . local for stuff that doesnt need syncing.

[–] [email protected] 6 points 1 day ago (1 children)

I've tried both and ~/.local/bin tends to be used by a bunch of tools to install their own binaries/scripts so depending on what you use it can become very messy (which did happen in my case). I used to have a ~/Documents/Scripts directory in my $PATH and that was much cleaner than my current setup so that's what I'd recommend, especially if you want to use Git with it! :)

[–] [email protected] 2 points 1 day ago

Thank you very much! I was exactly looking for someone telling me that some tools install their own binaries/scripts to ~/.local/bin.

Most probably I'll just symlink my scripts from ~/.local/bin then, this would avoid troubles with 3rd partys and most of my dotfiles are symlinked anyway, so the infrastructure is there.

[–] [email protected] 1 points 1 day ago* (last edited 1 day ago) (1 children)

Personally, I put a ~/.get-going or whatever you want to call it and put all my scripts in there. Name them with numbers first like “10-first.sh” “20-second.sh” and then just put a line in .bashrc or .zshrc or whatever you like. Aliases and any critical stuff last. Then one line in your rc file can include them all.

I made some bash scripts for distro-hopping that are now [undiscloded] years old so I can basically backup a few folders — the second being ~/bin where I put AppImages and stuff and sometimes ~/Development (I don’t always need the dev one because backups of those exist as repos) folder if I need to reinstall. A lot of people backup their whole home directory. But I prefer my method and that’s why we use Linux. I don’t want my settings for every app coming with me when I go on a new journey. Choose your own adventure.

[–] [email protected] 1 points 1 day ago (1 children)

Thanks, I think I get the idea, I just don't understand the number-prefix, why did you start this convention?

(Btw.: For some years now I stick to the convention, that everything import is under one sub directory under my home. As long as I have a tarball backup of that sub directory, I am good to lose the whole hard disk w/o fear (e.g. ready for a clean upgrade, distro hop or just go traveling w/o fearing that I forgot to switch off the oven ;-)).

[–] [email protected] 2 points 1 day ago (1 children)

It’s just alphabetical so the scripts run in the right order. The numbers serve like “A” or “B” except you can add new scripts between one and ten if it comes up and your “10-whatever” file is a mess. It’s sort of a convention on Linux but not everyone does it.

Then you just add

for FILE in ~/.shellrc.d/*; do
    source $FILE
done

To your ~./bashrc (or your preferred shell). Replace shellrc.d with whatever you choose. I use shellrc.d on servers and stuff because the dot d is also kind of a convention for naming folders. People have their own opinions about that but don’t worry about it until you have strong opinions.

[–] [email protected] 2 points 1 day ago

It just makes it easier to backup your customizations. I copy a lot of my settings in there. I use Vim (which isn’t necessarily the best choice but I’m old) so I just put my .vimrc stuff in my folder. Then you just have to backup one folder and, if nothing else, your CLI will stay the same.

People argue over emacs and vim (as text editors) and systemd vs init but it’s your machine. That’s part of what makes Linux fun.

[–] [email protected] 0 points 1 day ago

Since both locations are under $HOME the singular difference between them is the hidden aspect of ~/.local. That's pretty much it.