r_bash
44 subscribers
342 photos
16 videos
8.38K links
Download Telegram
This bash program isn't closing the file descriptor

    printf "This is the first line\r\nThis is the second line\r\n" > "test.txt"
: {fd}< "test.txt"
read <&$fd-
printf "$?; $REPLY\n"
read <&$fd-
printf "$?; $REPLY"


This program outputs:
    0; This is the first line
0; This is the second line


The first read command should have closed the file descriptor but it seems like it doesn't. I don't understand this behaviour.

Edit: This is what the manual says.

> The redirection operator
>
> n<&digit-
> moves the file descriptor digit to file descriptor n, or the standard input (file descriptor 0) if n is not specified. digit is closed after being duplicated to n.

---
Edit (Solution): Took me a long time but here's the real use case of >&fd- and why its effect goes away after one command.

First, let's discuss why the effect goes away after one command. When we redirect, if the redirect was eternal, it would block a fd permanently. For example printf "hey" >&3 would lead to stdout permanently becoming a copy of fd 3 which isn't ideal at all. Therefore, bash automatically restores the state before the redirect after the command is complete.

Now this leads to the question, what is the point of >&fd- then?

Here's a code snippet to showcase that.
    # Run in one terminal
mkfifo my_pipe
cat my_pipe

    # Run in a separate terminal
exec 3> my_pipe

(
echo "Worker is doing some fast work....
sleep 100 > /dev/null &

) >&3 & # <--- HERE IS THE COPY (>&3)

exec 3>&-

echo "Main script finished."

Because we don't close the fd 3, sleep can potentially write to it which leads to cat waiting for 100 seconds before being complete. This leads to terminal 1 being stuck for 100 seconds.

Had we used >&3-, we would have made a move operation and hence there would be no open fd to write to for sleep which leads to cat exiting instantly.

---
This is the best from my research about this.

I could still be wrong about the exact order of operations that I explained for things. If I am, someone correct me.

https://redd.it/1rvfhao
@r_bash
Why is this pattern expansion not working?

## Code snippet:

printf "%q\n" "${MAPFILE@}"
printf "\n"

printf "%q\n" "${MAPFILE@/%$'\r'}"
printf "\n"

MAPFILE=("${MAPFILE@/%$'\r'}")
printf "%q\n" "${MAPFILE@}"
printf "\n"

I wrote this code, MAPFILE basically contains line copied from clipboard.
Each line ends with a carriage return \r hence.

## Output:

$'\r'
$'# This is the first line.\r'
$'# This is the second line.\r'

''
\#\ This\ is\ the\ first\ line.
\#\ This\ is\ the\ second\ line.

$'\r'
$'# This is the first line.\r'
$'# This is the second line.\r'

1) At first you can see that each line contains an ending \r.
2) Then if I just print the expansion output directly, there are no \r at the end of each line.
3) But then if I print after assignment, it has again changed.

I want to add before any one suggests this, we can change MAPFILE manually, it is not a constant.
I have changed this array in other places as well and the program works fine.

And mind you I have tried this method of removing a character for other characters such as \t and it works.
It is for some god forsaken reason, not working only when I try to remove \r.

ALSO: I can remove \r using a loop instead where I do the same pattern expansion but line by line.

I am using git bash on windows.
If anyone has any ideas about why this isn't working, it'd be a huge help.

https://redd.it/1rzjbue
@r_bash
Practice Examples

Ive been coding in python on windows for a while and consider it my main programming language, but Ive been intending to pick up another programming language for a while ( I was going to move to c / c++)

Tell me why, after installing Ubuntu on wsl to try it out

and my friend started teaching me some bash

is it literally so fun to write?

And like its kind of useful too because I can just make functions for navigating my terminal and new aliases...

Anyways Im looking for practice problems to go over, suitable for a beginner so I can keep learning, if you have any suggestions.

https://redd.it/1rzqod6
@r_bash
Made a script to send reels to the group chat
https://redd.it/1s00htk
@r_bash
I want to share my unit test lib for TUI apps

https://github.com/fissible/ptyunit

Most bash test frameworks only assert on stdout. That breaks down as soon as your script:

\- renders to /dev/tty

\- uses cursor movement / ANSI

\- handles arrow keys or interactive input

ptyunit runs your script inside a real pseudoterminal, sends keystrokes (UP, DOWN, ENTER, etc), and lets you assert against what a user would actually see.

out=$(python3 pty_run.py examples/confirm.sh RIGHT ENTER)

assert_contains "$out" "Cancelled"

I originally built this because I couldn’t reliably test a git diff TUI I was working on. Capturing /dev/tty output made it possible to catch layout and rendering issues.

It also doubles as a minimal assertion framework, so you can use it standalone instead of pulling in another dependency.

Would be curious if anyone else here is testing interactive bash tools, or if you’ve run into this gap before.

Install:

\- bpkg install fissible/ptyunit

\- brew tap fissible/tap && brew install ptyunit

Feedback welcome.

https://redd.it/1s048iz
@r_bash
...i know ...quite useless, but: ...why not watch /dev/video per bash script ? ;-P
https://redd.it/1s6gif6
@r_bash
How to improve bash script which is collecting data every 10 seconds

