Dominic Böttger

← Zurück zum Blog

Veröffentlicht am 11. Februar 2026 von Dominic Böttger (vor 1 Monaten) · 15 Min. Lesezeit

Wer auf Arch Linux umgestiegen ist, aber weiterhin Zugriff auf SharePoint-Dokumentbibliotheken der Arbeit benötigt, hat vermutlich bemerkt, dass Microsoft keinen nativen OneDrive-Client für Linux anbietet. Unter Windows funktioniert ein Klick auf den “Sync”-Button in SharePoint einfach so. Unter Linux passiert nichts.

In dieser Anleitung zeige ich, wie man diesen “Sync”-Button unter Arch Linux zum Laufen bringt — komplett mit automatischer Drive-Erkennung, Unterordner-Synchronisation, Windows-artiger Ordnerbenennung und systemd-Hintergrunddiensten. Klick auf “Sync” im Browser, und die Dateien erscheinen in ~/SharePoint/.

Was wir bauen

Wenn man in SharePoint auf “Sync” bei einer Bibliothek oder einem Ordner klickt, löst SharePoint eine odopen://-Protokoll-URL aus. Wir richten ein:

  1. abraunegg/onedrive — den Open-Source OneDrive-Sync-Client für Linux
  2. Einen Protocol Handler (odopen-handler.sh), der odopen://-URLs vom Browser abfängt
  3. XDG Desktop-Integration, damit das System diese URLs an unseren Handler weiterleitet
  4. Systemd User Services für persistente Hintergrundsynchronisation

Das Endergebnis: Klick auf “Sync” in SharePoint, ein Terminal öffnet sich für die Ersteinrichtung, und danach synchronisieren die Dateien im Hintergrund — genau wie unter Windows.

Voraussetzungen

  • Arch Linux (oder eine Arch-basierte Distribution)
  • Ein AUR-Helper wie yay oder paru
  • python3 (für URL-Dekodierung)
  • notify-send (normalerweise bei der DE/WM dabei)
  • Ein Terminal-Emulator (alacritty, kitty, foot, etc.)

Schritt 1: abraunegg/onedrive installieren

Den OneDrive-Client aus dem AUR installieren:

yay -S onedrive-abraunegg

Das baut den Client aus dem Quellcode (er ist in D geschrieben, daher wird dmd als Build-Abhängigkeit benötigt).

Installation überprüfen:

onedrive --version
# onedrive v2.5.10

Schritt 2: Bei Microsoft authentifizieren

Eine einmalige Authentifizierung ist nötig, damit der Client ein Refresh Token erhalten kann. Wer nicht das gesamte persönliche OneDrive lokal synchronisieren möchte, kann die vollständige Synchronisation überspringen und nur eine einmalige Authentifizierung durchführen.

OneDrive starten und nach der Authentifizierung sofort abbrechen:

onedrive --sync --verbose

Das gibt eine URL aus — diese im Browser öffnen und sich mit dem Microsoft-Konto anmelden. Sobald die Authentifizierung abgeschlossen ist, mit Ctrl+C die Synchronisation stoppen. Das Refresh Token ist nun unter ~/.config/onedrive/refresh_token gespeichert.

Wer keine persönlichen OneDrive-Dateien lokal haben möchte, entfernt das Standard-Sync-Verzeichnis:

rm -rf ~/OneDrive

Der Standard-OneDrive-Dienst ist standardmäßig deaktiviert — er synchronisiert nicht im Hintergrund, es sei denn, man aktiviert ihn explizit:

# Überprüfen, dass er nicht läuft (sollte er nicht)
systemctl --user status onedrive

Schritt 3: Protocol Handler Script installieren

Das folgende Skript unter ~/.local/bin/odopen-handler.sh speichern:

#!/bin/bash
# =============================================================================
# odopen-handler.sh
# Protocol handler for odopen:// URLs (Teams/SharePoint "Sync" button)
# Uses abraunegg/onedrive as sync backend
# =============================================================================

set -euo pipefail

LOGFILE="$HOME/.local/share/odopen-handler/handler.log"
BASE_SYNC_DIR="$HOME/SharePoint"
BASE_CONFIG_DIR="$HOME/.config/onedrive-sharepoint"

mkdir -p "$(dirname "$LOGFILE")"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"
}

