29

I have 348 zip files and I want to find a file that is in one of the zip files, unzip -l doesn't work with wild cards?

How can I list content of all .zip files and grep through the merged list of all files contained in the zips?

Eduard Florinescu
  • 7,707
  • 9
  • 46
  • 51

4 Answers4

18

Using zipinfo is a fine solution here. However, in general whenever you want to apply a command to a list of files and the command doesn’t accept a list of files, you can use a for loop:

for file in *.zip; do
    unzip -l "$file"
done \
| grep "\.zip\|setup"

If the file you are searching for has spaces in it like: your file, in the grep regular expression you need to escape every space with a backslash like grep "\.zip\|your\ file".

Eduard Florinescu
  • 7,707
  • 9
  • 46
  • 51
Konrad Rudolph
  • 427
  • 2
  • 14
12

You can use zipinfo. It is included in the default Ubuntu installation. Check the manual page for more info.

For example, to look for a pattern setup in a bunch of zip files in current directory, use this command:

find ./ -iname *zip 2> /dev/null -print0 | xargs -0 zipinfo | grep setup
Anwar
  • 75,875
  • 31
  • 191
  • 309
6

To list the files in a zip archive you can use the following command.

unzip -l

To grep a compressed archive you should use the compressed archive utilities built to work with that type of archive format.

For zip archives:

zipgrep --help  
usage: zipgrep [egrep_options] pattern zipfile [members...]
Uses unzip and egrep to search the zip members for a string or pattern.

For tar archives:

zgrep --help
Usage: /bin/zgrep [OPTION]... [-e] PATTERN [FILE]...
Look for instances of PATTERN in the input FILEs, using their
uncompressed contents if they are compressed.

OPTIONs are the same as for 'grep'.

There are a few other tools that work with archives as well. You can pipe the out put into grep to do the same thing.

zcat
zcat my.archive.zip | grep "some text"

Or you can use the search functionality of these tools

zless
zmore
nelaaro
  • 9,838
  • 5
  • 35
  • 42
1

Here's a dash script for searching inside .zip files (should work with bash, zsh, ksh shells):

Simply call the script with no parameters and the script will prompt for necessary input:

  • zip files search location (the location can be: a folder path parameter OR separate zip file paths parameters)
  • path filters for files inside the zip files
  • strings to search for inside the files in the zip files
#!/bin/dash

ExtractFirstAndLastPathComponent () {
    #$1 = input path
    #$2 = returns the first path component
    #$3 = returns the last path component
    
    eval current_path="\"\$$1\""
    
    first_path_component=""
    last_path_component=""
    
    if [ -n "$current_path" ]; then
        #Remove trailing '/':
        temp="${current_path%?}"
        while [ "${current_path#"$temp"}" = "/" ]; do
            current_path="${current_path%?}"
            temp="${current_path%?}"
        done
        first_path_component="${current_path%"/"*}"
        if [ -z "$first_path_component" ]; then
            last_path_component="${current_path#'/'}"
        else
            last_path_component="${current_path#"$first_path_component""/"}"
        fi
    fi
    
    eval $2="\"\$first_path_component\""
    eval $3="\"\$last_path_component\""
}

GetFileEncoding () {
    #$1 = input file path
    #$2 = returns the file encoding
    
    eval file_to_test=\"\$$1\"
    
    GetFileSizeInBytes file_to_test file_to_test_size_in_bytes
    
    #Get file mime encoding:
    if [ -d "$file_to_test" ]; then
        result="directory"
    elif [ ! "$file_to_test_size_in_bytes" -eq "0" ]; then
        file_mime_type="$(file -bL --mime-encoding "$file_to_test" 2>/dev/null)" || { file_mime_type="undetermined"; }
        case "$file_mime_type" in
            *"binary"* )
                #Only binary files containing the NULL character (^@) are considered binaries in this script:
                (cat -v "$file_to_test" | sed "/\^@/i'\^@'\$NL2") | { grep -q "\^@"; } && result="binary" || result="text"
            ;;
            *"ascii"* | *"utf"* )
                result="text"
            ;;
            * )
                result="undetermined"
            ;;
        esac
    else
        result="text"
    fi
    eval $2=\"\$result\"
}

GetOSType () {
    #$1 = returns current operating system
        
    case "$(uname -s)" in
    *"Darwin"* | *"BSD"* )
        eval $1="BSD-based"
        ;;
    *"Linux"* )
        eval $1="Linux"
        ;;
    * )
        eval $1="Other"
        ;;
    esac
}

