簡體   English   中英

從多個 7-zip 文件中提取特定的文件擴展名

[英]Extract specific file extensions from multiple 7-zip files

我有一個 RAR 文件和一個 ZIP 文件。 在這兩個中有一個文件夾。 文件夾內有幾個 7-zip (.7z) 文件。 在每個 7z 中都有多個具有相同擴展名的文件,但它們的名稱各不相同。

RAR or ZIP file
  |___folder
        |_____Multiple 7z
                  |_____Multiple files with same extension and different name

我只想從數千個文件中提取我需要的那些……我需要那些名稱包含某個子字符串的文件。 例如,如果壓縮文件的名稱中包含'[!]''(U)''(J)' ,這就是確定要提取的文件的條件。

我可以毫無問題地提取文件夾,所以我有這個結構:

folder
   |_____Multiple 7z
                |_____Multiple files with same extension and different name

我在 Windows 環境中,但我安裝了 Cygwin。 我想知道如何輕松提取我需要的文件? 也許使用單個命令行。

更新

這個問題有一些改進:

  • 內部 7z 文件及其內部的相應文件的名稱中可以包含空格。
  • 有 7z 文件,其中只有一個文件不符合給定條件。 因此,作為唯一可能的文件,它們也必須被提取。

解決方案

謝謝大家。 bash 解決方案幫助了我。 我無法測試 Python3 解決方案,因為我在嘗試使用pip安裝庫時遇到了問題。 我不使用 Python,所以我必須研究並克服我在使用這些解決方案時遇到的錯誤。 現在,我找到了一個合適的答案。 謝謝大家。

此解決方案基於 bash、grep 和 awk,適用於 Cygwin 和 Ubuntu。

由於您需要首先搜索(X) [!].ext文件,如果沒有這樣的文件,則查找(X).ext文件,我認為不可能編寫一些單一的表達式來處理這個邏輯。

解決方案應該有一些 if/else 條件邏輯來測試存檔中的文件列表並決定提取哪些文件。

這是我測試腳本的 zip/rar 存檔中的初始結構(我制作了一個腳本來准備這個結構):

folder
├── 7z_1.7z
│   ├── (E).txt
│   ├── (J) [!].txt
│   ├── (J).txt
│   ├── (U) [!].txt
│   └── (U).txt
├── 7z_2.7z
│   ├── (J) [b1].txt
│   ├── (J) [b2].txt
│   ├── (J) [o1].txt
│   └── (J).txt
├── 7z_3.7z
│   ├── (E) [!].txt
│   ├── (J).txt
│   └── (U).txt
└── 7z 4.7z
    └── test.txt

輸出是這樣的:

output
├── 7z_1.7z           # This is a folder, not an archive
│   ├── (J) [!].txt   # Here we extracted only files with [!]
│   └── (U) [!].txt
├── 7z_2.7z
│   └── (J).txt       # Here there are no [!] files, so we extracted (J)
├── 7z_3.7z
│   └── (E) [!].txt   # We had here both [!] and (J), extracted only file with [!]
└── 7z 4.7z
    └── test.txt      # We had only one file here, extracted it

這是進行提取的腳本

#!/bin/bash

# Remove the output (if it's left from previous runs).
rm -r output
mkdir -p output

# Unzip the zip archive.
unzip data.zip -d output
# For rar use
#  unrar x data.rar output
# OR
#  7z x -ooutput data.rar