# ---------------------------------------------------------------------------
# URL parsing helpers
# ---------------------------------------------------------------------------
urldecode() {
    python3 -c "import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))" "$1"
}

parse_param() {
    local value
    value=$(echo "$1" | grep -oP "${2}=\K[^&]+" | head -1 || true)
    if [[ -n "$value" ]]; then
        urldecode "$value"
    fi
}

# ---------------------------------------------------------------------------
# Notification helper (works with most DEs)
# ---------------------------------------------------------------------------
notify() {
    if command -v notify-send &>/dev/null; then
        notify-send -i folder-sync "OneDrive Sync" "$1"
    fi
    log "$1"
}

# ---------------------------------------------------------------------------
# --list: Show all active syncs
# ---------------------------------------------------------------------------
cmd_list() {
    local found=0
    for config_dir in "$BASE_CONFIG_DIR"/*/; do
        [[ -d "$config_dir" ]] || continue
        local name
        name=$(basename "$config_dir")
        local service_name="onedrive-sharepoint-${name}"
        local sync_dir=""
        local drive_id=""
        local status="stopped"

        if [[ -f "$config_dir/config" ]]; then
            sync_dir=$(grep -oP 'sync_dir\s*=\s*"\K[^"]+' "$config_dir/config" || true)
            drive_id=$(grep -oP 'drive_id\s*=\s*"\K[^"]+' "$config_dir/config" || true)
        fi

        if systemctl --user is-active "$service_name" &>/dev/null; then
            status="running"
        elif systemctl --user is-enabled "$service_name" &>/dev/null; then
            status="enabled (stopped)"
        fi

        if [[ $found -eq 0 ]]; then
            echo "Active OneDrive SharePoint syncs:"
            echo ""
        fi
        found=$((found + 1))
        echo "  [$found] $name"
        echo "      Sync dir:  $sync_dir"
        echo "      Drive ID:  ${drive_id:-(not set)}"
        echo "      Service:   $service_name ($status)"
        echo ""
    done

    if [[ $found -eq 0 ]]; then
        echo "No SharePoint syncs configured."
    fi
}

# ---------------------------------------------------------------------------
# --unsync: Remove a sync configuration
# ---------------------------------------------------------------------------
cmd_unsync() {
    local target="${1:-}"

    # Collect available syncs
    local names=()
    for config_dir in "$BASE_CONFIG_DIR"/*/; do
        [[ -d "$config_dir" ]] || continue
        names+=("$(basename "$config_dir")")
    done

    if [[ ${#names[@]} -eq 0 ]]; then
        echo "No SharePoint syncs configured."
        exit 0
    fi

    # If no target given, show interactive picker
    if [[ -z "$target" ]]; then
        echo "Which sync do you want to remove?"
        echo ""
        for i in "${!names[@]}"; do
            local n="${names[$i]}"
            local sd=""
            if [[ -f "$BASE_CONFIG_DIR/$n/config" ]]; then
                sd=$(grep -oP 'sync_dir\s*=\s*"\K[^"]+' "$BASE_CONFIG_DIR/$n/config" || true)
            fi
            local svc_status="stopped"
            if systemctl --user is-active "onedrive-sharepoint-${n}" &>/dev/null; then
                svc_status="running"
            fi
            echo "  [$((i + 1))] $n  ($svc_status)"
            [[ -n "$sd" ]] && echo "      $sd"
        done
        echo ""
        read -rp "Enter number (or 'q' to quit): " choice
        [[ "$choice" == "q" || -z "$choice" ]] && exit 0
        if ! [[ "$choice" =~ ^[0-9]+$ ]] || [[ "$choice" -lt 1 ]] || [[ "$choice" -gt ${#names[@]} ]]; then
            echo "Invalid selection."
            exit 1
        fi
        target="${names[$((choice - 1))]}"
    fi

    # Resolve target: allow passing display name, safe name, or sync dir name
    local safe_target=""
    for n in "${names[@]}"; do
        local sd=""
        if [[ -f "$BASE_CONFIG_DIR/$n/config" ]]; then
            sd=$(grep -oP 'sync_dir\s*=\s*"\K[^"]+' "$BASE_CONFIG_DIR/$n/config" || true)
        fi
        # Match against: safe name, sync dir basename, or sync dir full path
        if [[ "$n" == "$target" ]] || \
           [[ "$(basename "$sd")" == "$target" ]] || \
           [[ "$sd" == "$target" ]]; then
            safe_target="$n"
            break
        fi
    done
    if [[ -z "$safe_target" ]]; then
        echo "Error: No sync found for '$target'"
        echo "Use --list to see available syncs."
        exit 1
    fi

    local service_name="onedrive-sharepoint-${safe_target}"
    local sync_dir=""
    if [[ -f "$BASE_CONFIG_DIR/$safe_target/config" ]]; then
        sync_dir=$(grep -oP 'sync_dir\s*=\s*"\K[^"]+' "$BASE_CONFIG_DIR/$safe_target/config" || true)
    fi

    echo ""
    echo "Removing sync: $safe_target"
    echo "  Config:   $BASE_CONFIG_DIR/$safe_target"
    echo "  Sync dir: $sync_dir"
    echo "  Service:  $service_name"
    echo ""
    read -rp "Delete local files too? [y/N]: " delete_files

    # Stop and disable service
    systemctl --user stop "$service_name" 2>/dev/null || true
    systemctl --user disable "$service_name" 2>/dev/null || true
    rm -f "$HOME/.config/systemd/user/${service_name}.service"
    systemctl --user daemon-reload

    # Remove config
    rm -rf "$BASE_CONFIG_DIR/$safe_target"

    # Remove local files if requested
    if [[ "$delete_files" =~ ^[yY]$ ]] && [[ -n "$sync_dir" ]]; then
        rm -rf "$sync_dir"
        echo "Removed local files: $sync_dir"
    fi

    echo "Sync '$safe_target' removed."
    log "Unsynced: $safe_target"
}

# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------

case "${1:-}" in
    --list)
        cmd_list
        exit 0
        ;;
    --unsync)
        cmd_unsync "${2:-}"
        exit 0
        ;;
    --help|-h)
        echo "Usage:"
        echo "  odopen-handler.sh <odopen://...>   Handle SharePoint sync URL"
        echo "  odopen-handler.sh --list           List all active syncs"
        echo "  odopen-handler.sh --unsync [name]  Remove a sync (interactive if no name)"
        echo "  odopen-handler.sh --help           Show this help"
        exit 0
        ;;
esac

URI="${1:-}"

if [[ -z "$URI" ]]; then
    log "ERROR: No URI provided."
    notify "Error: No odopen:// URL provided."
    exit 1
fi

log "Received URI: $URI"

# --- Parse parameters from odopen:// URL ---
SITE_URL=$(parse_param "$URI" "webUrl")
FOLDER_URL=$(parse_param "$URI" "folderUrl")
FOLDER_NAME=$(parse_param "$URI" "folderName")
LIST_TITLE=$(parse_param "$URI" "listTitle")
WEB_TITLE=$(parse_param "$URI" "webTitle")
SITE_ID=$(parse_param "$URI" "siteId")
LIST_ID=$(parse_param "$URI" "listId")
WEB_ID=$(parse_param "$URI" "webId")

# Strip curly braces (SharePoint sometimes returns IDs with {})
SITE_ID="${SITE_ID//[\{\}]/}"
LIST_ID="${LIST_ID//[\{\}]/}"
WEB_ID="${WEB_ID//[\{\}]/}"

# --- Derive the relative folder path within the library ---
RELATIVE_FOLDER=""
if [[ -n "$FOLDER_URL" ]] && [[ -n "$SITE_URL" ]]; then
    LIB_AND_FOLDER="${FOLDER_URL#"$SITE_URL"}"
    LIB_AND_FOLDER="${LIB_AND_FOLDER#/}"
    RELATIVE_FOLDER="${LIB_AND_FOLDER#*/}"
    if [[ "$RELATIVE_FOLDER" == "$LIB_AND_FOLDER" ]]; then
        RELATIVE_FOLDER=""
    fi
fi

log "Parsed: site=$WEB_TITLE | library=$LIST_TITLE | folder=$FOLDER_NAME | relative_path=$RELATIVE_FOLDER"
log "Site URL: $SITE_URL"
log "Site ID: $SITE_ID | Web ID: $WEB_ID | List ID: $LIST_ID"

# --- Derive unique names - Windows-style "Sitename - Folder" ---
if [[ -n "$FOLDER_NAME" ]] && [[ -n "$RELATIVE_FOLDER" ]]; then
    DISPLAY_NAME="${WEB_TITLE} - ${FOLDER_NAME}"
    SAFE_NAME=$(echo "${WEB_TITLE}_${FOLDER_NAME}" | tr ' ' '_' | tr -cd '[:alnum:]_-')
    SINGLE_DIR="$RELATIVE_FOLDER"
else
    DISPLAY_NAME="${WEB_TITLE} - ${LIST_TITLE}"
    SAFE_NAME=$(echo "${WEB_TITLE}_${LIST_TITLE}" | tr ' ' '_' | tr -cd '[:alnum:]_-')
    SINGLE_DIR=""
fi

CONFIG_DIR="${BASE_CONFIG_DIR}/${SAFE_NAME}"
SYNC_DIR="${BASE_SYNC_DIR}/${DISPLAY_NAME}"
SERVICE_NAME="onedrive-sharepoint-${SAFE_NAME}"

log "Config dir: $CONFIG_DIR"
log "Sync dir: $SYNC_DIR"
log "Single dir: $SINGLE_DIR"

# --- Check if abraunegg/onedrive is installed ---
if ! command -v onedrive &>/dev/null; then
    notify "Error: abraunegg/onedrive is not installed!\nInstall with: yay -S onedrive-abraunegg"
    log "ERROR: onedrive not found in PATH"
    exit 1
fi

# --- Check if this library is already configured ---
if [[ -d "$CONFIG_DIR" ]] && [[ -f "$CONFIG_DIR/config" ]]; then
    notify "Sync for '$DISPLAY_NAME' is already configured.\nSync directory: $SYNC_DIR"

    if systemctl --user is-active "$SERVICE_NAME" &>/dev/null; then
        log "Service $SERVICE_NAME is already running."
        notify "Sync is already running in the background."
    else
        log "Starting existing service $SERVICE_NAME..."
        systemctl --user start "$SERVICE_NAME" || true
        notify "Sync has been restarted."
    fi
    exit 0
fi

# --- Create new configuration ---
mkdir -p "$CONFIG_DIR"
mkdir -p "$SYNC_DIR"

log "Creating new onedrive config in $CONFIG_DIR"

# Create config without drive_id (will be determined in the first-run script)
cat > "$CONFIG_DIR/config" <<EOF
# OneDrive SharePoint Sync - $DISPLAY_NAME
# Auto-generated by odopen-handler.sh on $(date)
sync_dir = "$SYNC_DIR"
EOF

# Check if a default OneDrive profile exists (for token reuse)
DEFAULT_CONFIG="$HOME/.config/onedrive"

if [[ -f "$DEFAULT_CONFIG/refresh_token" ]]; then
    log "Found existing OneDrive config, copying refresh_token..."
    cp "$DEFAULT_CONFIG/refresh_token" "$CONFIG_DIR/refresh_token" 2>/dev/null || true
    notify "Configuration created for: $DISPLAY_NAME\n\nDetermining Drive ID..."
else
    log "No existing OneDrive config found. First-run will require authentication."
    notify "New configuration created.\n\nAuthentication and Drive ID lookup in terminal..."
fi

# --- Build single-directory flags for onedrive CLI ---
SINGLE_DIR_FLAG=""
if [[ -n "$SINGLE_DIR" ]]; then
    SINGLE_DIR_FLAG="--single-directory '$SINGLE_DIR'"
fi

# --- Create systemd user service ---
SERVICE_FILE="$HOME/.config/systemd/user/${SERVICE_NAME}.service"
mkdir -p "$(dirname "$SERVICE_FILE")"

EXEC_CMD="/usr/bin/onedrive --monitor --confdir=\"$CONFIG_DIR\""
if [[ -n "$SINGLE_DIR" ]]; then
    EXEC_CMD="$EXEC_CMD --single-directory '$SINGLE_DIR'"
fi

cat > "$SERVICE_FILE" <<EOF
[Unit]
Description=OneDrive SharePoint Sync - $DISPLAY_NAME
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=$EXEC_CMD
Restart=on-failure
RestartSec=30

# Performance & Safety
Nice=10
IOSchedulingClass=idle

[Install]
WantedBy=default.target
EOF

systemctl --user daemon-reload
log "Created systemd service: $SERVICE_NAME"

# --- Open interactive terminal for first-run setup ---
# Write env file (avoids sed issues with special chars like & in site names)
FIRST_RUN_ENV=$(mktemp /tmp/odopen-firstrun-XXXXXX.env)
cat > "$FIRST_RUN_ENV" <<ENVEOF
CONFIG_DIR=$(printf '%q' "$CONFIG_DIR")
SERVICE_NAME=$(printf '%q' "$SERVICE_NAME")
WEB_TITLE=$(printf '%q' "$WEB_TITLE")
LIST_TITLE=$(printf '%q' "$LIST_TITLE")
DISPLAY_NAME=$(printf '%q' "$DISPLAY_NAME")
SYNC_DIR=$(printf '%q' "$SYNC_DIR")
SINGLE_DIR=$(printf '%q' "$SINGLE_DIR")
ENVEOF

FIRST_RUN_SCRIPT=$(mktemp /tmp/odopen-firstrun-XXXXXX.sh)
cat > "$FIRST_RUN_SCRIPT" <<'FIRSTRUN'
#!/bin/bash
# Source environment variables
ENV_FILE="${BASH_SOURCE[0]%.sh}.env"
if [[ -f "$ENV_FILE" ]]; then
    source "$ENV_FILE"
else
    echo "ERROR: Environment file not found: $ENV_FILE"
    read
    exit 1
fi

echo "======================================================="
echo " OneDrive SharePoint Sync - First-Run Setup"
echo "======================================================="
echo ""
echo " Team/Site:    $WEB_TITLE"
echo " Library:      $LIST_TITLE"
if [[ -n "$SINGLE_DIR" ]]; then
echo " Folder:       $SINGLE_DIR"
fi
echo " Sync dir:     $SYNC_DIR"
echo " Config:       $CONFIG_DIR"
echo ""

# --- Step 1: Determine the Drive ID for this SharePoint library ---
echo "-------------------------------------------------------"
echo " Step 1: Determining Drive ID for '$WEB_TITLE'..."
echo " (First use: browser window will open for login)"
echo "-------------------------------------------------------"
echo ""

DRIVE_OUTPUT=$(onedrive --get-sharepoint-drive-id "$WEB_TITLE" 2>&1)
echo "$DRIVE_OUTPUT"
echo ""

if echo "$DRIVE_OUTPUT" | grep -q "ERROR"; then
    echo "======================================================="
    echo " ERROR: SharePoint site '$WEB_TITLE' not found."
    echo " Check the site name and your permissions."
    echo "======================================================="
    echo ""
    echo "Press Enter to close..."
    read
    exit 1
fi

# Extract drive_id - try exact match first, then auto-select if only one result
DRIVE_ID=$(echo "$DRIVE_OUTPUT" | grep -B1 "Library Name: *$LIST_TITLE" | grep -oP 'drive_id:\s+\K\S+' || true)

if [[ -z "$DRIVE_ID" ]]; then
    DRIVE_ID_COUNT=$(echo "$DRIVE_OUTPUT" | grep -c 'drive_id:' || true)
    if [[ "$DRIVE_ID_COUNT" -eq 1 ]]; then
        DRIVE_ID=$(echo "$DRIVE_OUTPUT" | grep -oP 'drive_id:\s+\K\S+')
        MATCHED_LIB=$(echo "$DRIVE_OUTPUT" | grep -oP 'Library Name:\s+\K.*')
        echo " Auto-selected: '$MATCHED_LIB' (only library found)"
    fi
fi

if [[ -z "$DRIVE_ID" ]]; then
    echo "-------------------------------------------------------"
    echo " Could not auto-match library '$LIST_TITLE'."
    echo " Available Drive IDs are shown above."
    echo ""
    echo " Please enter the drive_id (e.g. b!xxxxxx):"
    echo "-------------------------------------------------------"
    read -r DRIVE_ID
    if [[ -z "$DRIVE_ID" ]]; then
        echo "No Drive ID entered. Aborting."
        echo "Press Enter to close..."
        read
        exit 1
    fi
fi

echo ""
echo " Found Drive ID: $DRIVE_ID"
echo ""

# --- Step 2: Update config with drive_id ---
echo "-------------------------------------------------------"
echo " Step 2: Updating configuration..."
echo "-------------------------------------------------------"

cat > "$CONFIG_DIR/config" <<EOF2
# OneDrive SharePoint Sync - $DISPLAY_NAME
# Auto-generated by odopen-handler.sh
sync_dir = "$SYNC_DIR"
drive_id = "$DRIVE_ID"
EOF2

echo " Config written: $CONFIG_DIR/config"
echo ""

# --- Step 3: Initial synchronization ---
echo "-------------------------------------------------------"
echo " Step 3: Starting initial sync..."
if [[ -n "$SINGLE_DIR" ]]; then
echo " Folder: $SINGLE_DIR"
fi
echo "-------------------------------------------------------"
echo ""

SYNC_CMD=(onedrive --confdir="$CONFIG_DIR" --sync --verbose --resync --resync-auth)
if [[ -n "$SINGLE_DIR" ]]; then
    SYNC_CMD+=(--single-directory "$SINGLE_DIR")
fi

"${SYNC_CMD[@]}"

if [[ $? -eq 0 ]]; then
    echo ""
    echo "======================================================="
    echo " Initial sync completed successfully!"
    echo " Enabling background sync..."
    echo "======================================================="

    systemctl --user enable "$SERVICE_NAME"
    systemctl --user start "$SERVICE_NAME"

    echo ""
    echo " Service status:"
    systemctl --user status "$SERVICE_NAME" --no-pager || true
    echo ""
    echo " Your files are at: $SYNC_DIR"
    echo ""
    echo " Useful commands:"
    echo "   Status:    systemctl --user status $SERVICE_NAME"
    echo "   Stop:      systemctl --user stop $SERVICE_NAME"
    echo "   Logs:      journalctl --user -u $SERVICE_NAME -f"
    echo ""
else
    echo ""
    echo "======================================================="
    echo " ERROR during synchronization."
    echo " Check the output above for details."
    echo ""
    echo " Manual setup:"
    echo "   onedrive --confdir='$CONFIG_DIR' --sync --verbose"
    echo "======================================================="
fi

echo "Press Enter to close..."
read
FIRSTRUN

# Rename env file to match the script name so the script can find it
mv "$FIRST_RUN_ENV" "${FIRST_RUN_SCRIPT%.sh}.env"
chmod +x "$FIRST_RUN_SCRIPT"

# Open terminal - try available terminal emulators
open_terminal() {
    local cmd="$1"
    if command -v kitty &>/dev/null; then
        kitty --title "OneDrive Sync Setup" bash "$cmd" &
    elif command -v alacritty &>/dev/null; then
        alacritty --title "OneDrive Sync Setup" -e bash "$cmd" &
    elif command -v foot &>/dev/null; then
        foot --title "OneDrive Sync Setup" bash "$cmd" &
    elif command -v gnome-terminal &>/dev/null; then
        gnome-terminal --title "OneDrive Sync Setup" -- bash "$cmd" &
    elif command -v konsole &>/dev/null; then
        konsole -e bash "$cmd" &
    elif command -v xterm &>/dev/null; then
        xterm -title "OneDrive Sync Setup" -e bash "$cmd" &
    else
        log "ERROR: No terminal emulator found"
        notify "No terminal found!\nPlease run manually:\nbash $cmd"
        return 1
    fi
}

open_terminal "$FIRST_RUN_SCRIPT"
log "Opened terminal for first-run setup."

Ausführbar machen:

chmod +x ~/.local/bin/odopen-handler.sh

Schritt 4: Den odopen:// Protocol Handler registrieren

Einen Desktop-Eintrag erstellen, damit das System weiß, wie odopen://-URLs verarbeitet werden:

cat > ~/.local/share/applications/odopen-handler.desktop <<EOF
[Desktop Entry]
Type=Application
Name=OneDrive Sync Handler
Comment=Protocol handler for odopen:// URLs (Teams/SharePoint Sync)
Exec=$HOME/.local/bin/odopen-handler.sh %u
MimeType=x-scheme-handler/odopen;
NoDisplay=true
Terminal=false
EOF

Beim System registrieren:

xdg-mime default odopen-handler.desktop x-scheme-handler/odopen
update-desktop-database ~/.local/share/applications/

Registrierung überprüfen:

xdg-mime query default x-scheme-handler/odopen
# Sollte ausgeben: odopen-handler.desktop

Schritt 5: Browser konfigurieren

Der Browser muss erlauben, dass odopen://-URLs externe Anwendungen öffnen:

  • Firefox: Beim ersten Klick auf einen SharePoint “Sync”-Button fragt Firefox nach einer Anwendung. Den Handler auswählen und “Diese Auswahl merken” aktivieren.
  • Chromium/Chrome/Edge: Möglicherweise muss das Protokoll in chrome://settings/handlers erlaubt oder die Aufforderung beim ersten Erscheinen akzeptiert werden.

Verwendung

Eine SharePoint-Bibliothek oder einen Ordner synchronisieren

  1. Zu einer SharePoint-Dokumentbibliothek oder einem Ordner im Browser navigieren
  2. Auf den “Sync”-Button klicken
  3. Der Browser startet den Protocol Handler
  4. Ein Terminal-Fenster öffnet sich mit der Ersteinrichtung:
    • Schritt 1: Abfrage der SharePoint-API nach der Drive ID
    • Schritt 2: Konfiguration schreiben
    • Schritt 3: Erste Synchronisation durchführen
  5. Nach der ersten Synchronisation startet ein systemd Service, der auf Änderungen überwacht

Die Dateien erscheinen in ~/SharePoint/ mit Windows-artiger Benennung:

Was synchronisiert wirdLokaler Ordner
Gesamte Bibliothek auf “Project Alpha”~/SharePoint/Project Alpha - Documents/
Unterordner “Reports” auf “Project Alpha”~/SharePoint/Project Alpha - Reports/

Aktive Synchronisationen auflisten

odopen-handler.sh --list

Ausgabe:

Active OneDrive SharePoint syncs:

  [1] ProjectAlpha_Reports
      Sync dir:  /home/user/SharePoint/Project Alpha - Reports
      Drive ID:  b!abc123...
      Service:   onedrive-sharepoint-ProjectAlpha_Reports (running)

Eine Synchronisation entfernen

Interaktiver Modus (zeigt eine Auswahl):

odopen-handler.sh --unsync

Oder den Namen direkt angeben:

odopen-handler.sh --unsync "Project Alpha - Reports"

Der unsync-Befehl wird:

  1. Den systemd Service stoppen und deaktivieren
  2. Das Konfigurationsverzeichnis entfernen
  3. Optional die lokalen Dateien löschen (es wird nachgefragt)

Nützliche systemd-Befehle

# Sync-Status prüfen
systemctl --user status onedrive-sharepoint-<name>

# Live-Sync-Logs ansehen
journalctl --user -u onedrive-sharepoint-<name> -f

# Sync temporär stoppen
systemctl --user stop onedrive-sharepoint-<name>

# Gestoppten Sync neustarten
systemctl --user start onedrive-sharepoint-<name>

Wie es unter der Haube funktioniert

Wenn man in SharePoint auf “Sync” klickt, öffnet der Browser eine URL wie:

odopen://sync?siteId={...}&webId={...}&webTitle=MyProject&listTitle=Documents
  &folderName=Reports&folderUrl=https://tenant.sharepoint.com/sites/MyProject/...

Das Handler-Skript:

  1. Parst die URL, um den Site-Namen, den Bibliothekstitel, den Ordnernamen und den relativen Pfad zu extrahieren
  2. Leitet eine eindeutige Konfiguration ab aus den Site- und Ordnernamen und erstellt ein separates onedrive-Konfigurationsverzeichnis pro Synchronisation
  3. Fragt die SharePoint-API ab über onedrive --get-sharepoint-drive-id, um die richtige drive_id für die Dokumentbibliothek zu finden
  4. Behandelt lokalisierte Bibliotheksnamen — SharePoint sendet “Documents” in der URL, aber die API gibt möglicherweise “Dokumente” zurück. Das Skript wählt automatisch aus, wenn es nur eine Bibliothek gibt
  5. Erstellt einen systemd User Service, der onedrive --monitor mit dem spezifischen --confdir und optionalem --single-directory-Flag ausführt
  6. Verwendet eine env-Datei statt sed für die Variablenübergabe an das Ersteinrichtungsskript, um Probleme mit Sonderzeichen wie & in Site-Namen zu vermeiden

Fallstricke und Tipps

Das gesamte persönliche OneDrive nicht synchronisieren, wenn es nicht nötig ist. Der Standard-onedrive-Dienst ist deaktiviert. Nur SharePoint-Synchronisationen, die explizit über den “Sync”-Button eingerichtet werden, laufen.

Das Setup-Terminal fertig werden lassen. Wenn sich das Ersteinrichtungs-Terminal öffnet, alle drei Schritte abschließen lassen, bevor man es schließt. Die Drive ID wird während dieses Prozesses in die Konfiguration geschrieben.

SharePoint verwendet lokalisierte Namen. Der “Sync”-Button sendet englische Namen wie “Documents”, aber die API gibt lokalisierte Namen wie “Dokumente” zurück. Das Skript handhabt dies automatisch, wenn es nur eine Dokumentbibliothek auf der Site gibt.

Site-Namen mit Sonderzeichen funktionieren. Namen wie “Jung Garten & Landschaft” werden durch escapte Umgebungsvariablen korrekt behandelt.

Das Sync-Intervall beträgt standardmäßig 5 Minuten. Der onedrive-Client im Monitor-Modus prüft alle 300 Sekunden auf Änderungen. Das kann mit monitor_interval in der Konfigurationsdatei angepasst werden.

Datei-Indexer können Probleme verursachen. Wer Baloo oder Tracker nutzt, sollte das ~/SharePoint/-Verzeichnis von der Indexierung ausschließen, um versehentliche Dateiänderungen zu vermeiden, die upstream synchronisiert werden.

Geschrieben von Dominic Böttger

← Zurück zum Blog

Aktuelle Blogbeiträge

  • Figma mit lokalen Schriftarten auf Omarchy Linux verwenden

    Figma mit lokalen Schriftarten auf Omarchy Linux verwenden

    Wie du Figma auf Omarchy Linux mit voller lokaler Schriftarten-Unterstützung einrichtest – mit figma-agent-linux und einem user-agent Workaround. Vollständige Anleitung mit Desktop-Integration und systemd Socket-Aktivierung.

    19. März 2026 · 4 Min. Lesezeit

  • SharePoint und OneDrive unter Arch Linux mit einem Klick synchronisieren

    SharePoint und OneDrive unter Arch Linux mit einem Klick synchronisieren

    Microsoft SharePoint und OneDrive unter Arch Linux synchronisieren mit abraunegg/onedrive. Inklusive Protokoll-Handler-Skript, das den „Synchronisieren"-Button in SharePoint nativ funktionieren lässt, mit automatischer Laufwerkserkennung und systemd-Hintergrundsynchronisation.

    11. Feb. 2026 · 15 Min. Lesezeit

  • Von sequenziell zu parallel: Agent Teams in Spec Kit integrieren

    Von sequenziell zu parallel: Agent Teams in Spec Kit integrieren

    Wie wir Spec Kit um den neuen Befehl /speckit.team-implement erweitert haben, der parallele Arbeitsströme aus deiner Aufgabenliste automatisch erkennt und spezialisierte KI-Agent-Teams startet, um Features gleichzeitig zu implementieren – mit vollständigem Quellcode, Algorithmus-Erklärung und Nutzungsanleitung.

    6. Feb. 2026 · 45 Min. Lesezeit

  • Einen privaten Claude Code Plugin-Marktplatz für dein Team aufbauen

    Einen privaten Claude Code Plugin-Marktplatz für dein Team aufbauen

    Erfahre, wie du einen privaten Claude Code Plugin-Marktplatz für dein Team strukturierst – inklusive Repository-Layout, Namenskonventionen und der Registrierung lokaler oder Remote-Marktplätze.

    20. Jan. 2026 · 10 Min. Lesezeit

  • Spec Kit + Ralph Loop: KI-Kontexterschöpfung bei großen Features lösen

    Spec Kit + Ralph Loop: KI-Kontexterschöpfung bei großen Features lösen

    Wie wir die strukturierte Planung von Spec Kit mit der Fresh-Context-Methodik von Ralph Wiggum kombiniert haben, um eine KI-gestützte Entwicklungsschleife zu bauen, die Features jeder Größe ohne Kontextverschmutzung umsetzen kann.

    18. Jan. 2026 · 28 Min. Lesezeit

  • Mistral veröffentlicht Vibe CLI und Devstral 2: Open-Source KI-Coding auf neuem Niveau

    Mistral veröffentlicht Vibe CLI und Devstral 2: Open-Source KI-Coding auf neuem Niveau

    Mistral AI bringt Vibe CLI und Devstral 2 heraus und liefert damit leistungsstarke Open-Source KI-Coding-Unterstützung für dein Terminal. Erfahre, wie du diese bahnbrechenden Tools installierst und nutzt.

    16. Jan. 2026 · 4 Min. Lesezeit