このPython / Xonshスクリプト(bashから変換された)をどのように改善できますか?

このPython / Xonshスクリプト(bashから変換された)をどのように改善できますか?

私はxonshとPythonを学び始めました。私は主にPythonでシェルスクリプトを作成したいのですが、既存の中小規模のbashスクリプトの多くを置き換えたいと思います。

私は以下に提供されたbashスクリプトで始めました(いくつかの説明が追加されました)。手動でPythonに変換しました(下記参照)。私のPythonを改善したり、より簡潔にすることができる部分を指摘してください。

#!/bin/bash

while IFS= read -r line;
do
  echo $line | grep '^Path=' >/dev/null 2>&1
  if [[ $? -eq 0 ]]; then \
    path=~/.mozilla/firefox/$(echo $line | sed 's/^Path=//')/user.js; #open the user.js file for each profile
    if [[ -f ${path} ]]; then \
      echo "Processing: path: ${path}";
      grep 'browser.ctrlTab.sortByRecentlyUsed' ${path} >/dev/null 2>&1 #check if this property already exists
      if [ $? -ne 0 ]; then \
        echo 'user_pref("browser.ctrlTab.sortByRecentlyUsed", true);' >> ${path}; #did not exist, so append it to end of file
      else
        sed -i 's/browser.ctrlTab.sortByRecentlyUsed",.*false/browser.ctrlTab.sortByRecentlyUsed", true/' ${path} #did exist, so change it to desired value if needed
      fi
      echo "updated ctrlTab.sortByRecentlyUsed for $path"

      grep 'browser.tabs.loadBookmarksInTabs' ${path} >/dev/null 2>&1
      if [ $? -ne 0 ]; then \
        echo 'user_pref("browser.tabs.loadBookmarksInTabs", false);' >> ${path};
      else
        sed -i 's/browser.tabs.loadBookmarksInTabs",.*true/browser.tabs.loadBookmarksInTabs", false/' ${path}
      fi
      echo "updated loadBookmarksInTabs for $path"

    fi
  fi
done < ~/.mozilla/firefox/profiles.ini #read this file to find the path to all profiles

これで動作するPythonソリューションがありましたが、予想よりもはるかに複雑に見えます。私の目標は、この状況に対する短くて迅速な解決策を作成することです。私は今では大規模なPythonプログラムを書くつもりはありません。十分に学んだら、xonshを広く使用できることを願っています。明らかに、学習のために含めた印刷ドアを削除することができるようです。それに加えて、以下に示すソリューションをどのように改善または短縮できますか?

特に、私は次のものを使う予定でした。ピースサイドそしてPythonファイル入力しかし、これらでは実行可能なソリューションを実装することはできません。

import sys
from pathlib import Path
import re

trgt_file='user.js'
myuser='mountainx'
base_path = Path('/home/' myuser '/.mozilla/firefox')
if not base_path.is_dir():
  print("ERROR:", base_path, "does not exist.")
  # exit()
else:
  print("OK: processing", base_path)

ini_path = Path(base_path / 'profiles.ini')
if not ini_path.is_file():
  print("ERROR:", ini_path, "cannot be opened.")
  # exit()
else:
  print("OK: opening", ini_path)

match_ctrltab = False
match_bookmark_found = False
pro_path_re = re.compile(r'^Path=(\w+)$')
ctrltab_sort_regex = re.compile(r'^/*?user_pref\("browser\.ctrlTab\.sortByRecentlyUsed.*$', re.M)
ctrltab_sort_repls = 'user_pref("browser.ctrlTab.sortByRecentlyUsed", true);'
bookmark_open_regex = re.compile(r'^/*?user_pref\("browser\.tabs\.loadBookmarksInTabs.*$', re.M)
bookmark_open_repls = 'user_pref("browser.tabs.loadBookmarksInTabs", false);'
with open(ini_path, "r") as profiles_ini:
  for any_line in profiles_ini:
    m_path = pro_path_re.match(any_line)
    if not m_path is None:
      p_path = m_path.group(1)
      print("p_path:", p_path)
      profile_path = Path(base_path / p_path)
      print("profile_path:", profile_path)
      if profile_path.is_dir():
        print("The profile path is", profile_path)
        user_js_path = Path(profile_path / trgt_file)
        if Path(user_js_path).is_file():
          print("OK: valid file:", user_js_path)
          with open(user_js_path, 'r+') as file_userjs:
            #read the file contents
            file_contents = file_userjs.read()
            match_ctrltab = ctrltab_sort_regex.search(file_contents)
            if match_ctrltab is None:
              file_contents = file_contents + '\n' + ctrltab_sort_repls
              print('No match: added line to end of file contents:', ctrltab_sort_repls)
            else:
              file_contents = ctrltab_sort_regex.sub(ctrltab_sort_repls, file_contents)
              print('Match found. Replaced line in file for ctrltab_sort')

            match_bookmark = bookmark_open_regex.search(file_contents)
            if match_bookmark is None:
              file_contents = file_contents + '\n' + bookmark_open_repls
              print('No match: added line to end of file contents:', bookmark_open_repls)
            else:
              file_contents = bookmark_open_regex.sub(bookmark_open_repls, file_contents)
              print('Match found. Replaced line for bookmark_open_regex')
            file_userjs.seek(0)
            file_userjs.truncate()
            file_userjs.write(file_contents)
        else:
          print("SKIPPED: invalid file:", user_js_path)