for archive in output/folder/*.7z
do
  # See https://stackoverflow.com/questions/7148604
  # Get the list of file names, remove the extra output of "7z l"
  list=$(7z l "$archive" | awk '
      /----/ {p = ++p % 2; next}
      $NF == "Name" {pos = index($0,"Name")}
      p {print substr($0,pos)}
  ')
  # Get the list of files with [!].
  extract_list=$(echo "$list" | grep "[!]")
  if [[ -z $extract_list ]]; then
    # If we don't have files with [!], then look for ([A-Z]) pattern
    # to get files with single letter in brackets.
    extract_list=$(echo "$list" | grep "([A-Z])\.")
  fi
  if [[ -z $extract_list ]]; then
    # If we only have one file - extract it.
    if [[ ${#list[@]} -eq 1 ]]; then
      extract_list=$list
    fi
  fi
  if [[ ! -z $extract_list ]]; then
    # If we have files to extract, then do the extraction.
    # Output path is output/7zip_archive_name/
    out_path=output/$(basename "$archive")
    mkdir -p "$out_path"
    echo "$extract_list" | xargs -I {} 7z x -o"$out_path" "$archive" {}
  fi
done

這里的基本思想是檢查 7zip 檔案並使用7z l命令(文件列表)獲取每個檔案的文件列表。

該命令的輸出相當冗長,因此我們使用awk進行清理並獲取文件名列表。

之后,我們使用grep過濾此列表以獲取[!]文件列表或(X)文件列表。 然后我們只需將這個列表傳遞給 7zip 來提取我們需要的文件。

使用這個命令行怎么樣:

7z -e c:\myDir\*.7z -oc:\outDir "*(U)*.ext" "*(J)*.ext" "*[!]*.ext" -y

在哪里 :

  • myDir 是你的解壓文件夾
  • outDir 是你的輸出目錄
  • ext 是你的文件擴展名

-y 選項用於強制覆蓋,以防您在不同的檔案中有相同的文件名。

經過一些嘗試,這是不知何故的最終版本。 以前沒有用,所以我將其刪除,而不是附加。 讀到最后,因為不是所有的東西都需要最終解決方案。

到主題。 我會使用Python。 如果這是一個時間任務,那么它可能是矯枉過正的,但在任何其他情況下 - 您可以記錄所有步驟以供將來調查、正則表達式、編排一些命令以提供輸入以及獲取和處理輸出 - 每次。 所有這些情況在 Python 中都很容易。 如果你有它。

現在,我將寫下如何獲得 env。 配置。 並非所有都是強制性的,但嘗試安裝做了一些步驟,也許對過程的描述本身可能是有益的。

我有MinGW - 32 位版本。 然而,這不是提取 7zip 的強制性要求。 安裝后轉到C:\\MinGW\\bin並運行mingw-get.exe

  • Basic Setup我已經安裝了msys-base (右鍵單擊,標記安裝,從安裝菜單 - 應用更改)。 這樣我就有了 bash、sed、grep 等等。
  • All Packagesmingw32-libarchive with dll as class. Since python mingw32-libarchive with dll as class. Since python libarchive` 包只是一個包裝器,你需要這個 dll 來實際包裝二進制文件。

示例適用於 Python 3。我使用的是 32 位版本。 您可以從他們的主頁獲取它。 我已經安裝在默認目錄中,這很奇怪。 所以建議是安裝在你磁盤的根目錄 - 比如 mingw。

其他事情 - conemu比默認控制台好得多。

在 Python 中安裝包。 pip就是用來做這個的。 從您的控制台轉到 Python 主頁,那里有Scripts子目錄。 對我來說是: c:\\Users\\<<username>>\\AppData\\Local\\Programs\\Python\\Python36-32\\Scripts 您可以使用例如pip search archive進行pip search archive ,並使用pip install libarchive-c

> pip.exe install libarchive-c
Collecting libarchive-c
  Downloading libarchive_c-2.7-py2.py3-none-any.whl
Installing collected packages: libarchive-c
Successfully installed libarchive-c-2.7

cd ..調用python ,可以使用/導入新庫:

>>> import libarchive
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "c:\Users\<<username>>\AppData\Local\Programs\Python\Python36-32\lib\site-packages\libarchive\__init__.py", line 1, in <module>
    from .entry import ArchiveEntry
  File "c:\Users\<<username>>\AppData\Local\Programs\Python\Python36-32\lib\site-packages\libarchive\entry.py", line 6, in <module>
    from . import ffi
  File "c:\Users\<<username>>\AppData\Local\Programs\Python\Python36-32\lib\site-packages\libarchive\ffi.py", line 27, in <module>
    libarchive = ctypes.cdll.LoadLibrary(libarchive_path)
  File "c:\Users\<<username>>\AppData\Local\Programs\Python\Python36-32\lib\ctypes\__init__.py", line 426, in LoadLibrary
   return self._dlltype(name)
  File "c:\Users\<<username>>\AppData\Local\Programs\Python\Python36-32\lib\ctypes\__init__.py", line 348, in __init__
    self._handle = _dlopen(self._name, mode)
TypeError: LoadLibrary() argument 1 must be str, not None

所以它失敗了。 我試圖解決這個問題,但失敗了:

>>> import libarchive
read format "cab" is not supported
read format "7zip" is not supported
read format "rar" is not supported
read format "lha" is not supported
read filter "uu" is not supported
read filter "lzop" is not supported
read filter "grzip" is not supported
read filter "bzip2" is not supported
read filter "rpm" is not supported
read filter "xz" is not supported
read filter "none" is not supported
read filter "compress" is not supported
read filter "all" is not supported
read filter "lzma" is not supported
read filter "lzip" is not supported
read filter "lrzip" is not supported
read filter "gzip" is not supported
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "c:\Users\<<username>>\AppData\Local\Programs\Python\Python36-32\lib\site-packages\libarchive\__init__.py", line 1, in <module>
    from .entry import ArchiveEntry
  File "c:\Users\<<username>>\AppData\Local\Programs\Python\Python36-32\lib\site-packages\libarchive\entry.py", line 6, in <module>
    from . import ffi
  File "c:\Users\<<username>>\AppData\Local\Programs\Python\Python36-32\lib\site-packages\libarchive\ffi.py", line 167, in <module>
    c_int, check_int)
  File "c:\Users\<<username>>\AppData\Local\Programs\Python\Python36-32\lib\site-packages\libarchive\ffi.py", line 92, in ffi
    f = getattr(libarchive, 'archive_'+name)
  File "c:\Users\<<username>>\AppData\Local\Programs\Python\Python36-32\lib\ctypes\__init__.py", line 361, in __getattr__
    func = self.__getitem__(name)
  File "c:\Users\<<username>>\AppData\Local\Programs\Python\Python36-32\lib\ctypes\__init__.py", line 366, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: function 'archive_read_open_filename_w' not found

嘗試使用set命令直接提供信息,但失敗了...所以我轉向pylzma - 因為不需要 mingw。 pip安裝失敗:

> pip.exe install pylzma
Collecting pylzma
  Downloading pylzma-0.4.9.tar.gz (115kB)
    100% |--------------------------------| 122kB 1.3MB/s
Installing collected packages: pylzma
  Running setup.py install for pylzma ... error
    Complete output from command c:\users\texxas\appdata\local\programs\python\python36-32\python.exe -u -c "import setuptools, tokenize;__file__='C:\\Users\\texxas\\AppData\\Local\\Temp\\pip-build-99t_zgmz\\pylzma\\setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record C:\Users\texxas\AppData\Local\Temp\pip-ffe3nbwk-record\install-record.txt --single-version-externally-managed --compile:
    running install
    running build
    running build_py
    creating build
    creating build\lib.win32-3.6
    copying py7zlib.py -> build\lib.win32-3.6
    running build_ext
    adding support for multithreaded compression
    building 'pylzma' extension
    error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools

又失敗了。 但這很容易 - 我已經安裝了 2015 年的 Visual Studio 構建工具,並且奏效了。 我已經安裝了sevenzip ,所以我創建了示例存檔。 所以最后我可以啟動 python 並執行以下操作:

from py7zlib import Archive7z
f = open(r"C:\Users\texxas\Desktop\try.7z", 'rb')
a = Archive7z(f)
a.filenames

並得到空列表。 仔細觀察......可以更好地理解 - pylzma不考慮空文件 - 只是為了讓您意識到這一點。 因此,將一個字符放入我的示例文件中,最后一行給出:

>>> a.filenames
['try/a/test.txt', 'try/a/test1.txt', 'try/a/test2.txt', 'try/a/test3.txt', 'try/a/test4.txt', 'try/a/test5.txt', 'try/a/test6.txt', 'try/a/test7.txt', 'try/b/test.txt', 'try/b/test1.txt', 'try/b/test2.txt', 'try/b/test3.txt', 'try/b/test4.txt', 'try/b/test5.txt', 'try/b/test6.txt', 'try/b/test7.txt', 'try/c/test.txt', 'try/c/test1.txt', 'try/c/test11.txt', 'try/c/test2.txt', 'try/c/test3.txt', 'try/c/test4.txt', 'try/c/test5.txt', 'try/c/test6.txt', 'try/c/test7.txt']

所以......休息是小菜一碟。 實際上,這是原始帖子的一部分:

import os
import py7zlib

for folder, subfolders, files in os.walk('.'):
    for file in files:
        if file.endswith('.7z'):
            # sooo 7z archive - extract needed.
            try:
                with open(file, 'rb') as f:
                    z = py7zlib.Archive7z(f)
                    for file in z.list():
                        if arch.getinfo(file).filename.endswith('*.py'):
                            arch.extract(file, './dest')
            except py7zlib.FormatError as e:
                print ('file ' + file)
                print (str(e))  

附帶說明 - Anaconda 是很棒的工具,但完整安裝需要 500+MB,所以太多了。

也讓我從我的 github 分享wmctrl.py工具:

cmd = 'wmctrl -ir ' + str(active.window) + \
      ' -e 0,' + str(stored.left) + ',' + str(stored.top) + ',' + str(stored.width) + ',' + str(stored.height)
print cmd
res = getoutput(cmd)

這樣你就可以編排不同的命令——這里是wmctrl 可以以允許數據處理的方式處理結果。

您在問題賞金頁腳中聲明可以使用 linux。 而且我也不使用窗戶。 對於那個很抱歉。 我正在使用Python3 ,你必須在 linux 環境中(我會盡快在 windows 上測試這個)。

檔案結構

datadir.rar
          |
          datadir/
                 |
                 zip1.7z
                 zip2.7z
                 zip3.7z
                 zip4.7z
                 zip5.7z

提取結構

extracted/
├── zip1
│   ├── (E) [!].txt
│   ├── (J) [!].txt
│   └── (U) [!].txt
├── zip2
│   ├── (E) [!].txt
│   ├── (J) [!].txt
│   └── (U) [!].txt
├── zip3
│   ├── (J) [!].txt
│   └── (U) [!].txt
└── zip5
    ├── (J).txt
    └── (U).txt

這是我如何做到的。

import libarchive.public
import os, os.path
from os.path import basename
import errno
import rarfile

#========== FILE UTILS =================

#Make directories
def mkdir_p(path):
    try:
        os.makedirs(path)
    except OSError as exc: # Python >2.5
        if exc.errno == errno.EEXIST and os.path.isdir(path):
            pass
        else: raise

#Open "path" for writing, creating any parent directories as needed.
def safe_open_w(path):
    mkdir_p(os.path.dirname(path))
    return open(path, 'wb')

#========== RAR TOOLS ==================

# List
def rar_list(rar_archive):
    with rarfile.RarFile(rar_archive) as rf:
        return rf.namelist()

# extract
def rar_extract(rar_archive, filename, path):
    with rarfile.RarFile(rar_archive) as rf:
        rf.extract(filename,path)

# extract-all
def rar_extract_all(rar_archive, path):
    with rarfile.RarFile(rar_archive) as rf:
        rf.extractall(path)

#========= 7ZIP TOOLS ==================

# List
def zip7_list(zip7file):
    filelist = []
    with open(zip7file, 'rb') as f:
        for entry in libarchive.public.memory_pour(f.read()):
            filelist.append(entry.pathname.decode("utf-8"))
    return filelist

# extract
def zip7_extract(zip7file, filename, path):
    with open(zip7file, 'rb') as f:
        for entry in libarchive.public.memory_pour(f.read()):
            if entry.pathname.decode("utf-8") == filename:
                with safe_open_w(os.path.join(path, filename)) as q:
                    for block in entry.get_blocks():
                        q.write(block)
                break

# extract-all
def zip7_extract_all(zip7file, path):
    with open(zip7file, 'rb') as f:
        for entry in libarchive.public.memory_pour(f.read()):
            if os.path.isdir(entry.pathname.decode("utf-8")):
                continue
            with safe_open_w(os.path.join(path, entry.pathname.decode("utf-8"))) as q:
                for block in entry.get_blocks():
                    q.write(block)

#============ FILE FILTER =================

def exclamation_filter(filename):
    return ("[!]" in filename)

def optional_code_filter(filename):
    return not ("[" in filename)

def has_exclamation_files(filelist):
    for singlefile in filelist:
        if(exclamation_filter(singlefile)):
            return True
    return False

#============ MAIN PROGRAM ================

print("-------------------------")
print("Program Started")
print("-------------------------")

BIG_RAR = 'datadir.rar'
TEMP_DIR = 'temp'
EXTRACT_DIR = 'extracted'
newzip7filelist = []

#Extract big rar and get new file list
for zipfilepath in rar_list(BIG_RAR):
    rar_extract(BIG_RAR, zipfilepath, TEMP_DIR)
    newzip7filelist.append(os.path.join(TEMP_DIR, zipfilepath))

print("7z Files Extracted")
print("-------------------------")

for newzip7file in newzip7filelist:
    innerFiles = zip7_list(newzip7file)
    for singleFile in innerFiles:
        fileSelected = False
        if(has_exclamation_files(innerFiles)):
            if exclamation_filter(singleFile): fileSelected = True
        else:
            if optional_code_filter(singleFile): fileSelected = True
        if(fileSelected):
            print(singleFile)
            outputFile = os.path.join(EXTRACT_DIR, os.path.splitext(basename(newzip7file))[0])
            zip7_extract(newzip7file, singleFile, outputFile)

print("-------------------------")
print("Extraction Complete")
print("-------------------------")

在主程序之上,我已經准備好了所有必需的功能。 我沒有使用所有這些,但我保留了它們以備您需要時使用。

我在python3使用了幾個 python 庫,但你只需要使用pip安裝libarchiverarfile ,其他的都是內置庫。

這是我的源代碼樹副本

控制台輸出

這是運行此 python 文件時的控制台輸出,

-------------------------
Program Started
-------------------------
7z Files Extracted
-------------------------
(J) [!].txt
(U) [!].txt
(E) [!].txt
(J) [!].txt
(U) [!].txt
(E) [!].txt
(J) [!].txt
(U) [!].txt
(J).txt
(U).txt
-------------------------
Extraction Complete
-------------------------

問題

到目前為止,我面臨的唯一問題是,在程序根目錄生成了一些臨時文件。 無論如何它都不會影響程序,但我會嘗試修復它。

編輯

你必須跑

sudo apt-get install libarchive-dev

安裝實際的libarchive程序。 Python 庫只是圍繞它的一個包裝器。 看看官方文檔

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM