4

I can drag two files into the Meld GUI. I can also click File comparison (see Screenshot), then choose one file, compare that to nothing, then choose the second file. This seems roundabout. I want to compare two files, preferably in a multiselect filepicker GUI.

Even better would be to right-click one or two files in Nautilus to run Meld.

Is either of those possible?

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497
Joshua Fox
  • 2,941

3 Answers3

6

Personally, I always run meld from the terminal, so I give the two file names there:

meld fileA fileB

Using the GUI, all you need to do is select two files. I don't see why you need to "compare it to nothing". You just select the first file and then the second:

enter image description here

terdon
  • 100,812
  • You're right! I never noticed that after choosing one file for File Comparison, the GUI element under Directory Comparison is actually not for Directory Comparison anymore but rather for a second file! – Joshua Fox May 25 '15 at 18:45
  • Also, I found this about comparing two files straight out of Nautilus http://askubuntu.com/questions/112164/how-can-i-diff-two-files-with-nautilus – Joshua Fox May 25 '15 at 18:45
  • Any idea how one can integrate Meld with Nautilus in 22.10? – Juan Antonio Feb 10 '23 at 05:53
0

Here is a dash script for the automation of comparing [ folders/files / office/pdf documents / archives ] (the script uses diff / Meld by default - for the comparison; it also compares the contained URLs (...:\\...) in case of a .html/.htm/.pdf file):

#!/bin/dash
## Supported shells: dash, bash, zsh, ksh

PrintDesktopFile () { cat<<EOF [Desktop Entry] Name=File Comparer Comment=Compares [ folders/files / office/pdf documents / archives ] using 1. diff -r / 2. diff -r -q / 3. Meld TryExec=$terminal_emulator Exec=$terminal_emulator_plus_launch_flag "$current_shell_full_path" "$current_script_path" OPEN_WITH_MENU %U Terminal=$1 Type=Application Icon=utilities-terminal Categories=Utility; MimeType=inode/directory;text/plain;application/x-tar;application/x-gtar;application/x-compressed-tar;application/x-bzip-compressed-tar;application/x-tar-bz2;application/x-bzip2;application/zip;application/vnd.openxmlformats-officedocument.wordprocessingml.document;application/vnd.openxmlformats-officedocument.presentationml.presentation;application/vnd.openxmlformats-officedocument.presentationml.slideshow;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;application/vnd.oasis.opendocument.text;application/vnd.oasis.opendocument.spreadsheet;application/vnd.oasis.opendocument.presentation;application/pdf EOF }

PrintMenu () { { printf '\n%s\n' "Choose one option: " printf '%s\n' "Press: " printf '%s\n' " 1 - Compare with diff -r the previously selected files/folders" printf '%s\n' " 2 - Compare with diff -r -q the previously selected files/folders" printf '%s\n' " 3 - Compare with Meld the previously selected files/folders" printf '%s\n' " 0 - EXIT script" printf '%s\n' " - - FORCE EXIT (EXIT terminal)" }>"$print_to_screen" }

GetTerminalEmulatorPlusLaunchFlagLinux () {

terminal_emulator_raw=&quot;$(ps -o comm= -p &quot;$(($(ps -o ppid= -p &quot;$(($(ps -o sid= -p &quot;$$&quot;)))&quot;)))&quot;)&quot;
if [ ! &quot;${terminal_emulator_raw#*&quot;tmux&quot;*}&quot; = &quot;$terminal_emulator_raw&quot; ]; then
    terminal_emulator_raw=&quot;$(ps -o comm= -p &quot;$(($(ps -o ppid= -p &quot;$(($(ps -o sid= -p &quot;$(tmux display-message -p &quot;#{client_pid}&quot;)&quot;)))&quot;)))&quot;)&quot;
fi
terminal_emulator=&quot;${terminal_emulator_raw%&quot;-&quot;}&quot;

if [ &quot;$terminal_emulator&quot; = &quot;gnome-console&quot; ] || [ &quot;$terminal_emulator&quot; = &quot;gnome-terminal&quot; ]; then
    terminal_emulator_plus_launch_flag=&quot;\&quot;$terminal_emulator\&quot; --&quot;
elif [ &quot;$terminal_emulator&quot; = &quot;lxterminal&quot; ] || [ &quot;$terminal_emulator&quot; = &quot;qterminal&quot; ]; then
    #For LXDE and LXQt for example: the shell can be called directly (no terminal emulator needed):
    terminal_emulator_plus_launch_flag=&quot;&quot;
else
    terminal_emulator_plus_launch_flag=&quot;\&quot;$terminal_emulator\&quot; -e&quot;
fi

}

get_char () { { saved_tty_settings=$(stty -g); #save initial tty settings stty raw -echo #enable special characters (erase, kill, werase, rprnt) and disable echo-ing dd count=1 2>/dev/null #read (copy) and print: 1 input block stty "$saved_tty_settings" #restore initial tty settings }</dev/tty 2>/dev/null||{ printf '\n%s\n\n' "ERROR: Could not read character!">&2; } }

PrintJustInTitle () { printf "\033]0;$1\007">"$print_to_screen" }

CheckFilesFoldersToCompare () { if [ -z "$files_folders_to_compare" ]; then printf '%s\n' "Please add files/folders to the comparison queue first!" exit_code=1 else error="false" IFS=' ' for file in $(cat "$stored_file_paths_file_path"); do if [ ! -e "$file" ] || [ ! -r "$file" ]; then printf '%s\n' "ERROR: input file/folder: &quot;$file&quot; does not exist or is not readable!" error="true" fi done unset IFS if [ "$error" = "true" ]; then printf '\n%s\n\n' "Please: Try to readd files and compare them afterwards..." exit_code=1 fi fi }

GetOSType () { if [ -n "$1" ]; then case "$(uname -s)" in "Linux" ) eval $1="Linux" ;; "Darwin" | "BSD" ) eval $1="BSD-based" ;; * ) eval $1="Other" ;; esac else printf '%s\n' 'ERROR: GetOSType: Expected 1 parameter!'>&2 { printf '%s\n' 'Press Enter to exit...'; CleanUp; read temp; }>"$print_to_screen" fi }

GetCurrentShell () { if [ -n "$BASH_VERSION" ]; then current_shell_name="bash"; elif [ -n "$ZSH_VERSION" ]; then current_shell_name="zsh"; elif [ -n "$KSH_VERSION" ]; then current_shell_name="ksh"; elif [ "$PS1" = '$ ' ]; then current_shell_name="dash"; else current_shell_name="dash"; #default shell fi current_shell_full_path="$(which "$current_shell_name")"

eval $1=\&quot;\$current_shell_name\&quot;
eval $2=\&quot;\$current_shell_full_path\&quot;

}

PrintSavedBreakTimeoutFromFile() { cat "$temp_break_timeout_state_file" 2>/dev/null }

PrepareExitAndExit () { if [ "$input_answer" = "1" ] || [ "$input_answer" = "2" ]; then if [ "$OPEN_WITH_MENU" = "OPEN_WITH_MENU" ]; then { printf '%s\n' "Press Enter to exit..."; CleanUp; read temp; }>"$print_to_screen" fi else if [ ! "$1" = "RUN" ]; then PrintJustInTitle "" if [ -n "$TEMPORARY_EXTRACT_PATH" ] && [ -n "$TEMPORARY_EXTRACT_FOLDER" ]; then rm -R -f "$output_dir/"* fi fi>"$print_to_screen" fi

exit $exit_code

}

ExtractFirstAndLastPathComponent () { eval current_path="&quot;$$1&quot;"

first_path_component=&quot;&quot;
last_path_component=&quot;&quot;

if [ -n &quot;$current_path&quot; ]; then
    #Remove trailing '/' characters:
    while [ ! &quot;${current_path%&quot;/&quot;}&quot; = &quot;$current_path&quot; ]; do
        current_path=&quot;${current_path%&quot;/&quot;}&quot;
    done

    if [ -z &quot;$current_path&quot; ]; then
        eval current_path=\&quot;\$$1\&quot;
    fi

    last_path_component=&quot;${current_path##*&quot;/&quot;}&quot;
    first_path_component=&quot;${current_path%&quot;$last_path_component&quot;}&quot;
fi

eval $2=&quot;\&quot;\$first_path_component\&quot;&quot;
eval $3=&quot;\&quot;\$last_path_component\&quot;&quot;

}

ExtractFileIfArchive () { eval fileEFIA=&quot;$$1&quot;

if [ -z &quot;$first_time&quot; ]; then cat '/dev/null'&gt;&quot;$stored_file_paths_file_path&quot;; first_time=&quot;defined&quot;; fi

case &quot;$fileEFIA&quot; in
    *'.zip' | *'.odt' | *'.ods' | *'.odp' | *'.docx' | *.'xlsx' | *'.pptx' | *'.ppsx' | *'.pdf' | *'.htm' | *'.html' | *'.tar.'* | *'.bz2' | *'.xz' | *'.gz' | *'.tgz' | *'.tar' | '/dev/null' )
        error=&quot;false&quot;
        case &quot;$fileEFIA&quot; in
            *'.pdf' )
                CheckUtilities pdftotext pdftohtml
            ;;
        esac
        if [ &quot;$error&quot; = &quot;false&quot; ]; then
            ExtractArchive fileEFIA fileEFIA_extracted
            output1=&quot;$(printf '%s' &quot;$fileEFIA_extracted&quot;|sed &quot;s/'/$Q\&quot;\$Q\&quot;$Q/g&quot;)&quot;
            printf &quot;'%s' &quot; &quot;$output1&quot;
        fi
    ;;
    * )
        output1=&quot;$(printf '%s' &quot;$fileEFIA&quot;|sed &quot;s/'/$Q\&quot;\$Q\&quot;$Q/g&quot;)&quot;
        printf &quot;'%s' &quot; &quot;$output1&quot;
        fileEFIA_extracted=&quot;$fileEFIA&quot;
    ;;
esac

printf '%s\n' &quot;$fileEFIA_extracted&quot;&gt;&gt;&quot;$stored_file_paths_file_path&quot;

}

ExtractArchive () { eval current_archive_path="&quot;$$1&quot;"

if [ &quot;$current_archive_path&quot; = '/dev/null' ]; then
    {
    cd &quot;$output_dir&quot; || error=&quot;true&quot;
    mkdir &quot;null&quot;; cd &quot;null&quot; || error=&quot;true&quot;
    full_current_archive_extract_to_path=&quot;$output_dir/null&quot;
    cat /dev/null&gt;&quot;$full_current_archive_extract_to_path&quot;&quot;/&quot;&quot;temp.links.txt&quot; || error=&quot;true&quot;
    } 2&gt;/dev/null
    if [ &quot;$error&quot; = &quot;true&quot; ]; then printf '%s\n' &quot;ERROR: Could not create/access &lt;output&gt; directory structure!&quot;&gt;&amp;2; { printf '%s\n' &quot;Press Enter to exit...&quot;; CleanUp; read temp; kill $$; }&gt;&quot;$print_to_screen&quot;; fi

else
    full_current_archive_path=&quot;$current_archive_path&quot;

    ExtractFirstAndLastPathComponent current_archive_path fpc_current_archive_path lpc_current_archive_path
    cd &quot;$fpc_current_archive_path&quot;
    fpc_current_archive_path=&quot;$PWD&quot;

    ExtractFirstAndLastPathComponent fpc_current_archive_path fpc_fpc_current_archive_path lpc_fpc_current_archive_path
    current_archive_name_ext=&quot;${lpc_current_archive_path}&quot;
    if [ ! -d &quot;$current_archive_path&quot; ]; then
        current_archive_name_ext_plus=&quot;$current_archive_name_ext&quot;&quot;_extract&quot;
    else
        current_archive_name_ext_plus=&quot;$current_archive_name_ext&quot;
    fi
    full_current_archive_extract_to_path=&quot;$output_dir/$lpc_fpc_current_archive_path/$current_archive_name_ext_plus&quot;


    if [ ! -e &quot;$full_current_archive_extract_to_path&quot; ]; then
        {
        error=&quot;false&quot;
        cd &quot;$output_dir&quot; || error=&quot;true&quot;
        mkdir &quot;$lpc_fpc_current_archive_path&quot;; cd &quot;$lpc_fpc_current_archive_path&quot; || error=&quot;true&quot;
        mkdir &quot;$current_archive_name_ext_plus&quot;; cd &quot;$current_archive_name_ext_plus&quot; || error=&quot;true&quot;
        } 2&gt;/dev/null
        if [ &quot;$error&quot; = &quot;true&quot; ]; then printf '%s\n' &quot;ERROR: Could not create/access &lt;output&gt; directory structure!&quot;&gt;&amp;2; { printf '%s\n' &quot;Press Enter to exit...&quot;; CleanUp; read temp; kill $$; }&gt;&quot;$print_to_screen&quot;; fi

        case &quot;$current_archive_path&quot; in
            *'.zip' | *'.odt' | *'.ods' | *'.odp' | *'.docx' | *.'xlsx' | *'.pptx' | *'.ppsx' )
                unzip &quot;$full_current_archive_path&quot; -d &quot;$full_current_archive_extract_to_path&quot;
            ;;
            *'.pdf' )
                pdftotext &quot;$full_current_archive_path&quot; &quot;$full_current_archive_extract_to_path&quot;&quot;/&quot;&quot;temp.text.txt&quot;
                pdftohtml &quot;$full_current_archive_path&quot; &quot;$full_current_archive_extract_to_path&quot;&quot;/&quot;&quot;temp.html&quot;
                cat &quot;$full_current_archive_extract_to_path&quot;&quot;/&quot;*|eval &quot;$extract_urls_command&quot;&gt;&gt;&quot;$full_current_archive_extract_to_path&quot;&quot;/&quot;&quot;temp.links.txt&quot;
                full_current_archive_extract_to_path=&quot;$full_current_archive_extract_to_path&quot;
            ;;
            *'.htm' | *'.html' )
                cp &quot;$full_current_archive_path&quot; &quot;$full_current_archive_extract_to_path&quot;&quot;/&quot;&quot;temp.html&quot;
                cat &quot;$full_current_archive_extract_to_path&quot;&quot;/&quot;*|eval &quot;$extract_urls_command&quot;&gt;&gt;&quot;$full_current_archive_extract_to_path&quot;&quot;/&quot;&quot;temp.links.txt&quot;
                full_current_archive_extract_to_path=&quot;$full_current_archive_extract_to_path&quot;
            ;;
            *'.bz2' | *'.xz' | *'.gz' )
                cp &quot;$full_current_archive_path&quot; &quot;$full_current_archive_extract_to_path&quot;
                case &quot;$current_archive_path&quot; in
                    *'.bz2' ) bzip2 &quot;$full_current_archive_extract_to_path/&quot;&quot;$current_archive_name_ext&quot; -d &quot;$full_current_archive_extract_to_path&quot;; ;;
                    *'.xz' ) xz &quot;$full_current_archive_extract_to_path/&quot;&quot;$current_archive_name_ext&quot; -d &quot;$full_current_archive_extract_to_path&quot;; ;;
                    *'.gz' ) gzip -d &quot;$full_current_archive_extract_to_path/&quot;&quot;$current_archive_name_ext&quot;; ;;
                esac
                case &quot;${current_archive_name_ext%&quot;.&quot;*}&quot; in
                    *'.tar' )
                        tar -xvf &quot;$full_current_archive_extract_to_path/${current_archive_name_ext%&quot;.&quot;*}&quot; -C &quot;$full_current_archive_extract_to_path&quot;
                        rm &quot;$full_current_archive_extract_to_path/${current_archive_name_ext%&quot;.&quot;*}&quot;
                    ;;
                esac
            ;;
            *'.tgz' | *'.tar' )
                tar -xvf &quot;$full_current_archive_path&quot; -C &quot;$full_current_archive_extract_to_path&quot;
            ;;
        esac &gt;/dev/null 2&gt;/dev/null
    else
        cd &quot;$full_current_archive_extract_to_path&quot;
    fi
fi
eval $2=\&quot;\$full_current_archive_extract_to_path\&quot;

}

CheckUtilities () { #Check if any of the necessary utilities is missing: error="false"

for utility; do
    which $utility&gt;/dev/null 2&gt;/dev/null || { printf &quot;\n%s\n&quot; &quot;ERROR: the '$utility' utility is not installed!&quot;&gt;&amp;2; error=&quot;true&quot;; }
done

if [ &quot;$error&quot; = &quot;true&quot; ]; then
    {
    printf &quot;\n%s\n&quot; &quot;Press Enter to exit...&quot;&gt;&amp;2
    CleanUp; read temp;
    kill -s PIPE -- -$$; #kill all children processes, suppressing &quot;Terminated&quot; message
    }&gt;&quot;$print_to_screen&quot;
fi

}

CleanUp () { IFS="$initial_IFS" cd "$initial_dir" }

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

Q="'"

initial_IFS="$IFS"

cd "${0%/}" 2>/dev/null current_script_path="$(pwd -P)/${0##/}"

GetCurrentShell current_shell_name current_shell_full_path

APPS_DESKTOP_FILES_FOLDER="$HOME/.local/share/applications" #(for Linux) this is the default location for .desktop files (should not be changed)

desktop_file_name_ext="FileComparerScript.desktop" #(for Linux) can be changed desktop_file_path="$APPS_DESKTOP_FILES_FOLDER""/""$desktop_file_name_ext" #(for Linux) stored_file_paths_file_parent_dir_path="$APPS_DESKTOP_FILES_FOLDER" #can be changed

if [ ! -e "$stored_file_paths_file_parent_dir_path" ]; then stored_file_paths_file_parent_dir_path="$HOME" #can be changed fi

stored_file_paths_file_path="$stored_file_paths_file_parent_dir_path""/"'FileComparerScriptSettings.txt' temp_break_timeout_state_file="$stored_file_paths_file_parent_dir_path""/"'temp_break_timeout_state_file.txt' temp_compared_status_file="$stored_file_paths_file_parent_dir_path""/"'temp_compared_status_file.txt' temp_to_be_resetted_status_file="$stored_file_paths_file_parent_dir_path""/""temp_to_be_resetted.txt"

NL2=$(printf '%s' "\n\n") #Store New Line for use with sed insert_NL_before_URLs_command='sed -E '"'"'s/([a-zA-Z]://)/'"\${NL2}"'\1/g'"'" strip_NON_URL_text_command='sed -E '"'"'s/((.([^a-zA-Z+])|([a-zA-Z])))(([a-zA-Z])://)([^ ^'"${TAB}"'^>^<])./\4\5\7/g'"'" delete_lines_not_containing_an_URL='sed -E '"'"'/.://.*/!d'"'" extract_urls_command="$insert_NL_before_URLs_command|$strip_NON_URL_text_command|$delete_lines_not_containing_an_URL"

print_to_screen='/dev/tty'

CR=$(printf '\r')

default_menu_option="1" #can be changed [ 1..3 ]

GetOSType OS_TYPE

exit_code=0

OPEN_WITH_MENU=""; if [ "$1" = "OPEN_WITH_MENU" ]; then OPEN_WITH_MENU="OPEN_WITH_MENU"; shift; fi if [ "$1" = "--install" ]; then

IFS='

'

if [ &quot;$OS_TYPE&quot; = &quot;Linux&quot; ]; then
    {
        CheckUtilities grep update-desktop-database cat ps

        cat '/dev/null'&gt;&quot;$desktop_file_path&quot;

        GetTerminalEmulatorPlusLaunchFlagLinux

        #terminal_visible=false/true &lt;=&gt; not show / show: the initial launcher app (terminal) window
        #for LXDE and LXQt Desktop Environments: use &quot;true&quot;; otherwise: use &quot;false&quot;
        if [ -z &quot;$terminal_emulator_plus_launch_flag&quot; ]; then
            terminal_visible=&quot;true&quot;
        else
            terminal_visible=&quot;false&quot;
        fi

        PrintDesktopFile $terminal_visible&gt;&quot;$desktop_file_path&quot;

        favorite_icons_list=$(dconf read /org/gnome/shell/favorite-apps 2&gt;/dev/null)
        printf '%s' &quot;$favorite_icons_list&quot;|grep -F &quot;$desktop_file_name_ext&quot;&gt;/dev/null||{
            dconf write /org/gnome/shell/favorite-apps &quot;${favorite_icons_list%&quot;]&quot;}&quot;&quot;, &quot;&quot;'$desktop_file_name_ext'&quot;&quot;]&quot;
        } 2&gt;/dev/null
        # Update database of desktop entries cache:
        update-desktop-database &quot;$APPS_DESKTOP_FILES_FOLDER&quot;
    } &amp;&amp; {
        printf '\n%s\n\n' &quot;Installed in the Desktop Favorite Apps Ribbon.&quot;
    } || {
        printf '\n%s\n\n' &quot;ERROR: Install encountered errors.&quot;
    }
else
    printf '\n%s\n\n' &quot;ERROR: Currently, install is implemented only for the Linux operating systems!&quot;
fi

fi if [ -z "$1" ]; then printf '%s\n' "true">"$temp_to_be_resetted_status_file" fi if [ ! "$1" = "--install" ]; then

CheckUtilities mkdir cat diff kill stty dd unzip tar cp

{
    [ &quot;$OS_TYPE&quot; = &quot;Linux&quot; ] &amp;&amp; [ -e '/dev/shm' ] &amp;&amp; {
        TEMPORARY_EXTRACT_PATH='/dev/shm'
    } 2&gt;/dev/null || {
        [ &quot;$OS_TYPE&quot; = &quot;BSD-based&quot; ] &amp;&amp; [ -e &quot;$HOME&quot; ] &amp;&amp; {
            TEMPORARY_EXTRACT_PATH=&quot;$HOME&quot;
        }
    } || {
        printf &quot;Please provide TEMPORARY_EXTRACT_PATH: &quot;&gt;&amp;2; read TEMPORARY_EXTRACT_PATH&gt;&quot;$print_to_screen&quot;;
    }
    TEMPORARY_EXTRACT_FOLDER='TEMP_EXTR_FOLDER'
}&gt;/dev/null

initial_dir=&quot;$PWD&quot;

output_dir=&quot;&quot;
error=&quot;false&quot;
{
    [ -n &quot;$TEMPORARY_EXTRACT_PATH&quot; ] &amp;&amp; cd &quot;$TEMPORARY_EXTRACT_PATH&quot; &amp;&amp; {
        if [ ! -e &quot;$TEMPORARY_EXTRACT_FOLDER&quot; ]; then
            mkdir &quot;$TEMPORARY_EXTRACT_FOLDER&quot; || error=&quot;true&quot;
        fi
        cd &quot;$TEMPORARY_EXTRACT_FOLDER&quot; &amp;&amp; output_dir=&quot;$PWD&quot; || {
            error=&quot;true&quot;
        }
    } || error=&quot;true&quot;
} 2&gt;/dev/null
if [ &quot;$error&quot; = &quot;true&quot; ]; then
    printf '%s\n' &quot;Error: Could not access temporary folder \&quot;$TEMPORARY_EXTRACT_FOLDER\&quot; in the extract location: \&quot;$TEMPORARY_EXTRACT_PATH\&quot;!&quot;&gt;&amp;2
    { printf '%s\n' &quot;Press Enter to exit...&quot;; CleanUp; read temp; }&gt;&quot;$print_to_screen&quot;; exit 1
fi

cd &quot;$initial_dir&quot;


if [ ! -e &quot;$stored_file_paths_file_path&quot; ]; then
    cat '/dev/null'&gt;&quot;$stored_file_paths_file_path&quot;
fi

PrintJustInTitle &quot;File Comparer&quot;

if [ -e &quot;$1&quot; ]; then
    #######
    if [ -z &quot;$(cat &quot;$temp_compared_status_file&quot; 2&gt;/dev/null)&quot; ]; then
        printf '%s\n' &quot;compared&quot;&gt;&quot;$temp_compared_status_file&quot; 2&gt;/dev/null
    else
        cat '/dev/null'&gt;&quot;$temp_compared_status_file&quot; 2&gt;/dev/null
    fi

    if [ &quot;$(cat &quot;$temp_to_be_resetted_status_file&quot;)&quot; = &quot;true&quot; ]; then
        cat '/dev/null'&gt;&quot;$stored_file_paths_file_path&quot;
        printf '%s\n' &quot;false&quot;&gt;&quot;$temp_to_be_resetted_status_file&quot;
    fi
    #######

    for file; do
        files_folders_to_compare=$( \
            IFS='

';
for current_file in $(cat "$stored_file_paths_file_path"); do
ExtractFileIfArchive current_file;
done;
ExtractFileIfArchive file;
); done else

    if [ ! &quot;$1&quot; = &quot;RUN&quot; ]; then
        files_folders_to_compare=$( \
            if [ -n &quot;$1&quot; ]; then \
                IFS=&quot;$initial_IFS&quot;; \
                for file; do \
                    ExtractFileIfArchive file; \
                done; \
            else \
                IFS='

';
for file in $(cat "$stored_file_paths_file_path"); do
ExtractFileIfArchive file;
done;
fi;
); fi if [ -z "$3" ]; then files_folders_to_compare=$(
IFS=' ';
for current_file in $(cat "$stored_file_paths_file_path"); do
ExtractFileIfArchive current_file;
done;
); fi

    input_answer=&quot;$2&quot;

    if [ -z &quot;$input_answer&quot; ] || [ ! &quot;$1&quot; = &quot;RUN&quot; ]; then
        PrintMenu
        printf '%s\n' &quot;    Press other keys (e.g. arrows) during the first 1 second&quot;
        printf '%s\n' &quot;    to cancel automatically loading the default option ($default_menu_option)...&quot;
        printf '%s'   &quot;    &quot;
    fi&gt;&quot;$print_to_screen&quot;

    break_timeout_state=&quot;STATE1&quot;
    break_timeout_state=&quot;$(PrintSavedBreakTimeoutFromFile)&quot;
    if [ ! &quot;$break_timeout_state&quot; = &quot;STATE3&quot; ]; then
        cat '/dev/null'&gt;&quot;$temp_break_timeout_state_file&quot;
        printf &quot;STATE1&quot;&gt;&quot;$temp_break_timeout_state_file&quot;
    else
        printf &quot;STATE2&quot;&gt;&quot;$temp_break_timeout_state_file&quot;
    fi

    {
        sleep 1;
        break_timeout_state=&quot;$(PrintSavedBreakTimeoutFromFile)&quot;
        if [ &quot;$break_timeout_state&quot; = &quot;STATE1&quot; ]; then
            printf &quot;STATE3&quot;&gt;&quot;$temp_break_timeout_state_file&quot;
            #stty -F /dev/tty sane
            eval &quot;$current_shell_full_path&quot; \&quot;\$current_script_path\&quot; $OPEN_WITH_MENU RUN &quot;$default_menu_option&quot;
            exit 0
        fi
    }&amp;
    pid=$!

    break_timeout_state=&quot;$(PrintSavedBreakTimeoutFromFile)&quot;

    while [ ! &quot;$break_timeout_state&quot; = &quot;STATE3&quot; ] &amp;&amp; [ ! &quot;$input_answer&quot; = &quot;1&quot; ] &amp;&amp; [ ! &quot;$input_answer&quot; = &quot;2&quot; ] &amp;&amp; [ ! &quot;$input_answer&quot; = &quot;3&quot; ] &amp;&amp; [ ! &quot;$input_answer&quot; = &quot;0&quot; ] &amp;&amp; [ ! &quot;$input_answer&quot; = &quot;-&quot; ]; do
        input_answer=&quot;&quot;
        input_answer=$(get_char)
        if [ &quot;$break_timeout_state&quot; = &quot;STATE1&quot; ]; then
            eval kill -s PIPE $pid&gt;/dev/null 2&gt;/dev/null #kill the background process denoted here by $pid, suppressing &quot;Terminated&quot; message
            break_timeout_state=&quot;STATE2&quot;
            printf &quot;STATE2&quot;&gt;&quot;$temp_break_timeout_state_file&quot;
        fi
    done

    ##

    IFS='

