How to backup contacts to Google from Samsung devices which don't export vcf file? The Contact apps that ships with Samsung devices does not seem to have a function to reliably backup all contacts on the phone and SIM storage to Google accounts.

Is there any third-party app that'd let me reliably do so? Bonus points if it'd let me sort which contact goes to which Google accounts registered on the device.

As a last resort, if there's no such app, I don't mind if I can get to backup to storage with which I can transfer the contacts to a different device.

PS I intentionally point to a specific model because I need to do so from two different Samsung devices and both their contact apps did not let me move all contacts to Google account. One of them does have .vcf export, but the other doesn't. I want a more universal solution if at all possible.

PPS The device that can do .vcf export has Google accounts correctly setup on both the new and old devices but all contacts has not been synced, hence the question.
Most of the contacts backup apps lack one feature or the other e.g. they don't backup high resolution photo when creating .vcf file. So I prefer CLI method to extract contacts data directly from database file using some scripting, but it requires root. That's how I do scheduled back up of my contacts without Google account.

The following script doesn't save Google sync account information to .vcf file but you can extend the script to include/exclude any data if the concept is perceived.

Original credits to dump-contacts2db.sh.

#!/system/bin/bash -e

# dumps following fields of contacts from Android's sqlite database to vcf:
# Name, Phone Number, Email, Address, Photo, Title, Organization, Notes, Website
# required binaries: sqlite3, base64, xxd (all included with Android)

# contacts directory, database file and photos directory
CONT_PROVIDER='/data/user/0/com.android.providers.contacts'
DB="$CONT_PROVIDER/databases/contacts2.db"
PHOTO_DIR="$CONT_PROVIDER/files/photos"

# vcf file
VCF="/data/media/0/contacts_$(date '+%d-%b-%y_%H-%M-%S').vcf"

# delete backup file if error occurs
trap '[ $? -eq 0 ] || rm -f $VCF' EXIT

GEN_VCARD()
{
    # skip blank contact for first time
    [ -n "$name" ] || return 0

    # count number of contacts
    n=$((n+1))

    vcard="${name}${tel}${adr}${email}${url}${note}${org}${title}${photo}"
    vcard="BEGIN:VCARD"$'\n'"VERSION:3.0"$'\n'"${vcard}""END:VCARD"
    echo "$vcard" &gt;&gt;$VCF
    echo &gt;&gt;$VCF
}

# fetch contacts data from different columns of 'view_entities' table of contacts sqlite database
ROWS="$(
sqlite3 $DB "
SELECT
    view_entities._id, view_entities.mimetype_id, view_entities.data1, view_entities.data2, view_entities.data3,
    view_entities.data4, view_entities.data5, view_entities.data6, quote(view_entities.data15), view_entities.photo_uri
FROM view_entities
WHERE view_entities.deleted = 0
ORDER BY view_entities._id, view_entities.mimetype_id
")"

# to parse rows
IFS=$'\n'

# iterate through contacts data rows
for ROW in $ROWS
do
    # to parse columns from a row
    IFS="|"
    i=0

    # iterate through columns
    for COL in $ROW
    do
        i=$((i+1))

        # data included in each column
        case $i in
            1)  # Contact ID
                id=$COL;;

            2)  # Mime Type ID
                mime_id=$COL;;

            3)  # Phone Number, Website, Email, Notes, Address, Organization
                data=$COL;;

            4)  # Type ID of Phone No. / Email / Address, First Name
                first_name=$COL; type_id=$COL;;

            5)  # Name of Custom Phone No. Type / Email Type / Address Type, Last Name
                last_name=$COL; type=$COL;;

            6)  # Name Prefix, Title
                name_prefix=$COL; tytle=$COL;;

            7)  # Middle Name
                middle_name=$COL;;

            8)  # Name Suffix
                name_suffix=$COL;;

            9)  # Photo thumbnail hex data
                photo_hex=$COL;;

            10) # Full resolution Photo URI
                photo_uri=$COL;;
        esac
    done

    # start new contact when all rows of same contact ID are parsed
    if [ "$prev_id" != "$id" ]
    then
        # echo current vcard prior to resetting variables
        GEN_VCARD

        # init new vcard
        for i in name tel adr email url note photo org title; do eval "$i=''"; done
    fi

    # add current row to current vcard, 'mimetype' determines data type on every row
    case $mime_id in
        1)
            # Email
            case $type_id in
                0)  email_type=X-$type;;
                1)  email_type=HOME;;
                2)  email_type=WORK;;
                3)  email_type="";;
                4)  email_type=CELL;;
                *)  echo "Unknown email type of '$data'" &gt;&amp;2; exit 1;;
            esac

            email=$email'EMAIL;TYPE='$email_type':'$data$'\n';;

        4)
            # Organization, Title
            org='ORG:'$data$'\n'
            title='TITLE:'$tytle$'\n';;
        5)
            # Phone No.
            case $type_id in
                0)  tel_type=X-$type;;
                1)  tel_type=HOME;;
                2)  tel_type=CELL;;
                3)  tel_type=WORK;;
                7)  tel_type=VOICE;;
                12) tel_type=PREF;;
                *)  echo "Unknown phone no. type of '$data'" &gt;&amp;2; exit 1;;
            esac

            tel=$tel'TEL;TYPE='$tel_type':'$data$'\n';;

        7)
            # Name
            name="$name_prefix $first_name $middle_name $last_name $name_suffix"

            # remove leading/trailing spaces
            IFS=' ' read name &lt;&lt;&lt;"$name"

            # always add complete name as First Name
            name="N:;"$name";;;"$'\n'"FN:"$name$'\n';;
        8)
            # Postal Address
            case $type_id in
                0)  adr_type=X-$type;;
                1)  adr_type=HOME;;
                2)  adr_type=WORK;;
                *)  echo "Unknown address type of '$data'" &gt;&amp;2; exit 1;;
            esac

            adr=$adr'ADR;TYPE='$adr_type':;;'$data';;;;'$'\n';;

        10)
            # Photo
            if [ $photo_hex != "NULL" ]
            then
                # look for high-resolution photo instead of thumbnail
                uri=$(echo $photo_uri | sed 's|content://com.android.contacts/display_photo|'"$PHOTO_DIR"'|')
                if [ -f $uri ]
                then
                    # convert binary to base64
                    foto="$(base64 -w 0 $uri)"
                else
                    # remove prefix/suffix from hex output, convert hex to binary to base64
                    foto=$(echo $photo_hex | sed "s/^X'//; s/'$//" | tr '[:upper:]' '[:lower:]' | xxd -pr | base64 -w 0)
                fi

                photo="PHOTO;ENCODING=BASE64;JPEG:"$foto$'\n'
            fi;;

        12)
            # Note
            note="NOTE:"$data$'\n';;

        14)
            # Website
            url=$url"URL:"$data$'\n';;

        *)  echo "Unknown mime type: '$(sqlite3 $DB "SELECT view_entities.mimetype FROM view_entities WHERE view_entities.mimetype_id = $mime_id" | head -n1)'" &gt;&amp;2; exit 1;;

    esac

    # preserve current ID to compare with next row
    prev_id=$id

    # reset IFS for parent loop to parse rows
    IFS=$'\n'
done

# echo last vcard, loop is done
GEN_VCARD

echo "Backed up $n contacts."


If you prefer GUI or don't have root, you can use some app like Contacts VCF.