이 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를 광범위하게 사용할 수 있기를 바랍니다. 분명히 학습을 위해 포함했던 인쇄문을 제거할 수 있을 것 같습니다. 그 외에도 아래에 표시된 솔루션을 어떻게 개선하거나 단축할 수 있습니까?

특히, 나는 다음을 사용할 계획이었습니다.피스사이드그리고 파이썬파일 입력하지만 이것들로는 실행 가능한 솔루션을 구현할 수 없습니다.

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

이것은 내가 직접 생각해낸 것 중 단연 최고입니다.

저는 두 개의 함수를 작성하여 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)

물론 다른 검색을 사용하여 이를 반복하고 문자열을 바꿀 수도 있습니다.

나는 이것을 개선할 수 있는 방법이 있다고 확신합니다. 나는 다른 답변을 받아들이겠지만, 다른 답변이 없을 경우를 대비해 여기에 내 답변을 넣습니다.

내 코드에는 몇 가지 가정이 있습니다. 한 가지 큰 문제는 모든 Firefox 프로필 경로가 상대적이라고 가정한다는 것입니다. 이것이 유효한 가정이 아닌 경우에는profiles.ini에 대한 추가 처리가 필요합니다.

관련 정보