'

    case $input_answer in
        &quot;1&quot; )
            printf '\n%s\n' &quot;1. diff -r&quot;
            printf '%s\n' &quot;diff -r $files_folders_to_compare&quot;
            printf &quot;\n&quot;
            CheckFilesFoldersToCompare
            if [ &quot;$exit_code&quot; = &quot;0&quot; ]; then
                printf '%s\n' &quot;compared&quot;&gt;&quot;$temp_compared_status_file&quot;
                IFS='

' diff_output="$(eval diff -r $files_folders_to_compare)" error_code=$? if [ -z "$diff_output" ] && [ "$error_code" = "0" ]; then printf '%s\n' "NO DIFFERENCES ENCOUNTERED..." [ ! "$2" = "1" ] && printf '\n%s\n' "DONE.">"$print_to_screen" else printf '%s\n' "$diff_output" [ ! "$2" = "1" ] && printf '\n%s\n' "DONE.">"$print_to_screen" fi fi if [ "$default_menu_option" = "1" ]; then if [ ! "$2" = "$default_menu_option" ]; then PrepareExitAndExit; fi; else PrepareExitAndExit; fi ;; "2" ) printf '\n%s\n' "2. diff -r -q" printf '%s\n' "diff -r -q $files_folders_to_compare" #printf "\n" CheckFilesFoldersToCompare if [ "$exit_code" = "0" ]; then printf '%s\n' "compared">"$temp_compared_status_file" IFS=' ' diff_output="$(eval diff -r -q $files_folders_to_compare)" error_code=$? if [ -z "$diff_output" ] && [ "$error_code" = "0" ]; then printf '%s\n' "NO DIFFERENCES ENCOUNTERED..." [ ! "$2" = "2" ] && printf '\n%s\n' "DONE.">"$print_to_screen" else printf '%s\n' "$diff_output" [ ! "$2" = "2" ] && printf '\n%s\n' "DONE.">"$print_to_screen" fi fi if [ "$default_menu_option" = "2" ]; then if [ ! "$2" = "$default_menu_option" ]; then PrepareExitAndExit; fi; else PrepareExitAndExit; fi ;; "3" ) printf '\n%s\n' "3. Meld" printf '%s\n' "meld $files_folders_to_compare" printf "\n" CheckFilesFoldersToCompare if [ "$exit_code" = "0" ]; then printf '%s\n' "compared">"$temp_compared_status_file" IFS=' ' eval meld $files_folders_to_compare 2>/dev/null||{ printf '%s\n' "ERROR: Meld is not installed?">&2 { printf '%s\n' "Press Enter to exit..."; CleanUp; read temp; }>"$print_to_screen" exit_code=1 } fi if [ "$default_menu_option" = "3" ]; then if [ ! "$2" = "$default_menu_option" ]; then PrepareExitAndExit; fi; else PrepareExitAndExit; fi ;; "0" | "-" ) if [ "$input_answer" = "0" ]; then printf "\n">"$print_to_screen"; exit 0; elif [ "$input_answer" = "-" ]; then printf "\n">"$print_to_screen"; kill -s PIPE -- -$$; fi ;; esac fi fi 2>&1|sed "s/$/$CR/" IFS="$initial_IFS"

  1. If not already done: run the script with the --install flag to install the .desktop file (for quick access)

  2. Second step

    1. Right-Click the [ folders/files / office/pdf documents / archives ], choose Open With, and the choose File Comparer.
      -> in order to add these [ folders/files / office/pdf documents / archives ] to the comparison queue

    or:

    1. Run the script with the desired parameters ([ folders/files / office/pdf documents / archives ])

      -> in order to add these [ folders/files / office/pdf documents / archives ] to the comparison queue

  3. (Can be run repeatedly:) Run the script with no parameters in order to do the comparison (the file selection is memorized until running the steps 2.1. / 2.2. again)

0

If you want to compare two files with meld from Nautilus, you can write some scripts that can be activated through the right-click menu on a file or folder. The scripting language for Nautilus scripts may be bash, python, perl or ruby, just to list some examples. In this answer I'll show a solution based on bash.

The first thing to is to create the scripts folder with the terminal command:

mkdir -p ~/.local/share/nautilus/scripts

All scripts will go into this folder. Once a script is in the folder, it will not show up in the right-click context menu until you make it executable with the terminal command:

chmod +x <script_name>

