13

I have a zip which contains files with very long names.

If I try to extract on the command line, I get the error "File name too long".
If I use the graphical archive manager it will not extract the files and it will not let me rename them. The same problem occurs if I mount the archive.

I can extract and rename files individually using:

unzip -p -c example.zip "long file name.ogg" > shortname.ogg

This is impractical with lots of files.

Is there a tool which will truncate filenames as they are extracted?

1
  • What's the output of unzip -l <ARCHIVE> and how do you want the names truncated? Do you want to retain the directory structure inside the archive during extraction? May 19, 2017 at 11:04

3 Answers 3

22

To extract

We can use zipinfo as a part of this process, it's a program from zip package.

zipinfo -2 example.zip

will only shows the files names in example.zip, something look like:

file1-long-name-...-bla-bla.html
file2-long-name-...-bla-bla.html

so we can use this feature to extract all files:

zipinfo -2 example.zip | while read i;
do
  long_fname=${i%.*}
  unzip -p -c example.zip "$i" > "${long_fname:0:250}.${i##*.}"
done;
  • long_fname=${i%.*}: Removes extension from long file name, so in case of file name being less that of 256 character; We're not going to get a duplicate extension.
  • ${long_fname:0:250}.${i##*.}: Creates a new file name with legitimate number of character also adds a . and file real extension.

Simply we are looping into files list and extract each of them with a new legitimate file name which is 256 character.


To rename

You can use zipnote command, it's a part of zip package too.

Get a backup of your zip file first.

Run this command:

zipnote example.zip > names

Open names using an editor, it's look like this:

@ file name long in zip and a lot of other strings in the file name
@ (comment above this line)
@ (zip file comment below this line)

Add new file names like this:

@ file name long in zip and a lot of other strings in the file name
@=new short name for above file
@ (comment above this line)
@ (zip file comment below this line)

Then to rename files use:

zipnote -w example.zip < names

You renamed them all, you can also write a simple script which do this automatically for you.

4
  • Those are incredibly useful, thanks! I couldn't get the zipnote example to work - and the first one removes the file extension - but that's good enough for what I need to do. Thanks! May 14, 2017 at 11:05
  • I updated the answer, now it will keep the extension too ;)
    – Ravexina
    May 14, 2017 at 11:24
  • You should use IFS= read -r i in case the file names start with white space or contain things that the shell may interpret as escape sequences. May 19, 2017 at 11:05
  • 2
    I was still getting errors at a length of 250. I just lowered it to 100 and everything worked. Apr 2, 2021 at 19:29
2

The other answer didn't work for me for some reason, even after decreasing the output file name to length 100, so instead I just extracted all files at once with unzip -p > all.txt and then split the file all.txt with csplit based on some pattern.

Note that unzip will add lines like

------=_Part_4952_1005066427.1638023175221

and

    boundary="----=_Part_4953_1221949914.1638023175221"

which should be removed from the output and probably used to split the file, but I didn't need to do that so how to do this properly is left as an exercise :)

1

Here is the version that handles long directory names as well. You can add it as a function to your ~/.bashrc or wherever you store your functions or aliases.

unziplong() {
    max_base_len=100
    max_ext_len=15
    zipinfo -1 "$1" | grep -v "/$" | while read orig_path
    do
        new_path=""
        current_path="$orig_path"
        while [[ "$current_path" != "." ]]
        do
            segment="$(basename -- "$current_path")"
            ext="$([[ "$segment" =~ .*\..{1,$max_ext_len}$ ]] && echo ".${segment##*.}" || echo)"
            base="${segment%$ext}"
            segment="/$(xargs <<< "${base:0:$max_base_len}")$(xargs <<< "$ext")"
            new_path="$segment$new_path"
            current_path="$(dirname -- "$current_path")"
        done
        new_path="${new_path:1}"
        new_dir="$(dirname -- "$new_path")"
        mkdir -p "$new_dir"
        unzip -p "$1" "$orig_path" > "$new_path"
    done
}
1
  • Very readable code. Nice work! Feb 23 at 15:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .