patternbashMinor
Bash script for xrandr modification
Viewed 0 times
scriptxrandrformodificationbash
Problem
Simply, I call the script (brightness.sh) and pass a three character value as an argument. I've got commands to strip individual numbers for math processes, and then the final commands to apply the changes. The rest is just simple if-then stuff.
I know I've got a lot of commands calling xrandr data, and that's slowing down the whole script. I would love to reduce it to just one instance of data collection, but I'm not skilled enough to make a super complex piped command that could do all that in one line.
What should I do to make this run more efficiently? Like, would it be elegant to put all of the
```
#!/bin/bash
: '
gonna need some variables for later
collect some data
'
gv1=$(xrandr --verbose | grep -m 1 Gamma | awk '{print $2}')
gv2=$(xrandr --verbose | grep -m 2 Gamma | awk '{print $2}' | tail -n1)
c1=$(xrandr --verbose | grep -m 1 Brightness | awk '{print $2}')
c2=$(xrandr --verbose | grep -m 2 Brightness | awk '{print $2}' | tail -n1)
r1=$( echo $gv1 | cut -d':' -f1 )
g1=$( echo $gv1 | cut -d':' -f2 )
b1=$( echo $gv1 | cut -d':' -f3 )
r2=$( echo $gv2 | cut -d':' -f1 )
g2=$( echo $gv2 | cut -d':' -f2 )
b2=$( echo $gv2 | cut -d':' -f3 )
c=1.0
gm=0.01
br=0.1
# default values so the stupid thing doesn't freak out
cv1=$c1
cv2=$c2
: '
RGB is red green blue, C is Brightness, gm is delta gamma, br is delta brightness
only one value is coming in from the command line, which indicates what screen is changing with which value.
code being passed: i for inc, d for dec, b for balance (set to 1.0)
: r for red, g for green, b for blue
: 1 for DVI-I-1, 2 for DVI-I-2
If you use this script, run 'xrandr --verbose' in a terminal to see which screens are connected, then set the appropriate data here.
'
case $1 in
# #######
# brightness
# #######
dc2)
cv2=$
I know I've got a lot of commands calling xrandr data, and that's slowing down the whole script. I would love to reduce it to just one instance of data collection, but I'm not skilled enough to make a super complex piped command that could do all that in one line.
What should I do to make this run more efficiently? Like, would it be elegant to put all of the
--verbose output into a variable, or file, then grep that variable/file for even faster processing? I would think all that data wouldn't fit into a variable.```
#!/bin/bash
: '
gonna need some variables for later
collect some data
'
gv1=$(xrandr --verbose | grep -m 1 Gamma | awk '{print $2}')
gv2=$(xrandr --verbose | grep -m 2 Gamma | awk '{print $2}' | tail -n1)
c1=$(xrandr --verbose | grep -m 1 Brightness | awk '{print $2}')
c2=$(xrandr --verbose | grep -m 2 Brightness | awk '{print $2}' | tail -n1)
r1=$( echo $gv1 | cut -d':' -f1 )
g1=$( echo $gv1 | cut -d':' -f2 )
b1=$( echo $gv1 | cut -d':' -f3 )
r2=$( echo $gv2 | cut -d':' -f1 )
g2=$( echo $gv2 | cut -d':' -f2 )
b2=$( echo $gv2 | cut -d':' -f3 )
c=1.0
gm=0.01
br=0.1
# default values so the stupid thing doesn't freak out
cv1=$c1
cv2=$c2
: '
RGB is red green blue, C is Brightness, gm is delta gamma, br is delta brightness
only one value is coming in from the command line, which indicates what screen is changing with which value.
code being passed: i for inc, d for dec, b for balance (set to 1.0)
: r for red, g for green, b for blue
: 1 for DVI-I-1, 2 for DVI-I-2
If you use this script, run 'xrandr --verbose' in a terminal to see which screens are connected, then set the appropriate data here.
'
case $1 in
# #######
# brightness
# #######
dc2)
cv2=$
Solution
Your fundamental strategic error is trying to accomplish this task mostly in Bash. It's not impossible, but, as you have noticed, it's not a good tool for the job. Adversities include:
-
Parsing input from
As you noted, one simple way to avoid executing it many times is to save the output as a variable:
Another way is to pipe the output of
What to do instead? There are a number of programming languages that you could pick from. However, since you are already using
Hard-coding
You've hard-coded support for two screens. A more important concern, though, is that you would have to duplicate huge chunks of code to add support for more screens.
Suggested solution
This solution consists nearly entirely of an AWK script; Bash merely pipes
If you just want to do a dry run, change
Your other idea
You had the idea to make a script that modifies environment variables in the invoking shell. I don't think that that works: child processes don't change environment variables in the parent. If you want to do that, though, there are two ways:
Anyway, all of that is moot, since the suggested solution using AWK is more suitable anyway.
-
Parsing input from
xrandr --verbose. You have ended up running xrandr --verbose many times, extracting one bit of information each time, which is obviously suboptimal.As you noted, one simple way to avoid executing it many times is to save the output as a variable:
XRANDR_OUTPUT=$(xrandr --verbose).Another way is to pipe the output of
xrandr --verbose into a Bash while read loop.- Slicing and dicing text. Parsing the gamma color components using multiple
cutcommands is awkward.
- Arithmetic. Though Bash has support for integer arithmetic, floating-point arithmetic requires an external command.
What to do instead? There are a number of programming languages that you could pick from. However, since you are already using
awk, why not take full advantage of it? AWK is much more suited to nearly everything in this task.Hard-coding
You've hard-coded support for two screens. A more important concern, though, is that you would have to duplicate huge chunks of code to add support for more screens.
Suggested solution
This solution consists nearly entirely of an AWK script; Bash merely pipes
xrandr --verbose to it. You could probably reformulate it entirely in AWK by using getline to do the pipe.If you just want to do a dry run, change
system(cmd) at the end to print cmd.#!/bin/bash
xrandr --verbose | awk -v vars="$*" '
BEGIN {
# Constants
RESET = 1.0;
BRIGHTNESS_DELTA = 0.1;
GAMMA_DELTA = 0.01;
# Declare arrays
delete brightness; delete brightness_new;
delete gamma; delete gamma_new;
delete screens_to_adjust;
}
/Brightness/ { brightness[1 + length(brightness)] = $2; }
/Gamma/ { gamma[1 + length(gamma)] = $2; }
END {
split(vars, args);
for (arg in args) {
split(args[arg], verb_noun_screen, "");
verb = verb_noun_screen[1];
noun = verb_noun_screen[2];
screen = verb_noun_screen[3];
if (!index("dib", verb)) continue;
if (noun == "c") { # Brightness
brightness_new[screen] = \
(verb == "d") ? (brightness[screen] -= BRIGHTNESS_DELTA) :
(verb == "i") ? (brightness[screen] += BRIGHTNESS_DELTA) :
(brightness[screen] = RESET);
screens_to_adjust[screen] = 1;
} else if (color = index("rgb", noun)) { # Gamma component
split(gamma[screen], rgb, ":");
rgb[color] = \
(verb == "d") ? (rgb[color] -= GAMMA_DELTA) :
(verb == "i") ? (rgb[color] += GAMMA_DELTA) :
(rgb[color] = RESET);
gamma_new[screen] = gamma[screen] = sprintf("%g:%g:%g", rgb[1], rgb[2], rgb[3]);
screens_to_adjust[screen] = 1;
}
}
for (screen in screens_to_adjust) {
cmd = sprintf( \
"xrandr --output DVI-I-%d%s%s", \
screen, \
(screen in brightness_new ? \
sprintf(" --brightness %f", brightness_new[screen]) : ""), \
(screen in gamma_new ? \
sprintf(" --gamma %s", gamma_new[screen]) : "") \
);
system(cmd);
}
}
'Your other idea
You had the idea to make a script that modifies environment variables in the invoking shell. I don't think that that works: child processes don't change environment variables in the parent. If you want to do that, though, there are two ways:
eval $(some_script), wheresome_scriptis a program that prints outvar1=value1assignments (taking care to escape special characters properly!).
- Write a shell function instead of a program. Then that function executes in the same shell, not a child.
Anyway, all of that is moot, since the suggested solution using AWK is more suitable anyway.
Code Snippets
#!/bin/bash
xrandr --verbose | awk -v vars="$*" '
BEGIN {
# Constants
RESET = 1.0;
BRIGHTNESS_DELTA = 0.1;
GAMMA_DELTA = 0.01;
# Declare arrays
delete brightness; delete brightness_new;
delete gamma; delete gamma_new;
delete screens_to_adjust;
}
/Brightness/ { brightness[1 + length(brightness)] = $2; }
/Gamma/ { gamma[1 + length(gamma)] = $2; }
END {
split(vars, args);
for (arg in args) {
split(args[arg], verb_noun_screen, "");
verb = verb_noun_screen[1];
noun = verb_noun_screen[2];
screen = verb_noun_screen[3];
if (!index("dib", verb)) continue;
if (noun == "c") { # Brightness
brightness_new[screen] = \
(verb == "d") ? (brightness[screen] -= BRIGHTNESS_DELTA) :
(verb == "i") ? (brightness[screen] += BRIGHTNESS_DELTA) :
(brightness[screen] = RESET);
screens_to_adjust[screen] = 1;
} else if (color = index("rgb", noun)) { # Gamma component
split(gamma[screen], rgb, ":");
rgb[color] = \
(verb == "d") ? (rgb[color] -= GAMMA_DELTA) :
(verb == "i") ? (rgb[color] += GAMMA_DELTA) :
(rgb[color] = RESET);
gamma_new[screen] = gamma[screen] = sprintf("%g:%g:%g", rgb[1], rgb[2], rgb[3]);
screens_to_adjust[screen] = 1;
}
}
for (screen in screens_to_adjust) {
cmd = sprintf( \
"xrandr --output DVI-I-%d%s%s", \
screen, \
(screen in brightness_new ? \
sprintf(" --brightness %f", brightness_new[screen]) : ""), \
(screen in gamma_new ? \
sprintf(" --gamma %s", gamma_new[screen]) : "") \
);
system(cmd);
}
}
'Context
StackExchange Code Review Q#126066, answer score: 3
Revisions (0)
No revisions yet.