Once you have at least one executable script, then it will appear under 'scripts' in the context menu, as shown below (the image is just a reference taken from a StackExchange post, currently I don't have acces to my own Ubuntu for a more "personal" screenshot).

Example of a nautilus script from right-context menu

Now, you can add to that folder the following two files, that I have named Meld (select first item) and Meld (select second item). I have not put the .sh just for an aestethic reason (the extension appears in the context menu, and I don't like it): the presence of the shebang line at the beginning of every file it's enough for the system to understand how to execute them.

Content of Meld (select first item) file:

#!/bin/bash
#-------------------------------------------------------------------------------
# Script:  Meld (select first item)
# Author:  Lorenz Keel (AskUbuntu)
# Purpose: Define left-side compare operand for meld
# Note:    Don't forget to run: chmod +x <script_name>
#------------------------------------------------------------------------------

Exit values

STATUS_OK=0 STATUS_ERROR=1

Check meld package installation

if [ -n "$(command -v meld)" ]; then

Create temp file

TMP_FILE="/tmp/nautilus-compare.txt"

Check if the Nautilus environment variable is empty

if [ -z "${NAUTILUS_SCRIPT_SELECTED_FILE_PATHS}" ]; then # If it's blank, exit with notification of error exit "${STATUS_ERROR}" else # Remove the previous version of the temporary file if [ -f "${TMP_FILE}" ]; then rm -rf "${TMP_FILE}" fi # Put quotes around every full path while read -r FULL_PATH; do echo "'${FULL_PATH}'" | tee "${TMP_FILE}" done < <(echo "${NAUTILUS_SCRIPT_SELECTED_FILE_PATHS::-1}") fi else

Get the name of the current icon theme

ICON_THEME="$(gsettings get org.gnome.desktop.interface icon-theme | tr -d "'")"

Set the value of the notification icon

if [ -f "/usr/share/icons/${ICON_THEME}/scalable/status/dialog-error-symbolic.svg" ]; then NOTIFY_ICON="/usr/share/icons/${ICON_THEME}/scalable/status/dialog-error-symbolic.svg" else NOTIFY_ICON="error" fi

Specifies the timeout in seconds after which the dialog is closed

NOTIFY_TIMEOUT=5000 TEXT_ERROR="Installare meld per usare questo script" notify-send -t "${NOTIFY_TIMEOUT}" -i "${NOTIFY_ICON}" "${TEXT_ERROR}" fi

Exit with notification of success

exit "${STATUS_OK}"

The above files does not trigger yet meld, but simply creates a temporary files that contains the list of the files that are selected in Nautilus when you press the Meld (select first item) item in the context menu. The selected files are read from the special variable NAUTILUS_SCRIPT_SELECTED_FILE_PATHS that serves for this specific purpose. The remaining part of the script triggers a notification with the proper icon in the Ubuntu top bar in case of errros.

Content of Meld (select first item) file:

#!/bin/bash
#-------------------------------------------------------------------------------
# Script:  Meld (select second item)
# Author:  Lorenz Keel (AskUbuntu)
# Purpose: Define right-side compare operand for meld
# Note:    Don't forget to run: chmod +x <script_name>
#------------------------------------------------------------------------------

Exit values

STATUS_OK=0 STATUS_ERROR=1

Check meld package installation

if [ -n "$(command -v meld)" ]; then

Temp file location

TMP_FILE="/tmp/nautilus-compare.txt"

Check if the Nautilus environment variable is empty

if [ -z "${NAUTILUS_SCRIPT_SELECTED_FILE_PATHS}" ]; then # If it's blank, exit with notification of error exit "${STATUS_ERROR}" else if [ -f "${TMP_FILE}" ]; then # Put quotes around every full path while read -r FULL_PATH; do echo "'${FULL_PATH}'" | tee -a "${TMP_FILE}" done < <(echo "${NAUTILUS_SCRIPT_SELECTED_FILE_PATHS::-1}") else # Temp file is absent, left item has not been selected exit "${STATUS_ERROR}" fi fi

Open meld

tail -2 "${TMP_FILE}" | xargs meld &

Wait before deleting the temp file

sleep 1s && rm -rf "${TMP_FILE}" else

Get the name of the current icon theme

ICON_THEME="$(gsettings get org.gnome.desktop.interface icon-theme | tr -d "'")"

Set the value of the notification icon

if [ -f "/usr/share/icons/${ICON_THEME}/scalable/status/dialog-error-symbolic.svg" ]; then NOTIFY_ICON="/usr/share/icons/${ICON_THEME}/scalable/status/dialog-error-symbolic.svg" else NOTIFY_ICON="error" fi

Specifies the timeout in seconds after which the dialog is closed

NOTIFY_TIMEOUT=5000 TEXT_ERROR="Installare meld per usare questo script" notify-send -t "${NOTIFY_TIMEOUT}" -i "${NOTIFY_ICON}" "${TEXT_ERROR}" fi

Exit with notification of success

exit "${STATUS_OK}"

This second script fills the temporary files with the second item of the comparison and then run meld.

You can create these files with you preferred text editor, such as nano (from terminal) or gedit and gnome-text-editor (if you prefer a GUI). The important thing is to save them in the folder I mentioned at the beginning of this answer and enable execution rights on them.

These scripts have been written by myself for my personal usage, my minimal debugging showed me that they work for the simple tasks I ask them to do, I'm aware that probably they can be improved a lot.

Lorenz Keel
  • 8,905