GetFileSizeInBytes () {
    #$1 = the input file path
    #$2 = returns the input file size
    
    eval file="\"\$$1\""
    [ -z "$OS_TYPE" ] && GetOSType OS_TYPE
    if [ "$OS_TYPE" = "Linux" ] || [ "$OS_TYPE" = "Other" ]; then
        file_size_in_bytes="$(stat -c %s -- "$file")" 2>/dev/null || { file_size_in_bytes="-1"; }
    elif [ "$OS_TYPE" = "BSD-based" ]; then
        file_size_in_bytes="$(stat -Lf %z -- "$file")" 2>/dev/null || { file_size_in_bytes="-1"; }
    else
        file_size_in_bytes="-1"
    fi
    eval $2="$file_size_in_bytes"
}

trap1 () {
    printf "\n""Aborted.\n">"$print_to_screen"
    
    CleanUp
        
    #kill all children processes, suppressing "Terminated" message:
    kill -s PIPE -- -$$ 2>/dev/null
    
    exit
}

CleanUp () {
    
    if [ -n "$TEMPORARY_EXTRACT_PATH" ] && [ -n "$TEMPORARY_EXTRACT_FOLDER" ]; then
        rm -R -f "$output_dir/"*
    fi
    
    #Restore "INTERRUPT" (CTRL-C) and "TERMINAL STOP" (CTRL-Z) signals:
    trap - INT
    trap - TSTP
    
    #Clear the title:
    printf "\033]0;%s\007" "">"$print_to_screen"
    
    #Restore initial IFS:
    unset IFS

}

GetModifiedFileDateFull () {
    #$1 = the input file path
    #$2 = returns input file modified date
    
    eval input_file=\"\$$1\"
    [ -z "$OS_TYPE" ] && GetOSType OS_TYPE
    {
        if [ "$OS_TYPE" = "Linux" ] || [ "$OS_TYPE" = "Other" ]; then
            current_modified_date_full="$(stat -c "%y" "$input_file")" || { current_modified_date_full="-1"; }
        elif [ "$OS_TYPE" = "BSD-based" ]; then
            current_modified_date_full="$(stat -f "%Sm" "$input_file")" || { current_modified_date_full="-1"; }
        fi
    }||{
        current_modified_date_full="-1"
    }
    eval $2=\"\$current_modified_date_full\"
}

GetModifiedFileDateSSE () {
    #$1 = the input file path
    #$2 = returns input file modified date - Seconds Since Epoch
    
    eval input_file=\"\$$1\"
    [ -z "$OS_TYPE" ] && GetOSType OS_TYPE
    {
        if [ "$OS_TYPE" = "Linux" ] || [ "$OS_TYPE" = "Other" ]; then
            current_modified_date_SSE="$(stat -c "%Y" "$input_file")" || { current_modified_date_SSE="-1"; }
        elif [ "$OS_TYPE" = "BSD-based" ]; then
            current_modified_date_SSE="$(stat -f "%m" "$input_file")" || { current_modified_date_SSE="-1"; }
        fi
    }||{
        current_modified_date_SSE="-1"
    }
    eval $2=\"\$current_modified_date_SSE\"
}

GenerateMatchedFilesArrays () {
    IFS='
'
    i=0
    cd "$initial_dir"
    
    for current_search_path in $(eval find $search_path -prune -exec printf "%s\\\\n" {} + ); do
        
        if [ ! -d "$current_search_path" ]; then
            ExtractFirstAndLastPathComponent current_search_path fpc_current_search_path lpc_current_search_path
            cd "$initial_dir"; cd "$fpc_current_search_path"; current_search_path="$PWD/$lpc_current_search_path"
            find_zip_command="find \$current_search_path -type f -name \"*.zip*\""
        elif [ -d "$current_search_path" ]; then
            cd "$initial_dir"; cd "$current_search_path"; current_search_path="$PWD"
            find_zip_command="find \$current_search_path/. -type f -name \"*.zip*\""
        fi
        
        for file1 in $(\
            { \
            j=0;\
            PrintJustInTitle "Step 1: Finding archive files..."
            for file2 in $(\
                eval "$find_zip_command";\
            ); do \
                j=$(($j + 1)); \
                PrintJustInTitle "Step 1: Processing file $j..."; \
                GetFileSizeInBytes file2 file2_size_in_bytes; \
                if [ "$file2_size_in_bytes" -le "$max_arhive_size_in_bytes" ]; then \
                    not_match="false"; \
                    for k in $(seq 1 $search_strings_array_0); do \
                        eval current_search_string=\"\$search_strings_array_$k\";\
                        eval unzip -q -c \"\$file2\" "$unzip_total_inside_archive_file_path_filter" 2>/dev/null|grep -l -i "$current_search_string">/dev/null||{ not_match="true"; break; }; \
                    done; \
                    if [ "$not_match" = "false" ]; then printf '%s\n' "$file2"; fi; \
                fi; \
            done|sort --numeric-sort|uniq -d;\
            j=0;\
            PrintJustInTitle "Step 2: Finding archive files..."
            for file2 in $(\
                eval "$find_zip_command";\
            ); do \
                j=$(($j + 1)); \
                PrintJustInTitle "Step 2: Processing file $j..." ; \
                GetFileSizeInBytes file2 file2_size_in_bytes; \
                if [ "$file2_size_in_bytes" -le "$max_arhive_size_in_bytes" ]; then \
                    not_match="false"; \
                    for k in $(seq 1 $search_strings_array_0); do \
                        eval current_search_string=\"\$search_strings_array_$k\";\
                        eval unzip -q -c \"\$file2\" "$unzip_total_inside_archive_file_path_filter" 2>/dev/null|grep -l -i "$current_search_string">/dev/null||{ not_match="true"; break; }; \
                    done; \
                    if [ "$not_match" = "false" ]; then printf '%s\n' "$file2"; fi; \
                fi; \
            done|sort --numeric-sort|uniq -u;\
            }|sort --numeric-sort;\
            PrintJustInTitle "Preparing to store necessary file info..."
        ); do
            found="false"
            previous_file_path="$current_file_path"
            current_file_path="$file1"
            if [ -z "$previous_file_path" ]; then
                previous_file_path="$current_file_path"
                found="true"
            else
                if [ ! "$current_file_path" = "$previous_file_path" ]; then
                    found="true"
                fi
            fi
            
            if [ "$found" = "true" ]; then
                i=$(($i + 1))
                PrintJustInTitle "Storing necessary file info: file $i..."
                eval current_file_path=\"\$file1\"
                eval file_paths_$i=\"\$current_file_path\"
                GetModifiedFileDateSSE current_file_path current_file_modified_date_SSE
                eval file_modified_dates_SSE_$i=\"\$current_file_modified_date_SSE\"
                GetModifiedFileDateFull current_file_path current_file_modified_date_full
                if [ ! "${current_file_modified_date_full%"."*" "*}" = "$current_file_modified_date_full" ]; then
                    current_file_modified_date_full_part1="${current_file_modified_date_full%"."*" "*}"
                    current_file_modified_date_full_part2="${current_file_modified_date_full#$current_file_modified_date_full_part1"."*" "}"
                    current_file_modified_date_full="$current_file_modified_date_full_part1$current_file_modified_date_full_part2"
                fi
                eval file_modified_dates_full_$i=\"\$current_file_modified_date_full\"
                GetFileSizeInBytes current_file_path current_file_path_size_in_bytes
                eval file_size_$i=\"\$current_file_path_size_in_bytes\"
            fi
        done;
    done
    eval file_paths_0=$i
    
    cd "$initial_dir"
    PrintJustInTitle ""
}

PrintMatchedFilesInfo () {
    PrintJustInTitle "Loading files data..."
    for i in $(seq 1 $file_paths_0); do
        PrintJustInTitle "Loading files data: file $i..."
        eval current_file_path=\"\$file_paths_$i\"
        eval current_file_modified_date_SSE=\"\$file_modified_dates_SSE_$i\"
        eval current_file_modified_date_full=\"\$file_modified_dates_full_$i\"
        eval current_file_size=\"\$file_size_$i\"
        printf "%s \n" "$i"
        printf "%s \n" "$(printf '%05d' "$i")"
        printf "%s \n" "$current_file_path"
        printf "%s \n" "$current_file_modified_date_SSE"
        printf "%s \n" "$current_file_modified_date_full"
        printf "%s \n" "$(printf '%015d%s' "$current_file_size" "B")"
    done
}

CheckUtilities () {
    #Check if any of the necessary utilities is missing:
    error="false"
    
    for utility; do
        man -f $utility >/dev/null 2>/dev/null || { ErrorMessage "ERROR: the '$utility' utility is not installed!"; error="true"; }
    done>&2
}

ErrorMessage () {
    printf '\n%s\n' "$1">&2
}

PrintInTitle () {
    printf "\033]0;%s\007" "$1"
}

PrintJustInTitle () {
    PrintInTitle "$1">"$print_to_screen"
}


#Enable globbing (POSIX compliant):
set +f
#Enable globbing (zsh):
setopt no_nomatch 2>/dev/null

print_to_screen='/dev/tty' #print to screen only

max_arhive_size_in_bytes=100000000 #approx. 100 MB

Q="'"

NL2=$(printf '%s' "\n\n") #Store New Line for use with sed

GetOSType OS_TYPE
if [ "$OS_TYPE" = "Linux" ]; then
    TEMPORARY_EXTRACT_PATH='/dev/shm' #RAM MEMORY
    TEMP_EXTRACT_FOLDER='TEMP_EXTRACT_FOLDER'
    
    man -f zenity>/dev/null 2>/dev/null||{ ErrorMessage "ERROR: The \"zenity\" utility is not installed!"; error="true"; }
elif [ "$OS_TYPE" = "BSD-based" ] || [ "$OS_TYPE" = "Other" ]; then
    TEMPORARY_EXTRACT_PATH="$HOME" #$HOME folder
    TEMP_EXTRACT_FOLDER='TEMP_EXTRACT_FOLDER'
    
    man -f zenity>/dev/null 2>/dev/null||{ ErrorMessage "ERROR: The \"zenity\" utility is not installed - it can be installed with: \"brew install zenity\"!"; error="true"; }
fi

CheckUtilities stat mkdir rmdir unzip cat grep seq find sed file rm uniq sort

if [ "$OS_TYPE" = "Linux" ] || [ "$OS_TYPE" = "Other" ]; then
    man -f gedit >/dev/null 2>/dev/null && {
        editor_command1="gedit -w"
        editor_command2="gedit --new-window -w"
    } || {
        man -f kate >/dev/null 2>/dev/null && {
            editor_command1="kate"
            editor_command2="kate --new-window"
        } || {
            ErrorMessage "ERROR: the 'gedit' or 'kate' utility is not installed!"
            error="true"
        }
    }
elif [ "$OS_TYPE" = "BSD-based" ]; then
    editor_command1="edit -w"
    editor_command2="edit --new-window -w"
    man -f edit >/dev/null 2>/dev/null || {
        ErrorMessage "ERROR: the 'edit' utility is not installed (TextWrangler/BBEdit command-line tools)!"
        error="true"
    }
fi

if [ "$error" = "true" ]; then
    printf "\n">&2
    CleanUp; exit 1
fi

eval find /dev/null $find_search_archive_total_path_filter>/dev/null || {
    printf "%s\n\n" "ERROR: Invalid find parameters provided!">&2
    CleanUp; exit 1
}

###

initial_dir="$PWD"

output_dir=""
error="false"
{
    cd "$TEMPORARY_EXTRACT_PATH" && {
        if [ ! -e "$TEMP_EXTRACT_FOLDER" ]; then
            printf '%s\n' "The specified temporary directory: \"$TEMP_EXTRACT_FOLDER\" - does not exist in the specified location: \"$TEMPORARY_EXTRACT_PATH\" - do you want to create it? [ Yes / No ] (default=Enter=No): ">"$print_to_screen"
            read answer
            if [ "$answer" = "Yes" ] || [ "$answer" = "yes" ] || [ "$answer" = "Y" ] || [ "$answer" = "y" ]; then
                mkdir "$TEMP_EXTRACT_FOLDER" || error="true"
            fi
        fi
        cd "$TEMP_EXTRACT_FOLDER" && output_dir="$PWD" || {
            error="true"
        }
    } || error="true"
} 2>/dev/null
if [ "$error" = "true" ]; then
    printf '%s\n' "Error: Could not access temporary folder \"$TEMP_EXTRACT_FOLDER\" in the extract location: \"$TEMPORARY_EXTRACT_PATH\"!">&2
    read temp
    exit 1
fi

#Trap "INTERRUPT" (CTRL-C) and "TERMINAL STOP" (CTRL-Z) signals:
trap 'trap1' INT
trap 'trap1' TSTP

printf '\n%s\n' "For the next provided parameters:"
printf '\n%s\n' " - please quote:"
printf '%s\n' "     - groups of single quotes (') within \"...\""
printf '%s\n' "     - groups of double quotes (\") or groups of other characters within '...'"
printf '\n%s\n' " - please escape:"
printf '%s\n' "     - literal '\' with '\' ('\\\\')"
printf '%s\n' "     - if within double quotes (\"...\"): special characters have to be escaped with '\'"
printf '%s\n' "     - if within single quotes ('...'): special characters (other than '\') don't have to be escaped"
printf "\n"
printf '\n%s\n' "Current_path = \"$initial_dir\"";
printf '\n%s\n' "Where to search path(s) (separated by space): "
printf '%s' ">> where to search path(s): >> "
read search_path;
if [ -z "$search_path" ]; then
    printf '\n%s\n\n' "ERROR: Search path cannot be empty!"
    exit 1
fi

printf '\n%s\n' "Inside archive file path filter: (what file path to lookup inside the archive) (default=Enter='*'):">"$print_to_screen"
current_inside_archive_file_path_filter="not_defined"
find_total_inside_archive_file_path_filter=""
unzip_total_inside_archive_file_path_filter=""
i=0;
while [ -n "$current_inside_archive_file_path_filter" ]; do
    printf '%s' ">> add inside archive path filter (concatenated with logical OR): >> "
    IFS= read -r current_inside_archive_file_path_filter
    unset IFS #Reset IFS
    if [ -n "$current_inside_archive_file_path_filter" ]; then
        if [ -z "$find_total_inside_archive_file_path_filter" ]; then
            find_total_inside_archive_file_path_filter="-path ""$current_inside_archive_file_path_filter"
        else
            find_total_inside_archive_file_path_filter="$find_total_inside_archive_file_path_filter -o -path ""$current_inside_archive_file_path_filter"
        fi
        unzip_total_inside_archive_file_path_filter="$unzip_total_inside_archive_file_path_filter"" ""$current_inside_archive_file_path_filter"
    fi
done
if [ "$current_inside_archive_file_path_filter" = "not_defined" ]; then
    find_total_inside_archive_file_path_filter='-path '"'"'*'"'"
    unzip_total_inside_archive_file_path_filter="'"'*'"'"
fi

printf '\n%s\n' "Search string:">"$print_to_screen";
current_search_string="not_defined"
search_strings_array_0=0
i=0
while [ -n "$current_search_string" ]; do
    printf '%s' ">> add search string (concatenated with logical AND): >> "
    IFS= read -r current_search_string
    unset IFS #Reset IFS
    if [ -n "$current_search_string" ]; then
        i=$(($i + 1))
        eval search_strings_array_$i=\"\$current_search_string\"
    fi
done
if [ ! "$i" = "0" ]; then
    eval search_strings_array_0=$i
else
    search_strings_array_1=""
    search_strings_array_0=1
fi

printf '\n%s' "Also open files that match the file path filters but don't match the search string(s) (open them as a secondary group of files (in the same editor window))? [ Yes / No ] (default=Enter=No): "
read answer
if [ "$answer" = "Yes" ] || [ "$answer" = "yes" ] || [ "$answer" = "Y" ] || [ "$answer" = "y" ]; then
    open_non_match_string_files="true"
else
    open_non_match_string_files="false"
fi

IFS='
'

cd "$TEMPORARY_EXTRACT_PATH" && {
    
    rmdir "$TEMP_EXTRACT_FOLDER" || {
        printf '%s\n' "Error: Could not remove \"$TEMP_EXTRACT_FOLDER\" directory in: \"$TEMPORARY_EXTRACT_PATH\" - directory not empty (for security reasons - it has to be removed manualy)!">&2
        exit 1
    } && {
        mkdir "$TEMP_EXTRACT_FOLDER"
    }
} || {
    printf '%s\n' "Error: Could not remove \"$TEMP_EXTRACT_FOLDER\" directory in: "$TEMPORARY_EXTRACT_PATH" - \"$TEMPORARY_EXTRACT_PATH\" is not accessible !">&2
    exit 1
}

unset IFS

IFS='
'

cd "$initial_dir"

printf "\n">"$print_to_screen"
GenerateMatchedFilesArrays
pmfi_result="defined"
count=0
while [ -n "$pmfi_result" ]; do
    PrintJustInTitle "Loading files data..."
    
    count=$(($count + 1))
    if [ ! "$count" = "1" ]; then
        printf '%s\n\n' ">>>">"$print_to_screen"
    fi
    
    pmfi_result=$(zenity --list --separator='|' --multiple --width "1900" --height "900" --hide-column='1' --print-column='1' --column "Hidden column" --column "File Path number" --column "File Path" --column "Modified Date number" --column "Modified Date" --column "File Size" $(PrintMatchedFilesInfo; PrintJustInTitle "Building zenity list dialog..."))
    
    sequence=""
    pmfi_result_temp="$pmfi_result"
    while [ -n "$pmfi_result_temp" ]; do
        number="${pmfi_result_temp%%"|"*}"
        if [ -z "$sequence" ]; then
            sequence="$number"
        else
            sequence="$sequence"" ""$number"
        fi
        pmfi_result_temp="${pmfi_result_temp#"$number"}"
        pmfi_result_temp="${pmfi_result_temp#"|"}"
    done
    sequence="${sequence%" "}"
    
    j=0
    unset IFS
    for i in $(printf "$sequence"); do
        
        padded_number="$(printf '%05d' "$i")"
        
        eval current_file_path=\"\$file_paths_$i\"
        
        ExtractFirstAndLastPathComponent current_file_path fpc_current_file_path lpc_current_file_path
        
        cd "$fpc_current_file_path"
        fpc_current_file_path="$PWD"
        
        ExtractFirstAndLastPathComponent fpc_current_file_path fpc_fpc_current_file_path lpc_fpc_current_file_path
        archive_name="${lpc_current_file_path%".zip"*}"
        if [ ! -e "$output_dir/$padded_number/$lpc_fpc_current_file_path/$archive_name" ]; then
            cd "$output_dir"
            mkdir "$padded_number"
            cd "$padded_number"
            mkdir "$lpc_fpc_current_file_path"
            cd "$lpc_fpc_current_file_path"
            mkdir "$archive_name"
            cd "$archive_name"
            unzip -q "$current_file_path" -d "$output_dir/$padded_number/$lpc_fpc_current_file_path/$archive_name" 2>/dev/null
        else
            cd "$output_dir/$padded_number/$lpc_fpc_current_file_path/$archive_name"
        fi
        IFS='
'
        new_group=""
        
        for file3 in $(eval find . -type f "$find_total_inside_archive_file_path_filter"); do
            GetFileEncoding file3 file3_encoding
            if [ "$file3_encoding" = "text" ]; then
                path4="$(printf '%s\n' "$output_dir/$padded_number/$lpc_fpc_current_file_path/$archive_name/$file3"|sed "s/'/$Q\"\$Q\"$Q/g")"
                not_match="false"
                for k in $(seq 1 $search_strings_array_0); do
                    eval current_search_string=\"\$search_strings_array_$k\"
                    cat "$output_dir/$padded_number/$lpc_fpc_current_file_path/$archive_name/$file3"|grep -l -i "$current_search_string">/dev/null||{ not_match="true"; break; }
                done
                if [ "$not_match" = "false" ]; then
                    new_group="$new_group"" ""'$path4'";
                fi
            fi
        done
        
        if [ "$open_non_match_string_files" = "true" ]; then
            for file3 in $(eval find . -type f "$find_total_inside_archive_file_path_filter"); do
                GetFileEncoding file3 file3_encoding
                if [ "$file3_encoding" = "text" ]; then
                    path4="$(printf '%s\n' "$output_dir/$padded_number/$lpc_fpc_current_file_path/$archive_name/$file3"|sed "s/'/$Q\"\$Q\"$Q/g")"
                    not_match="false"
                    for k in $(seq 1 $search_strings_array_0); do
                        eval current_search_string=\"\$search_strings_array_$k\"
                        cat "$output_dir/$padded_number/$lpc_fpc_current_file_path/$archive_name/$file3"|grep -l -i "$current_search_string">/dev/null||{ not_match="true"; break; }
                    done
                    if [ "$not_match" = "true" ]; then
                        new_group="$new_group"" ""'$path4'";
                    fi
                fi
            done
        fi
        
        if [ -n "$new_group" ]; then
            j=$(($j + 1))
            eval groups_$j=\"\$new_group\"
        fi
    done
    
    groups_0=$j
    
    if [ ! "$groups_0" = "0" ]; then
        eval $editor_command2 &
        sleep 0.5
        for i in $(seq 1 $groups_0); do
            eval current_group="\"\$groups_$i\""
            if [ "$i" = "1" ]; then
                eval $editor_command1 "$current_group" &
                sleep 0.5
            else
                eval $editor_command2 "$current_group" &
                sleep 0.5
            fi
        done
    fi
    IFS='
'
done
unset IFS

sleep 1; CleanUp
cd "$initial_dir"

Or, as a 100% command line utility, after saving the next script - call it with the --help flag to find out how to use it:

#!/bin/dash

PrintInTitle () {
    printf "\033]0;%s\007" "$1"
}

PrintJustInTitle () {
    PrintInTitle "$1">"$print_to_screen"
}

CleanUp () {
    trap - INT
    trap - TSTP
    if [ -n "$TEP" ] && [ -n "$TEF" ]; then
        rm -R -f "$output_dir/"*
    fi
    unset IFS
    PrintJustInTitle ""
    if [ "$1" = "1" ]; then
        printf "Aborted\n">"$print_to_screen"
        kill -s PIPE -- -$$ 2>/dev/null
    fi
}

DisplayHelp () {
    printf '\n%s\n' "Parameters Syntax:"
    printf '\n%s\n\n' "    [ flags ] <search_string> <inside_archive_path_filter> <search_path(s)>"
    printf '%s\n' "For <search_string>, <inside_archive_path_filter> and <search_path(s)>:"
    printf '%s\n' "    Please quote groups of single quotes (') within \"...\" and groups of double quotes (\") or groups of other characters within '...'"
    printf '%s\n' "    If within double quotes (\"...\"): special characters have to be escaped with backslash (\) / if within single quotes ('...') special characters don't have to be escaped"
    printf '%s\n' "Also, for <search_string>:"
    printf '%s\n' "    Literal '\' have to be escaped with '\' ('\\\\')"
    printf '\n%s\n' "Flags:"
    printf '%s\n' "    -q / --quick (this is the default flag)"
    printf '%s\n' "        - prints only the archive paths (quicker than the rest of the flag options)"
    printf '%s\n' "        - <inside_archive_path_filter> is not required (it is considered '*')"
    printf '%s\n' "    -s / --short"
    printf '%s\n' "        - prints the archive paths and the inner file paths, without printing the matched file content"
    printf '%s\n' "    -d / --detailed"
    printf '%s\n' "        - prints the archive paths and the inner file paths, and also prints the matched file content"
    printf '%s\n' "    -h / --help"
    printf '%s\n\n' "        - displays this help information"
}


#Enable globbing (POSIX compliant):
set +f
#Enable globbing (zsh):
setopt no_nomatch 2>/dev/null

IFS='
'

print_to_screen='/dev/tty'
initial_dir="$PWD"

case "$(uname -s)" in
    *"Linux"* )
        #TEMPORARY_EXTRACT_PATH
        TEP='/dev/shm' #RAM MEMORY
    ;;
    *"Darwin"* | *"BSD"* | * )
        #TEMPORARY_EXTRACT_PATH
        TEP="$HOME" #$HOME folder
    ;;
esac
#TEMP_EXTRACT_FOLDER
TEF='TEMP_EXTRACT_FOLDER'

trap 'CleanUp' INT
trap 'CleanUp' TSTP

quick_flag="true" #default flag
short_flag="false"
detailed_flag="false"
help_flag="false"

search_string=""
inside_archive_path_filter=""

params_count=0
j=0
for param; do
    case "$param" in
        "-q" | "quick_flag=" )
            quick_flag="true"
        ;;
        "-s"|"--short" )
            short_flag="true"
            quick_flag="false"
        ;;
        "-d"|"--detailed" )
            detailed_flag="true"
            quick_flag="false"
        ;;
        "-h"|"--help" )
            help_flag="true"
        ;;
        * )
            params_count=$(($params_count + 1))
            if [ "$params_count" = "1" ]; then
                search_string="$param"
            elif [ "$params_count" = "2" ]; then
                if [ ! "$quick_flag" = "true" ]; then
                    inside_archive_path_filter="'""$param""'"
                else
                    printf '\n%s\n\n' "Warning: For the -q flag: Parameter 2: \"$param\" is treated as a <search_path> parameter (not as <inside_archive_path_filter> (which is skipped))!">"$print_to_screen"
                    j=$(($j + 1))
                    eval search_paths_$j=\"\$param\"
                fi
            else
                j=$(($j + 1))
                eval search_paths_$j=\"\$param\"
            fi
        ;;
    esac
done
search_paths_0=$j

if [ "$help_flag" = "true" ] || [ "$params_count" = "0" ]; then
    DisplayHelp
    exit 0
fi

printf '\n%s\n\n' "Current_path = \"$PWD\"";

error="false"
{
    cd "$TEP" && {
        if [ ! -e "$TEF" ]; then
            printf '%s\n' "The specified temporary directory: \"$TEF\" - does not exist in the specified location: \"$TEP\" - do you want to create it? [ Yes / No ] (default=Enter=No): ">"$print_to_screen"
            read answer
            if [ "$answer" = "Yes" ] || [ "$answer" = "yes" ] || [ "$answer" = "Y" ] || [ "$answer" = "y" ]; then
                mkdir "$TEF" || error="true"
            fi
        fi
        cd "$TEF" && output_dir="$PWD" || error="true"
    } || error="true"
} 2>/dev/null
if [ "$error" = "true" ]; then
    printf '%s\n' "Error: Could not access temporary folder \"$TEF\" in the extract location: \"$TEP\"!">&2
    read temp
    exit 1
fi

cd "$initial_dir"


IFS='
'

if [ "$inside_archive_path_filter" = "''" ] || [ "$inside_archive_path_filter" = "\"\"" ]; then inside_archive_path_filter="'"'*'"'"; fi

