2

I'm writing a script to change on the fly the firefox home page through a text variable:

sed -i 's|\("browser.startup.homepage",\) "\(.*\)"|\1 "$ddrs"|' /home/max/.mozilla/firefox/*.default/prefs.js

I need to bypass the issue of expanding the $ddrs variable inside the sed single quote statement.

6
  • Can you explain a little more what you mean by "bypassing the issue of expanding"? As it is now, $ddrs should not be expanded, so what is there to "bypass"?
    – Celada
    Commented Dec 17, 2015 at 2:39
  • "bypass" mean "I want the variable would be expanded" :-)
    – Symb932
    Commented Dec 17, 2015 at 2:42
  • 1
    I think that could be clarified in the question. "bypass the issue of expanding" == "I want it expanded"??? Anyway, if you want it expanded, just put it outside the single quotes (ideally in a set of double quotes instead)!
    – Celada
    Commented Dec 17, 2015 at 2:44
  • What are some values of $dds. They would be URLs such as unix.stackexchange.com correct?
    – clarity123
    Commented Dec 17, 2015 at 2:44
  • @user454038 yes. A random one sorted from text file as follow: ddrs=$(shuf -n1 urls.txt)
    – Symb932
    Commented Dec 17, 2015 at 2:47

5 Answers 5

3

For less fiddling with escaping special characters such as double quotes (and $ if you use it), you could just put the variable itself in double quotes and the rest in single quotes. No spaces between.

's|\("browser.startup.homepage",\) "\(.*\)"|\1 '"$ddrs"' |'

On a separate note, since you don't refer back to the second value, there's no reason to use parentheses for it:

's|\("browser.startup.homepage",\) ".*"|\1 '"$ddrs"' |'

Likewise, if you want your variable value $ddrs to appear in double quotes in the file you're editing, you'll need to include those double quotes:

's|\("browser.startup.homepage",\) ".*"|\1 "'"$ddrs"'" |'

But then, it occurs to me, you probably have more than one value on a single line (or you could have.) Since sed regexes are greedy, your .* will match the rest of the line up to the last double quote. To make it non-greedy (for this specific use case), use a character class matching anything but a double quote:

's|\("browser.startup.homepage",\) "[^"]*"|\1 "'"$ddrs"'" |'

And, although an accidental match would be unlikely, the dots (.) in the first string aren't matching dots, they're matching the character class "any single character". To avoid that you escape them:

's|\("browser\.startup\.homepage",\) "[^"]*"|\1 "'"$ddrs"'" |'

It looks like you're using a comma delimiter, but then you match one space ONLY after the comma—not two spaces or zero. I suspect that any number of spaces there is possible, so for robustness, allow arbitrary spaces between the comma and the double quote:

's|\("browser\.startup\.homepage",\) *"[^"]*"|\1 "'"$ddrs"'" |'

There are no other obvious issues here. (1) The trailing space I will assume is deliberate; (2) I suppose | is a fairly safe character to assume won't be in URL (the contents of $ddrs). The escaping here is a bit of a nightmare to read, but for this specific use specifically in sed I think that's unavoidable.

Robustness is more important than readability, though readability is good to strive for where it doesn't sacrifice robustness.

2

Wouldn't it be simpler to just set your home page to a single URL that returned a redirect to a random page from your homepage URL database?

That way, the homepage randomisation would occur every time you clicked on Home (or opened a new tab/page that went to Home) rather than just every time you restarted the browser. It would also avoid having to mess with your firefox config all the time.

To do this, the simplest way would be to install a minimalist web server somewhere that supported CGI scripts, and write a simple CGI that issued a URL redirection on every request, with the URLs coming from a simple db (even a text file would do).

1
  • I was waiting for someone to point out the elephant in the room, but I didn't know enough about the alternative approaches to do it. :) I agree with this solution.
    – Wildcard
    Commented Dec 17, 2015 at 7:56
0

Try using quotes " to enclose the entire sed argument, and escape any existing quotes, like this:

sed -i "s|\(\"browser.startup.homepage\",\) \"\(.*\)\"|\1 \"$ddrs\" |" /home/max/.mozilla/firefox/*.default/prefs.js

Explanation

The original argument you have been passing to sed looks like this:

's|\("browser.startup.homepage",\) "\(.*\)"|\1 "$ddrs" |'
  • uses syntax s|search|replace|,
  • but enclosed using literal quote ', which blocks the bash expansion of $ddrs, that is why $ddrs was not Bash-expanded

We simply modified this argument to:

"s|\(\"browser.startup.homepage\",\) \"\(.*\)\"|\1 \"$ddrs\" |"
  • however since we are using " quotes, we need to escape original " by replacing them with \"
  • since it is now enclosed only in " quote, Bash will successfully expand $ddrs
2
  • It shouldn't matter if there are unescaped quotes in the variable.
    – Wildcard
    Commented Dec 17, 2015 at 3:11
  • @Wildcard you're right, thanks, and just updated answer now.
    – clarity123
    Commented Dec 17, 2015 at 3:19
0

You really should escape thoroughly any variable data you might wish to use in a regexp. For example:

printf %s\\n "$ddrs" |
sed  -e'# the RHS field needs backslashes'  \
     -e'# delimiter, and newlines escaped.' \
     -e'H;1h;$!d;g;s|[\/]|\\&|g;s|\n|\\&|g' \
     -e'# now the string is s/// safe, so ' \ 
     -e's|.*|s/\\("browser\\.startup\\.homepage",\\) ".*"/\\1 "&"/|' |
sed -i "" -f- ./path/to/your/edit/file
0

FOUND A SOLUTION BY MYSELF
Instead of insist in something cool as replace a targeted string inside a line I've opted to replace the entire line, so:

  1. First I've put the urls.txt file in the ~/.mozilla/firefox/*.default/ directory.
  2. Then I've wrote the follow code at the very begin of the /usr/lib/firefox/firefox.sh file:
ddrs=$(shuf -n1 ~/.mozilla/firefox/*.default/urls.txt)
nwli="user_pref(\"browser.startup.homepage\", "$ddrs");"
sed -i "/browser.startup.homepage/c\\$nwli" ~/.mozilla/firefox/*.default/prefs.js

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .