Rumah  >  Soal Jawab  >  teks badan

Parse arahan 'pip install' untuk mendapatkan julat dalam teks pakej yang dipasang

Saya sedang mengusahakan projek yang memerlukan saya mengekstrak nama dan lokasi pakej Python yang dipasang menggunakan arahan pip install.

Halaman web mengandungi elemen code yang mengandungi berbilang baris teks dan arahan bash. Saya ingin menulis kod JS yang boleh menghuraikan teks ini dan mencari pakej dan lokasinya dalam teks.

Sebagai contoh, jika teks ialah:

$ pip install numpy
pip install --global-option build_ext -t ../ pandas>=1.0.0,<2
sudo apt update
pip uninstall numpy
pip install "requests==12.2.2"

Nak dapat result macam ni:

[
    {
        "name": "numpy",
        "position": 14
    },
    {
        "name": "pandas",
        "position": 65
    },
    {
        "name": "requests",
        "position": 131
    }
]

Bagaimana saya boleh melaksanakan fungsi ini dalam JavaScript?

P粉431220279P粉431220279429 hari yang lalu553

membalas semua(2)saya akan balas

  • P粉773659687

    P粉7736596872023-09-08 15:53:55

    Anda boleh lihat kod yang saya jelaskan dalam jawapan ini.

    Berikut ialah satu lagi penyelesaian yang serupa, lebih berdasarkan ungkapan biasa:

    const pipOptionsWithArg = [
      '-c',
      '--constraint',
      '-e',
      '--editable',
      '-t',
      '--target',
      '--platform',
      '--python-version',
      '--implementation',
      '--abi',
      '--root',
      '--prefix',
      '-b',
      '--build',
      '--src',
      '--upgrade-strategy',
      '--install-option',
      '--global-option',
      '--no-binary',
      '--only-binary',
      '--progress-bar',
      '-i',
      '--index-url',
      '--extra-index-url',
      '-f',
      '--find-links',
      '--log',
      '--proxy',
      '--retires',
      '--timeout',
      '--exists-action',
      '--trusted-host',
      '--cert',
      '--client-cert',
      '--cache-dir',
    ];
    const optionWithArgRegex = `( (${pipOptionsWithArg.join('|')})(=| )\S+)*`;
    const options = /( -[-\w=]+)*/;
    const packageArea = /["']?(?<package_part>(?<package_name>\w[\w.-]*)([=<>~!]=?[\w.,<>]+)?)["']?(?=\s|$)/g;
    const repeatedPackages = `(?<packages>( ${packageArea.source})+)`;
    const whiteSpace = / +/;
    const PIP_COMMAND_REGEX = new RegExp(
      `(?<command>pip install${optionWithArgRegex}${options.source})${repeatedPackages}`.replaceAll(' ', whiteSpace.source),
      'g'
    );
    export const parseCommand = (command) => {
      const matches = Array.from(command.matchAll(PIP_COMMAND_REGEX));
    
      const results = matches.flatMap((match) => {
        const packagesStr = match?.groups.packages;
        if (!packagesStr) return [];
    
        const packagesIndex = command.indexOf(packagesStr, match.index + match.groups.command.length);
    
        return Array.from(packagesStr.matchAll(packageArea))
          .map((packageMatch) => {
            const packagePart = packageMatch.groups.package_part;
            const name = packageMatch.groups.package_name;
    
            const startIndex = packagesIndex + packagesStr.indexOf(packagePart, packageMatch.index);
            const endIndex = startIndex + packagePart.length;
    
            return {
              type: 'pypi',
              name,
              version: undefined,
              startIndex,
              endIndex,
            };
          })
          .filter((result) => result.name !== 'requirements.txt');
      });
    
      return results;
    };
    

    balas
    0
  • P粉194541072

    P粉1945410722023-09-08 10:50:01

    Berikut ialah penyelesaian alternatif, cuba gunakan gelung dan bukannya ungkapan biasa:

    Ideanya adalah untuk mencari baris yang mengandungi pip install teks, ini adalah baris yang kami minati. Kemudian, pecahkan perintah itu kepada perkataan dan gelung di atasnya sehingga anda mencapai bahagian pakej arahan itu.

    Pertama, kami akan menentukan ungkapan biasa untuk pakej. Ingat, pakej boleh jadi seperti pip install 'stevedore>=1.3.0,<1.4.0' "MySQL_python==1.2.2":

    const packageArea = /(?<=\s|^)["']?(?<package_part>(?<package_name>\w[\w.-]*)([=<>~!]=?[\w.,<>]+)?)["']?(?=\s|$)/;
    

    Perhatikan pengelompokan bernama , package_part 用于识别“带版本的包”字符串,而 package_name digunakan untuk mengekstrak nama pakej.


    Mengenai parameter

    Kami mempunyai dua jenis argumen baris arahan: pilihan dan bendera.

    Masalah dengan

    pilihan ialah kita perlu memahami bahawa perkataan seterusnya bukanlah nama pakej, tetapi nilai pilihan.

    Jadi, saya mula-mula menyenaraikan semua pilihan dalam arahan pip install:

    const pipOptionsWithArg = [
      '-c',
      '--constraint',
      '-e',
      '--editable',
      '-t',
      '--target',
      '--platform',
      '--python-version',
      '--implementation',
      '--abi',
      '--root',
      '--prefix',
      '-b',
      '--build',
      '--src',
      '--upgrade-strategy',
      '--install-option',
      '--global-option',
      '--no-binary',
      '--only-binary',
      '--progress-bar',
      '-i',
      '--index-url',
      '--extra-index-url',
      '-f',
      '--find-links',
      '--log',
      '--proxy',
      '--retires',
      '--timeout',
      '--exists-action',
      '--trusted-host',
      '--cert',
      '--client-cert',
      '--cache-dir',
    ];
    

    Saya kemudian menulis fungsi yang akan saya gunakan kemudian untuk memutuskan perkara yang perlu dilakukan apabila ia melihat hujah:

    const handleArgument = (argument, restCommandWords) => {
      let index = 0;
      index += argument.length + 1; // +1 是为了去掉 split 时的空格
    
      if (argument === '-r' || argument === '--requirement') {
        while (restCommandWords.length > 0) {
          index += restCommandWords.shift().length + 1;
        }
        return index;
      }
    
      if (!pipOptionsWithArg.includes(argument)) {
        return index;
      }
    
      if (argument.includes('=')) return index;
    
      index += restCommandWords.shift().length + 1;
      return index;
    };
    

    Fungsi ini menerima parameter yang diiktiraf dan selebihnya perintah, dibahagikan kepada perkataan.

    (Di sini anda mula melihat "kaunter indeks". Oleh kerana kita juga perlu mencari kedudukan setiap penemuan, kita perlu menjejaki kedudukan semasa dalam teks asal).

    Dalam beberapa baris terakhir fungsi, anda dapat melihat bahawa saya mengendalikan --option=something--option something kedua-dua kes.


    Parser

    Penghurai utama kini membahagikan teks mentah kepada baris dan kemudian kepada perkataan.

    Setiap operasi mesti mengemas kini indeks global untuk menjejaki kedudukan kita dalam teks, dan indeks ini membantu kita mencari dan mencari dalam teks tanpa terperangkap dalam subrentetan yang salah, menggunakan indexOf(str, counterIndex):

    export const parseCommand = (multilineCommand) => {
      const packages = [];
      let counterIndex = 0;
    
      const lines = multilineCommand.split('\n');
      while (lines.length > 0) {
        const line = lines.shift();
    
        const pipInstallMatch = line.match(/pip +install/);
        if (!pipInstallMatch) {
          counterIndex += line.length + 1; // +1 是为了换行符
          continue;
        }
    
        const pipInstallLength = pipInstallMatch.index + pipInstallMatch[0].length;
        const argsAndPackagesWords = line.slice(pipInstallLength).split(' ');
        counterIndex += pipInstallLength;
    
        while (argsAndPackagesWords.length > 0) {
          const word = argsAndPackagesWords.shift();
    
          if (!word) {
            counterIndex++;
            continue;
          }
    
          if (word.startsWith('-')) {
            counterIndex += handleArgument(word, argsAndPackagesWords);
            continue;
          }
    
          const packageMatch = word.match(packageArea);
          if (!packageMatch) {
            counterIndex += word.length + 1;
            continue;
          }
    
          const startIndex = multilineCommand.indexOf(packageMatch.groups.package_part, counterIndex);
          packages.push({
            type: 'pypi',
            name: packageMatch.groups.package_name,
            version: undefined,
            startIndex,
            endIndex: startIndex + packageMatch.groups.package_part.length,
          });
    
          counterIndex += word.length + 1;
        }
      }
    
      return packages;
    };
    

    balas
    0
  • Batalbalas