i=0
PrintJustInTitle "Loading list of files to analyze..."

found_zip_file_match="false"
for k in $(seq 1 $search_paths_0); do
    
    #current_search_path
    eval CSP="\"\$search_paths_$k\""
    
    cd "$initial_dir"
    if [ ! -d "$CSP" ]; then
        CSP2="$CSP"
        CSP=""
        find_zip_command="find \$CSP2 -type f -path \"*.zip*\" -exec printf '%s\\n' {} + "
    else
        cd "$CSP" && {
            CSP="$PWD/"
            find_zip_command="find . -type f -path \"*.zip*\" -exec printf '%s\n' {} + "
        } || {
            CleanUp 1
        }
    fi
    
    eval "$find_zip_command" 2>/dev/null|sort --numeric-sort|{ while read -r zip_file
    do
        i=$(($i + 1))
        PrintJustInTitle "Analyzing file $i..."
        if [ "$quick_flag" = "true" ]; then #default flag
            unzip -q -c "$zip_file" 2>/dev/null|grep -i -F "$search_string">/dev/null && {
                printf '%s\n' "$CSP$zip_file"|grep --color -F "$CSP$zip_file"
                found_zip_file_match="true"
            }
        elif [ "$short_flag" = "true" ]; then
            unzip -q -c "$CSP$zip_file" 2>/dev/null|grep -i -F "$search_string">/dev/null && {
                cd "$TEP" && {
                    unzip "$CSP$zip_file" -d "$TEF">/dev/null 2>/dev/null
                    cd "$output_dir"
                    j=0
                    for inside_zip_file in $(eval find . -type f -path "$inside_archive_path_filter"); do
                        j=$(($j + 1))
                        if [ "$j" = "1" ]; then
                            printf "\n"
                            printf '%s\n' "$CSP$zip_file:"
                            printf "\n"
                        else
                            cat "$inside_zip_file"|grep -i -F "$search_string">/dev/null 2>/dev/null && {
                                printf '%s\n' "$inside_zip_file"|grep --color -F "$inside_zip_file"
                                found_zip_file_match="true"
                            }
                        fi
                    done
                    if [ ! "$j" = "0" ]; then
                        printf "\n"
                    fi
                    if [ -n "$TEP" ] && [ -n "$TEF" ]; then
                        rm -R -f "$output_dir/"*
                    fi
                    cd "$CSP"
                }
            }
        elif [ "$detailed_flag" = "true" ]; then
            cd "$TEP" && {
                unzip "$CSP$zip_file" -d "$TEF">/dev/null 2>/dev/null
                cd "$output_dir"
                for inside_zip_file in $(eval find . -type f -path "$inside_archive_path_filter"); do
                    cat "$inside_zip_file"|grep -i -F "$search_string">/dev/null 2>/dev/null && {
                        printf "\n"
                        printf '%s\n' "$CSP$zip_file/$inside_zip_file"|grep --color -F "$CSP$zip_file/$inside_zip_file"
                        printf "\n"
                        cat "$inside_zip_file"|grep -i -F "$search_string"
                        found_zip_file_match="true"
                    }
                done
                if [ -n "$TEP" ] && [ -n "$TEF" ]; then
                    rm -R -f "$output_dir/"*
                fi
                cd "$CSP"
            }
        fi
    done;
    if [ "$k" = "$search_paths_0" ] && [ "$found_zip_file_match" = "true" ]; then printf "\n"; else printf '%s\n\n' "No results found."; fi
    }
done

PrintJustInTitle ""
CleanUp
I. Marin
  • 11
  • 2