You are not logged in.
I am new to bash scripting and I am fairly certain this is a noob question, but I just couldn't find the answer anywhere. I am writing a dmenu script and I have a config string that specifies the appearance, i.e.,
DMENU="dmenu -i -nb #000000 -ft 'xft:DejaVu'"Now if I want to call dmenu from within a script I can just do
echo "aaa/nbbb" | $DMENUand get dmenu with 2 options. However, if I also want to specify the font with a space in its name, i.e.,
DMENU="dmenu -i -fn 'xft:Source Code Pro'"The call to $DMENU fails. I've tried all kinds of braces and quotes in different places, but so far nothing works. For example, I've tried
DMENU="dmenu -i -fn \'xft:Source Code Pro\'"
DMENU="dmenu -i -fn \"xft:Source Code Pro\""
DMENU="dmenu -i -fn xft:Source\ Code\ Pro"etc. Is there an arrangement of braces to make this work? What is the correct practice?
Last edited by drybalka (2019-05-27 15:28:03)
Offline
Please provide the actual code you'd use to call it - clearly that grep line isn't right, why would you `grep` the input for the content of the DMENU variable? Also your attempt at a newline in the input is wrong. You say that worked, so I gather you just typed out what you thought you used as a command, but it's not what you actually used.
We need to see the actual commands.
Also when you say dmenu fails, how does it fail? Does dmenu run and just appear incorrectly, or does it not run? What error messages do you get?
We need to see the actual commands and error messages.
https://bbs.archlinux.org/viewtopic.php?id=57855
Last edited by Trilby (2019-05-27 11:52:54)
"UNIX is simple and coherent" - Dennis Ritchie; "GNU's Not Unix" - Richard Stallman
Offline
Thanks for the reply! My bad, I should have posted the actual minimal example, sorry. The script code is:
#!/bin/bash
DMENU='dmenu -fn DejaVu'
echo -e "aaa\nbbb" | $DMENU
DMENU='dmenu -fn Source\ Code\ Pro'
echo -e "aaa\nbbb" | $DMENUNow by executing this script the first call gives dmenu with 2 options aaa and bbb, that then returns my choice to the prompt as expected. The second call, however, does not show dmenu at all and gives an error message (of incorrect call of dmenu, if I understand correctly?):
usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor] [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]The exit code is 1.
Offline
echo -e "aaa\nbbb" | eval $DMENUOr, if you're ok hijacking any positional parameters:
set -- dmenu -fn "Source Code Pro"
echo -e "aaa\nbbb" | "$@"Alternatively, if you are using bash or zsh, you could probably do something similar with an array instead of DMENU being a simple variable.
Last edited by Trilby (2019-05-27 13:05:01)
"UNIX is simple and coherent" - Dennis Ritchie; "GNU's Not Unix" - Richard Stallman
Offline
That's great, it works, thanks! Could you for educational purposes explain in a few words why it needs eval, please?
Last edited by drybalka (2019-05-27 13:09:12)
Offline
This is probably related with how dmenu is coded. I tried your code replacing "dmenu" with "rofi -dmenu" and it worked.
export DMENU='dmenu -l 10 -fn Inconsolata 14' && echo -e "a\nb\nc" | $DMENUfails
export DMENU='rofi -dmenu -l 10 -fn Inconsolata 14' && echo -e "a\nb\nc" | $DMENUworks
So you can replace dmenu with rofi or avoid using $DMENU and write entire dmenu line everytime.
EDIT: I took so much time in typing, and failed to look Trilby's reply, which is certainly more useful.
Last edited by Docbroke (2019-05-27 13:12:33)
Arch is home!
https://github.com/Docbroke
Offline
That's great, it works, thanks! Could you for educational purposes explain in a few words why it needs eval, please?
It doesn't need it, but that's one of several ways of accomplishing the goal. The problem really isn't about quotation, but tokenization and whitespace. Dmenu - for the invocation you are attempting - must be passed three command line arguments:
0: dmenu
1: -fn
2: Source Code Pro
Now, think how the following line would be tokenized by the shell:
dmenu -fn Source Code ProThat would give 5 parameters:
0: dmenu
1: -fn
2: Source
3: Code
4: Pro
So this is why you quote the "Source Code Pro" so that the interactive shell tokenizes the string differently and passes the original 3 parameters that we wanted. Note that - quite importantly - the shell does not pass dmenu the following parameters:
0: dmenu
1: -fn
2: "Source Code Pro"
That would be something entirely different. The quotes in the interactive shell affect it's tokenziation prior to calling and exec function (e.g., see `man exec`).
So what happens when you put that whole string into a variable and then try to execute it?
DMENU='dmenu -fn "Source Code Pro"'
$DMENU
usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]
[-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]Dmenu tells you the parameters it got were wrong. But it doesn't specify why. Here's what was actually sent to dmenu:
0: dmenu
1: -fn
2: "Source
3: Code
4: Pro"
If you want to confirm this yourself, replace dmenu with the program compiled from the code below:
#include <stdio.h>
int main(int argc, const char *argv[]) {
int i;
for (i = 0; i < argc; i++)
printf("%s\n", argv[i]);
return 0;
}For a more explicit demonstration, lets say we compiled that code above to a program called 'args':
$ ./args one "two three"
./args
one
two three
$ var='one "two three"'
$ ./args $var
./args
one
"two
three"The problem is in the order of parsing operations done by the shell. I don't know precisely how it works, but I gather there are two tokenization steps, one to break up what was literally typed, this one using whitespace to tokenize but also keeping quoted items as a single token, and another step right before the call to the exec function that only uses whitespace to tokenize. Between those, the variables are expanded. This is what I suspect - what is certain is that the variable expansion happens after the effect of quotes on tokenization.
So eval essentially adds another step to the processing expanding any variables first (I'm far far from an expert on eval, but I believe it does a full parsing of the line once, then returns the result, so in effect the input line gets parsed twice allowing the quotes within the variable to affect tokenization).
So that's eval.
Using an array is another solution as arrays are already broken into the individual components and they are easy to use to explicitly tell the shell what should be different tokens without having it rely on whitespace. Bash, Zsh, and potentially some other modern shells have lots of array features. POSIX shells only have one array; many people will tell you that POSIX shells cannot use arrays at all, they are wrong. POSIX shells do have one array, and it's the command line arguments passed to the current shell. Most scripts just use these as read-only input, but they can be overwritten with the `set -- one two three` format I suggested in a previous post.
Last edited by Trilby (2019-05-27 14:42:19)
"UNIX is simple and coherent" - Dennis Ritchie; "GNU's Not Unix" - Richard Stallman
Offline
Thank you for the detailed explanation, this reply is golden! Marking thread as solved.
Offline