У меня есть заархивированный файл Data.zip
, который (если не распакован) содержит много файлов:
file_1.txt
file_2.txt
...
...
Я хочу получить команду CLI, чтобы превратить это в новую папку Data_zipped
, которая содержит отдельного человека. файлы в Data.zip
несжатые:
Data_zipped/file_1.zip
Data_zipped/file_2.zip
...
...
Но хитрость в том, что Data.zip
содержит так много файлов (и они все вместе такие большие), что я не могу сначала распаковать Data.zip, а затем сжать отдельные файлы внутри него одним махом: все должно происходить «на лету»:
Для всех файлов в Data.zip/
name_of_that_file.zip
Data_zipped
Как это сделать с помощью CLI?
Я изменил @ Сверхчистый скрипт Джорджа , чтобы лучше объяснить структуру папок:
#!/bin/bash
#Name of zip file
filename=$1
# Check if valid zip file is passed
if [[ $(file "$filename" | grep -o "Zip archive data") =~ "Zip archive data" ]]
then
# List the contents of the zip file
unzip -l "$filename"
# Get the number of files in zip file
count=$(unzip -l "$filename" | awk '{count = $2 - 2} END {print count}')
echo "$count"
fi
exit 0
Когда я запускаю его, я получаю (я использую токен Data.zip только с несколькими файлами в нем, но Вы поняли):
./GU_script.sh Data.zip
Archive: Data.zip
Length Date Time Name
--------- ---------- ----- ----
0 2017-11-21 22:58 Data/
120166309 2017-11-21 14:58 Data/Level1_file.csv
120887829 2017-11-21 14:58 Data/Level1_other_file.csv
163772796 2017-11-21 14:59 Data/Level1_yet_other_file.csv
193519556 2017-11-21 14:59 Data/Level1_here_is_another_file.csv
153798779 2017-11-21 14:59 Data/Level1_so_many_files.csv
131918225 2017-11-21 14:59 Data/Level1_many_more_to_go.csv
--------- -------
884063494 7 files
5
Итак, я бы хотел, чтобы Level1_file.csv
и другие файлы были заархивированы по отдельности. y (-> Level1_file.zip) и положить в папку.
Я закончил тем, что объединил ответы @ George's и @David Foerster:
#!/bin/bash
#Name of zip file
filename="$1"
# Check if valid zip file is passed
if file "$filename" | grep -wq "Zip archive data";
then
#!/bin/bash
src="$filename"
dst=.
LC_ALL=C unzip -l "$src" |
sed -re '1,/^-{6}/d; /^-{6}/,$d; /\/$/d; s/^\s*(\S+\s+){3}//' |
while IFS= read -r f; do
out="${f##*/}"; out="$dst/${f%%/*}_zipped/${out%.*}.zip"
if [ ! -d "${out%/*}" ]; then
mkdir -p "${out%/*}" || break
fi
zip --copy "$src" --out "$out" "$f" || break
done
else
echo "Invalid file type: \"zip\" file required"
exit 1
fi
Вы можете использовать операцию «копирования» из zip(1)
и некоторые искажения пути к файлу. Он имеет преимущество в том, что копирует сжатые потоки данных непосредственно в целевой архив без прерывистой распаковки.
#!/bin/bash
src=Data.zip
dst=.
LC_ALL=C unzip -l "$src" |
sed -re '1,/^-{6}/d; /^-{6}/,$d; /\/$/d; s/^\s*(\S+\s+){3}//' |
while read -r f; do
out="${f##*/}"; out="$dst/${f%%/*}_zipped/${out%.*}.zip"
if [ ! -d "${out%/*}" ]; then
mkdir -p "${out%/*}" || return
fi
zip --copy "$src" --out "$out" "$f" <&- || return
done
Я добавил LC_ALL=C
к вызову unzip
, потому что его выходной формат выглядит немного нестабильным в разных реализациях, и я хочу, по крайней мере, избегать зависящих от локали вариантов вывода.
Это должно быть в состоянии сделать то, что вы хотите:
#!/bin/bash
#Name of zip file
filename="$1"
# Check if valid zip file is passed
if file "$filename" | grep -wq "Zip archive data";
then
# List the contents of the zip file
unzip -l "$filename"
# Make the destination folder
# after checking they don't exist
if [ ! -d Data_zipped ];
then
mkdir Data_zipped
fi
#make temporary folder
#for extracted files
tempdir=$(mktemp -d)
# Make temporary file to hold the filenames
mysrc=$(mktemp)
# Get the filesnames from the zip folder
unzip -c Data.zip | cut -d" " -f3- | grep -E -o "[^Data/].*" | grep -Ev \(.zip\) | sed '/^\s*$/d' > "$mysrc"
while read -r var;
do
unzip -j "$filename" "Data/$var" -d "$tempdir/"
# Get name of file from each read line
zip Data_zipped/"$var".zip "$tempdir/$var"
# remove the original file
rm -rf "$tempdir/${var:?}"
done < "$mysrc"
else
echo "Invalid file type: \"zip\" file required"
exit 1
fi
Примечание :
Используется древовидная структура:
Data
├── file_10.txt
├── file_1.txt
...
Рассматривали ли вы поиск файловой системы fuse с поддержкой zip ?
. В основном это представляет файл zip как обычный каталог, из которого любое приложение может открывать и читать файлы, в то время как библиотека fuse обрабатывает грязные детали чтения и записи сжатого потока.
В Ubuntu вы можете установить его с помощью sudo apt install fuse-zip
После установки fuse-zip вы можете смонтировать zip-файл с помощью fuse-zip /path/to/some.zip mnt/
, где mnt - пустой каталог по вашему выбору.
После завершения размонтируйте его с помощью fusermount -u mnt/
, где mnt - каталог, куда вы его смонтировали.
fuse-zip даже создаст молнию на лету для вас, если он не будет существовать.
Вы можете разархивировать файлы, содержащиеся в Data.zip, один за другим: unzip Data.zip file1.txt
и сжать их.
mkdir Data_unzipped
for i in `seq 1 100` # or whatever the number of your files is
do
unzip Data.zip file_${i}.txt
zip Data_unzipped/file_${i}.zip file_${i}.txt
rm file_${i}.txt
done