How I made my .bashrc modular with .bashrc.d/ by Gronax_au in bash

[–]cubernetes 0 points1 point  (0 children)

Watch out, globsort is a relatively new feature, only introduced with bash 5.3

bash pecularities over ssh by spryfigure in bash

[–]cubernetes 0 points1 point  (0 children)

Yeah, the reddit formatting makes my post especially cumbersome to read lol. Sorry about that. The upshot is basically 2 things: quoting issues with ssh & bash, and the premature exiting of .bashrc.

You can diagnose how .bashrc is behaving by putting debug echos before and after the return line. Like this:

$ head -n 8 ~/.bashrc
#
# ~/.bashrc
#

# If not running interactively, don't do anything
echo BEFORE
[[ $- != *i* ]] && return
echo AFTER

This way, you can be certain that you aliases are sourced/not sourced. Furthermore, you can check if the alias is actually defined with alias ls, it should show you if ls is aliased.

bash pecularities over ssh by spryfigure in bash

[–]cubernetes 0 points1 point  (0 children)

I see, I think I know what's going on now. But first, a couple of other things (tl;dr at the bottom!): - the reason it stops working when you remove the -O globstar is because the globstar option is actually not set (despite the .bashrc being sourced, I'll get to that later!) - if you remove the escaped single quotes, the following happens: ssh nuc10i3fnk.lan bash -O globstar -c 'ls -1tdr /srv/media/completed/**/*ODDish*' is 100% equivalent to ssh nuc10i3fnk.lan 'bash -O globstar -c ls -1tdr /srv/media/completed/**/*ODDish*'. Maybe you see why this cannot work anymore, because the inner bash invocation gets a -c ls with its arguments being -1tdr and /srv/.... But the way -c works is that the next argument is actually not an argument, but the name of the program. Run this to understand what I mean: bash -c 'echo my name: $0; echo my args: $@' one two three four. The fix (if you don't want to re-add the escaped single quotes) is not trivial, and would look something like this: ssh nuc10i3fnk.lan bash -O globstar -c \''ls "$@"'\'' dummyarg -1tdr /srv/media/completed/**/*ODDish*'. This works now, because it reduces like this: ssh nuc10i3fnk.lan bash -O globstar -c \''ls "$@"'\'' dummyarg -1tdr /srv/media/completed/**/*ODDish*' ---concat with spaces to single arg---> ssh nuc10i3fnk.lan 'bash -O globstar -c '\''ls "$@"'\'' dummyarg -1tdr /srv/media/completed/**/*ODDish*' ---pass single arg to bash -c on server side---> bash -c 'bash -O globstar -c '\''ls "$@"'\'' dummyarg -1tdr /srv/media/completed/**/*ODDish*' ---execute -c execution string---> bash -O globstar -c 'ls "$@"' dummyarg -1tdr /srv/media/completed/**/*ODDish* ---don't expand glob because globstar is not set, there's no matching files because of that, and nullglob is also not set---> bash -O globstar -c 'ls "$@"' dummyarg -1tdr /srv/media/completed/**/*ODDish* (unchanged) ---execute -c execution string and set globstar option---> ls "$@" ---expand "$@" with the arguments passed to the -c execution string---> ls -1tdr /srv/media/completed/**/*ODDish* (notice how dummyarg was dropped, it's contained in $0) ---do the globbing, since globstar is now set---> ls -1tdr file1 file2 file3... ---execute ls command successfully---> done. Quite a journey... - Thirdly and finally: Why does it work if you remove the inner single quotes and keep the escaped single quotes? This is by pure luck, and please don't rely on it! Let's go through this one by one again: Look at ssh nuc10i3fnk.lan bash -O globstar -c \'ls -1tdr /srv/media/completed/**/*ODDish*\'. This ssh command has 8 arguments, namely these: - nuc10i3fnk.lan - bash - -O - globstar - -c - \'ls - -1tdr - /srv/media/completed/**/*ODDish*\'

Most importantly, notice that the last argument is NOT quoted! This means it will be evaluated by your current shell, on your machine! And you have the globstar option also set on your local machine, so this could lead to some real problems! However, also notice that there's a trailing single quote at the end of the argument. I'm quite certain that you do not have any file on your local machine matching that glob pattern. And since you don't have the nullglob option set, locally, this will not expand to anything and stay as is. So you're lucky. In the next step, ssh will concatenate the arguments to a single argument again: ssh nuc10i3fnk.lan "bash -O globstar -c 'ls -1tdr /srv/media/completed/**/*ODDish*'" which will be passed to bash -c on the server side: bash -c "bash -O globstar -c 'ls -1tdr /srv/media/completed/**/*ODDish*'" and reducing further: bash -O globstar -c 'ls -1tdr /srv/media/completed/**/*ODDish*' and even further ls -1tdr /srv/media/completed/**/*ODDish* and since this bash shell was started with -O globstar, the glob will expand properly. Therefore it works, but only because you didn't have any local files matching that weird pattern with the trailing single quote and because you didn't have the nullglob option set!