I wrote a script which is collecting data from solar inverter every 10 seconds for 5 minutes, it does some math and send data to [emoncms](https://github.com/emoncms/emoncms). It does work but is not optimal in term of CPU usage, it is running on SBC and consume roughly 80% of CPU time. My question is how can I initiate next data collection without checking script running time in a loop. Below is simplified script. I need to improve line 7.

#!/bin/bash
set -o pipefail
IFS=$''
samples="0"
nr="0"
while [ $SECONDS -lt 292 ]; do #5min-8s
if [[ (( $(( (samples - 1) * 10 + 10 )) == $SECONDS )) || (( 0 == $SECONDS )) ]]; then
((samples++))
timestart=$SECONDS
output="$(./inverter_poller --run-once)" # get data from inverter
timeend=$SECONDS
echo ${output} > /var/log/inverter.last
rs232time=$((timeend - timestart)) # usually it is 6-7 seconds
if (( rs232time < 17 )); # inverter is not responding if it is 17s or more
then
gridv=`echo ${output} | grep grid_voltage | tr -d " "_\",:[:alpha:]`
***more data extraction and math***
else
echo inverter not responding >> /var/log/inverter.last
fi
looptime=$((SECONDS - timestart))
echo "time": $looptime >> /var/log/inverter.last
fi
done
***boring data processing and sending to emoncms was here***

https://redd.it/1s6dsf2
@r_bash
Function on .bashrc

Hello! I trying to add this function on my bashrc, but because of the quotes and single quotes, it's returning this error:
\-bash: .bashrc: line 142: unexpected EOF while looking for matching `''

\-bash: .bashrc: line 145: syntax error: unexpected end of file


The function is this one:
140 dwdb() {

141 local query="SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME;"

142 sqlcmd -S link -d table -U user -P 'password' -C -Q "$query"

143 }

https://redd.it/1s5emg1
@r_bash
Exact timing of $PS0 and $BASHCOMMAND

I've had this line at the bottom of my ~/.bashrc for a while:

trap 'test "$BASH
COMMAND" == "$PROMPTCOMMAND" && printf "\e]2;$PWD\a" || printf "\e]2;$BASHCOMMAND\a"' DEBUG ;;

It uses a DEBUG trap to print the running command ($BASHCOMMAND) to the terminal emulator's title bar or else print the working directory ($PWD) if nothing is executing. I know some terminal emulators do this for you, but my favorite (urxvt) does not.

This has worked very well for me, but I recently learned about the $PS0 prompt, which is also run before the command executes, like a DEBUG trap, and I thought it might be a better way to implement this same thing.

It doesn't seem to be working, though, and I think the issue is in the timing of when $PS0 is shown and when $BASH
COMMAND is updated. When I add:

PS0="[\e]2;$BASHCOMMAND\a\]"

in my ~/.bashrc, the string in the terminal's title bar does update, but it's always one command behind (and the first is an odd test command). Is there any solution to this, like a way to update the $BASH
COMMAND sooner or an alternative way to get the command (from readline maybe?).

Or is the way I've already been doing this with the DEBUG trap likely still the best? I've always wondered how inefficient that is. It's all builtins, so I would hope not very, but I'm not too sure.

https://redd.it/1s4cx2x
@r_bash
Functions from my bashrc

My list of functions has gotten pretty long, thought maybe I'd share, [as asked](https://www.reddit.com/r/bash/comments/1s3lo2t/comment/ocgkhi1/). Share some interesting functions of your own, or any feedback you think I could use.

\>> [bashrc excerpt gist](https://gist.github.com/ekipan/1c2e5a6164fbe7601b32e089d889994c), and [permalink at time of post](https://gist.github.com/ekipan/1c2e5a6164fbe7601b32e089d889994c/adac6cb830131238a3666390d7574ec8a95f15be)

# a few of them:
e() { echo >&2 "$@"; "$@"; } # echo and run
hl() { bat -Ppl "${1:-help}"; } # highlight eg: find --help | hl
iftty() { if [[ -t "$1" ]]; then "${@:2}"; else cat; fi; }
opts() { iftty 0 "$@" --help |& rg "^\s*-" | hl; } # eg: opts find
# see gist for the rest.

A few I use _constantly_: gits() h() opts(). A ps1() that puts a newline if the last command didn't, so my prompt is on the left margin while scrolling back. A bit of whimsy like q() that I [adapted from a reddit post](https://www.reddit.com/r/zsh/comments/1rs6gcn/). I like the interface I designed for the path() function but since I only used it exactly once in my bashrc I just took it back out.

My style is definitely a lot more dense and nongeneric than most people or LLMs would like, but I own these functions and dense, direct code is better IMO.

**Background:** After my old Windows Thinkpad started getting a bit sick, I switched to using my Steam Deck as my main PC, with a dock, TV, and bluetooth keyboard. It seems a pretty good Arch flavor, and I wasn't entirely new to Linux, but I've learned a lot. One pain point is lack of manpages, so one of the first things I put in my .bashrc was a bunch of aliases to open my browser or curl from <https://cheat.sh>.

I had a ten-year-old account ended up shadowbanned, presumably because I posted a bashrc excerpt with URLs in it, maybe also because I'd forgotten about the account for years, idk. Thus the pastebin: I'm wary of posting too much code directly.

https://redd.it/1s3u7f5
@r_bash