私がテストしている日付は次のとおりです。一般的なソリューションは、すべてのFirefox user.jsまたはprefs.jsファイルで機能します。

file_contents = """
user_pref("media.gmp-gmpopenh264.abi", "x86_64-gcc3");
user_pref("media.gmp-gmpopenh264.autoupdate", false);
user_pref("media.gmp-gmpopenh264.enabled", false);
user_pref("media.gmp-gmpopenh264.lastUpdate", 1571947201);
user_pref("media.gmp-gmpopenh264.version", "1.8.1");
user_pref("browser.ctrlTab.sortByRecentlyUsed",false);
//user_pref("browser.ctrlTab.sortByRecentlyUsed", true);"""

答え1

これは私が自分で思いついたものの中で断然最高です。

私は2つの関数を書いてmodtestというディレクトリに保存しました(そしてファイルをffutils.pyとして保存しました)。
touch modtest/__init__.py
バラよりhttps://stackoverflow.com/a/33770042これについて詳しく学んでください。

import sys
from pathlib import Path
import re

def find_profile_dirs(user=None, debug=False):
  profiles = []
  if user is None:
    base_path = Path('~/.mozilla/firefox').expanduser()
  else:
    base_path = Path('/home') / user / '.mozilla/firefox'
  if not base_path.is_dir():
    print("ERROR:", base_path, "does not exist.")
  elif debug:
    print("OK: processing", base_path)
  ini_path = Path(base_path / 'profiles.ini')
  if not ini_path.is_file():
    print("ERROR:", ini_path, "cannot be opened.")
  elif debug:
    print("OK: opening", ini_path)
  pro_path_re = re.compile(r'^Path=(\w+)$')
  with open(ini_path, "r") as profiles_ini:
    for any_line in profiles_ini:
      m_path = pro_path_re.match(any_line)
      if not m_path is None:
        p_path = m_path.group(1)
        if debug:
          print("p_path:", p_path)
        profile_path = Path(base_path / p_path)
        if debug:
          print("profile_path:", profile_path)
        if profile_path.is_dir():
          profiles.append(profile_path)
          print("The profile path is", profile_path)
  return profiles


# profiles_paths is a list of Paths of all Firefox profiles to process
def process_userjs(searchExp, replaceExp, profiles_paths, debug=False):
  match_result = None
  search_regex = re.compile(searchExp, re.M)

  trgt_file='user.js'
  for profile_path in profiles_paths:
    user_js_path = Path(profile_path / trgt_file)
    if Path(user_js_path).is_file():
      if debug:
        print("OK: valid file:", user_js_path)
      with open(user_js_path, 'r+') as file_userjs:
        file_contents = file_userjs.read()
        match_result = search_regex.search(file_contents)
        if match_result is None:
          file_contents = file_contents + '\n' + replaceExp
          if debug:
            print('No match: added new line to end of file contents:', replaceExp)
        else:
          file_contents = search_regex.sub(replaceExp, file_contents)
          if debug:
            print('Match found. Modified existing line in file.')

        file_userjs.seek(0)
        file_userjs.truncate()
        file_userjs.write(file_contents)
    elif debug:
      print("SKIPPED: invalid file:", user_js_path)

その後、次の機能を使用しました。

import sys
sys.path = ['/path/to/parent/of/modtest'] + sys.path # if needed
from modtest import ffutils

my_profiles = ffutils.find_profile_dirs()

srchexp = r'^/*?user_pref\("browser\.ctrlTab\.sortByRecentlyUsed.*$'
replexp = 'user_pref("browser.ctrlTab.sortByRecentlyUsed", true);'
ffutils.process_userjs(srchexp, replexp, my_profiles, True)

もちろん、別の検索を使用してこれを繰り返して文字列を置き換えることもできます。

私はこれを改善する方法があると確信しています。私は別の答えを受け入れますが、他の答えがない場合に備えて、ここに私の答えを入れます。

私のコードにはいくつかの仮定があります。 1つの大きな問題は、すべてのFirefoxプロファイルパスが相対的であると仮定することです。これが有効な仮定ではない場合は、profiles.iniの追加処理が必要です。

関連情報