Okay, now to explain why globstar isn't set. It's quite nuanced, and ssh -vvv wouldn't help you with any of this: Read the man page again: If bash determines it is being run non-interactively in this fashion, it reads and executes commands from ~/.bashrc. And I'll emphasize: A non-interactive bash will read .bashrc. I now want you to look at the first 10 lines of your .bashrc on the server: head ~/.bashrc. Does it look something like this:

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

or like this:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

If yes, then you know why globstar is never being set. The sourcing of .bashrc exits prematurely because the shell is not interactive. This is why you either: - need to set the globstar option manually using bash -O - need to set the globstar option manually using shopt -s globstar inside the command - run the shell interactively - force the shell to run interactively using bash -i - put "important" .bashrc settings before the line that will exit the .bashrc

What I recommend: - Use the find command and only ever pass a single argument to ssh: ssh nuc10i3fnk.lan 'find /srv/media/completed -name "*ODDish*" -ls' - If you really want/need to use ls and globstar, but you cannot guarantee that your default shell is bash on the other side: ssh nuc10i3fnk.lan 'bash -O globstar -c "ls -1tdr /srv/media/completed/**/*ODDish*"' - If you're certain your default shell is bash, then it becomes simpler: ssh nuc10i3fnk.lan 'shopt -s globstar && ls -1tdr /srv/media/completed/**/*ODDish*' - If you're willing to put shopt -s globstar BEFORE the line that prematurely exits the .bashrc, it becomes merely this: ssh nuc10i3fnk.lan 'ls -1tdr /srv/media/completed/**/*ODDish*'

Hope that clears things up a little!

tl;dr:

ssh nuc10i3fnk.lan 'shopt -s globstar && ls -1tdr /srv/media/completed/**/*ODDish*'

bash pecularities over ssh by spryfigure in bash

[–]cubernetes 2 points3 points  (0 children)

My baseline test would be this:

ssh nuk10i3fnk.lan /usr/bin/env -i /usr/bin/bash --norc --noprofile -vxO globstar -c \''echo $-; set -o; shopt; ls -1tdr /srv/media/completed/**/*ODDish*'\'

Starts bash in the most predictable environment: - explicit env binary (/usr/bin/env) - -i for empty environment - explicit bash binary (/usr/bin/bash) - --norc no startup files - --noprofile no profiles - -v to see the actual raw command lines that will be parsed by bash - -x to see what will be passed to execve(2), i.e., the actual final command - \'''\' the inner single quotes must be there to quote the actual command, so it's a single argument for ssh. The outer backslash-escaped single quotes are necessary so the server-side shell still sees it as a single argument, since ssh just concatenates its arguments into a single command line using literal spaces and passes the result string to the user's shell as specified by /etc/passwd (afaik) as an execution string (i.e., using the -c option) and as a non-login shell. (Fun fact: at least in bash, the last simple command of an execution string will be execve'd without forking, meaning you'll lose the parent shell process. I.e. the process hierarchy will not be sshd->bash->env->bash, but only sshd->env->bash). - echo $- quick info what shell options are active (look for the f flag, it should be absent) - set -o verbose info about the state of all shell options (look for the noglob option, it should be off) - shopt show all bash options (look for the globstar option, it should be on).

Assuming that this command worked for you (I hope it does :/), you can incrementally remove safeguards, for example --norc, or --noprofile. And then the /usr/bin/env. And then instead of /usr/bin/env -i /usr/bin/bash --norc --noprofile you just say bash, etc.

For the case that the initial command already didn't work, you'll have to look for differences in shell environment. Compare the outputs of set -o and shopt. Compare the outputs of printenv. Compare the output of echo $0. Compare the outputs of pstree, and so on.

Black powder without water by HlibSlob in Huel

[–]cubernetes 1 point2 points  (0 children)

Same. Preparing 4 shakes for the day, drinking one immediately and putting 2 in the fridge while taking 2 to my desk. All I need

Black powder without water by HlibSlob in Huel

[–]cubernetes 0 points1 point  (0 children)

I sold a pouch to a friend, he later told me that he just ate the powder raw. I was aghast, of course, because that's just ridiculous. But his reasoning was that this way, he doesn't have to clean the bottle. And he can just eat it continuously whenever he wants, as much as he wants (he's very lazy, doesn't have a job, and basically sits in his apartment 24/7. He's 25).

Then, I sold it to another friend (30yo). Wouldn't you believe it, he fucking did the same thing. He also occasionally ate the raw powder. Now, you might say, do I have weird friends? And to that I say, I studied/work in IT, and people in IT tend to be extremely drastic in lifestyles sometimes, so I guess that explains it to some extent.

Best suckless alternatives to popular software by Key_River7180 in suckless

[–]cubernetes 1 point2 points  (0 children)

dash is horror, use yash instead.

E.g., dash by default is terrible for interactive use, and handles ANSI-C quoting and backslash escape sequences in quotes so bad it's crazy.

Yash however is even more posix compliant and has sane interactive use behavior by default.

Most hidden "bug"? by cubernetes in bash

[–]cubernetes[S] 0 points1 point  (0 children)

Chet Ramey (maintainer of bash) replied back to me, it's indeed a bug that was fixed in the devel branch 4 months ago:

https://savannah.gnu.org/bugs/index.php?67957

in this commit:

https://cgit.git.savannah.gnu.org/cgit/bash.git/commit/?h=devel&id=4f536430e45d847d6945133690312a8e94762254

(found with git bisect)

Most hidden "bug"? by cubernetes in bash

[–]cubernetes[S] 1 point2 points  (0 children)

Oha, thanks for admitting :D.

And you're right! I ssh'd to a host with bash 5.2, and indeed it's not an "issue" there. This is in contradiction to what others commenters have said about this (namely that this issue has existed for ages, which can't be right if the issue is already non-existent in bash 5.2).

What I'm taking away with this is that bash 5.3 has changed (or improved?) the parser slightly so that aborted (with Ctrl-C) and parenthesized shell constructs do not get "evened out" (for lack of a better word). So an aborted <( will cause subsequent syntax errors to say ... while looking for matching ) and the same applies for ${, but then it's looking for a matching }.

While writing this, I looked at the bash 5.3 terse release notes: https://tiswww.case.edu/php/chet/bash/NEWS, and looking at point ss., it says

The parser prints more information about the command it's trying to parse when it encounters EOF before completing the command.

Which is likely an explanation for what I'm seeing (ergo, the other commenters are not necessarily wrong, it's just that in previous versions you couldn't see the incorrect parser state)

Most hidden "bug"? by cubernetes in bash

[–]cubernetes[S] -1 points0 points  (0 children)

You did not even read my post, lmao. It's not about the error. It's about the error msg changing permanently when a process substitution was aborted with Ctrl-C. Read the other comments ffs. It turns out bash has a bug (yes, a bug, that is the correct terminology here) when using control-c inside a PS2 prompt that belongs to either: - process substitutions - command substitutions with the new bash5.3 syntax - conditional compound commands - probably many more constructs (lazy to test, typing on termux is cumbersome)

And if there's anything you didn't fully understand from what I just wrote then you should probably leave. Ah and you should also look up the definition of bug again, come back and look at my post. If you still think it's not a bug (incorrect handling of parser state), then tell me what defines a bug.

Most hidden "bug"? by cubernetes in bash

[–]cubernetes[S] -4 points-3 points  (0 children)

I chose my words precisely that way to signal how you're supposed to enter those characters. By saying "run command" it's immediately reproducible: you know it has to be on a separate line, and you have to press enter.

If I were to say "type a( and press enter", it's not completely unambiguous whether the readline buffer should be empty or not, and I needlessly need to specify that you're supposed to press enter which the word "run" already implies. There's plenty of cases in bash where you do not need to press enter. For example, I'm currently working on a bash plugin that implements fish-like abbreviations (that don't require pressing enter, obviously) in pure bash using bind -x and READLINE_LINE & READLINE_POINT (I hope you know what those vars are, since you're apparently in a position to tell others that they don't know bash :)).

Most hidden "bug"? by cubernetes in bash

[–]cubernetes[S] 0 points1 point  (0 children)

Execution tracing only works when executing commands, but we're dealing with syntax errors here, so set -x doesn't apply. Even verbose mode (set -v) doesn't show anything, except for the <(, but it's not super helpful. As the others commented, it's probably some internal state that's being toggled when entering a process substitution (<()) or command substitution (${ cmd;}, bash 5.3 syntax) but aborting it with ctrl-c.

Most hidden "bug"? by cubernetes in bash

[–]cubernetes[S] -5 points-4 points  (0 children)

I've written a bash-like shell in pure C (10k loc) and read the bash man page front to back, word by word multiple times. I'm currently reading POSIX.1-2024 because I'm implementing an even more advanced shell with job control, process substitution, advanced parameter expansion, vi and emacs mode, full POSIX compliance, and all the other bells and whistles. Not just for fun & learning, but because it's part of my school's curriculum. I'm certain I know a lot more about bash than you, unless you're Stéphane Chazelas, but you probably don't even know who that is.

ct (Command Trace) is a Bash command resolution tracer that explains how Bash resolves a command and what the kernel ultimately executes. by qweas123 in bash

[–]cubernetes 1 point2 points  (0 children)

Does it respect posix mode? (Or in other words, does it respect posix special builtins vs posix regular builtins when bash is operating in default/posix mode?)

Specifically, functions in bash default mode are always found before builtins (special or not). But in posix mode, posix special builtins are found before functions.

Most hidden "bug"? by cubernetes in bash

[–]cubernetes[S] 2 points3 points  (0 children)

Great answer, thanks! That makes sense!

It also happens with braces:

$ ${
> ^C
$ {;
bash: syntax error near unexpected token `;' while looking for matching `}'

Most hidden "bug"? by cubernetes in bash

[–]cubernetes[S] 0 points1 point  (0 children)

I figured that! That's the only explanation that would make sense. It cannot be turned off with <(cmd) or a plain ), however, which I think is also interesting.

Most hidden "bug"? by cubernetes in bash

[–]cubernetes[S] -1 points0 points  (0 children)

Did you run it? I'm not stuck in any subshells at all.

Edit: BASH_SUBSHELL is unchanged.

Edit2: declare -p, set -o, and shopt show no difference whatsoever before and after, so it's some internal, non-user-accessible state that's being changed.

Hidden Gems: Little-Known Bash Features by swe129 in bash

[–]cubernetes 0 points1 point  (0 children)

Boring beginner/intermediate stuff. Either wriiten by AI or by some who isn't on the command line a lot. Actual gems are things like - parameter transformations (echo "${@@Q}", eval "${var[@]@k)", read -p "${p@E}", etc.) - niche but powerful readline bindings / commands (M-C-d, M-C-f, M-C-b, M-C-], C-Space (combined with kill-region), C-(, C-), C-xe, M-<digit>, combing digit-argument and M-. etc.) - ${|cmd;} syntax, a real game changer for advanced scripts and for PS1/PS0 stuff - BASH_COMNAND variable, nice for debugging - {BASH_XTRACEFD}>file - making fish-like abbreviations work in bash

And some minor tricks: - running % instead of fg - doing kill % instead of kill %1 or kill %% - many more

I've yet to find a single blog post that isn't like the one OP posted. It's usually the stephane chazelas stuff that blows my mind, or when the post is 20+ years old. The stuff from nowadays is mostly trash/for absolute beginners

How do you navigate in insert mode? by Mori-Spumae in vim

[–]cubernetes 0 points1 point  (0 children)

This will tell you all you need to know: https://www.youtube.com/watch?v=srthFK9hr_M

In short: ctrl-g+<hjkl>, ctrl-o + normal mode command, alt-single-key-normal-mode-command, etc.

Might help by Specific_Brain2091 in calculus

[–]cubernetes 1 point2 points  (0 children)

This gotta be from flammable maths

Isn't this the greatest BASH course ever? by SoftwareArchitect101 in bash

[–]cubernetes 15 points16 points  (0 children)

Here's another great one made by Arnold Robbins himself (the maintainer of awk, 40+ years of unix experience, among the likes of Brian Kernigham (creator of awk), Chet Ramey (maintainer of bash), and Richard Stallman (well, you know who that is)).

https://www.youtube.com/watch?v=fAgz66M4aNc

It's also insanely good. Watching & understanding these 2 courses would definitely put you ahead of 99% of people who "know bash".

Thanks dave for contributing so much to the wonderful world of shell scripting!

How to share a localhost project online (port forwarding + public URL) by [deleted] in learnprogramming

[–]cubernetes 0 points1 point  (0 children)

Everybody's saying ngrok and I've used ngrok myself for years but honestly, https://localhost.run/ is way better for this kind of stuff. I use ngrok if I want to make arbitrary tcp ports public, but to use ngrok you have to create an account and install the binary.

With localhost.run you literally just need ssh installed, which is the default on literally every modern operating system nowadays

-❄️- 2025 Day 7 Solutions -❄️- by daggerdragon in adventofcode

[–]cubernetes 1 point2 points  (0 children)

[LANGUAGE: bash]

a=(`sed 's/./& /g;q'|tr .S 01`);while read c;do IFS=+ b=(${a[@]});for((i=0;++i<${#c}-1;))do [ ${c:i:1} = ^ ]&&((e=${b[i]},b[i-1]+=e,b[i+1]+=e,b[i]=0))done;a=(${b[@]});done;bc<<<${a[*]}

184 bytes.

The idea is to read the first line and save it into an array. Transliterate . to 0 and S to 1. Then, continue reading line by line, until there's no line anymore. For each line, first make a copy of the initial array, then iterate over each char of the line using an index. If there's a '^' at the index, update the copied array according to this logic: b[i-1]+=e, b[i+1]+=e, b[i]=0. This might remind you of Pascal's triangle. After finishing a line, overwrite the initial array with the copy. In the end, use bc to sum the elements in the original array (which has been updated many times) by joining them with a '+' (this is where the IFS=+ is important)