Skip to main content

gdrives v0.7.1

Command-line tools for Google Drive.

Browse Google Drives, list folder contents by path or ID, export Google Docs, Sheets, and Slides to Office formats, download individual files or whole folder trees, read and write Google Sheet cell ranges, read and edit Google Docs content in place, and generate hyperlinked folder maps — all from the terminal. Human-readable Drive paths (e.g. My Drive/projects) resolve against a local drive-name cache, with first-class support for shared drives and "Shared with me" items. Listings carry URL, type, modified-date, owner, and sharer columns, and can be written as nested markdown or flat CSV from a single API traversal. Folder downloads scan and summarize before prompting, auto-exporting Google-native files and streaming binaries to disk with atomic writes. Authenticates via OAuth, a service account, or Application Default Credentials, requesting read-only Drive access (drive.readonly) by default; the Sheets and Docs write commands opt into spreadsheets / documents scopes stored in separate tokens, so read-only users are never re-prompted. Built on the Google Drive API v3, Sheets API v4, and Docs API v1 with a Typer CLI.

Project Structure

gdrives/
├── cli.py       # Typer CLI: ls, export, download, show-drives, sheets-*, docs-*
├── auth.py      # OAuth, service-account, and ADC authentication
├── drives.py    # Drive name→ID cache (fetch, save, resolve)
├── resolve.py   # Path→ID resolution (drive paths and "shared with me")
├── files.py     # Drive API wrappers: pagination, folder walk, file helpers
├── listing.py   # DriveEntry, recursive collection, and table/markdown/CSV formatters
├── export.py    # Export Google Docs, Sheets, and Slides to Office formats
├── download.py  # Download a single file, or recurse a folder, to local disk
├── sheets.py    # Read and write Google Sheet cell ranges (Sheets API v4)
└── docs.py      # Read and edit Google Docs content in place (Docs API v1)

Installation

uv tool install gdrives

As a project dependency:

uv add gdrives

From GitHub instead of PyPI:

uv tool install git+https://github.com/gitronald/gdrives.git
# or, as a dependency: uv add git+https://github.com/gitronald/gdrives.git

From source (for development):

git clone https://github.com/gitronald/gdrives.git
cd gdrives
uv sync

The distribution and the installed command are both named gdrives.

Setup

You need Google Drive API credentials before using gdrives. Pick the method that fits, follow its steps, then run gdrives show-drives to verify. Every method needs a Google Cloud project with the Google Drive API enabled; read commands request read-only access (drive.readonly). The sheets-* commands additionally need the Google Sheets API enabled, and the docs-* commands the Google Docs API. The write commands request a write scope cached in its own token so read-only access is never disturbed: spreadsheets (gdrives_token_rw.json) for sheets-update, sheets-append, sheets-clear, and sheets-set; documents (gdrives_token_documents.json) for docs-update, docs-append, docs-replace, docs-clear, and docs-create. When more than one is configured, authentication is attempted in order: OAuth, then service account, then ADC.

OAuth — personal use, interactive browser auth

  1. At console.cloud.google.com, create or select a project, then enable the Google Drive API under APIs & Services → Library.
  2. Configure the OAuth consent screen (APIs & Services → OAuth consent screen): choose External, fill in app name and support email, leave it in Testing, and add your Google address under Test users.
  3. Credentials → Create Credentials → OAuth client ID, application type Desktop app, then download the JSON.
  4. Save it as gdrives_credentials.json in your config directory and export that path:
    export GOOGLE_CONFIG_DIR=~/.google   # directory holding gdrives_credentials.json
    
  5. Run gdrives show-drives. A browser opens for one-time authorization; the token is cached to $GOOGLE_CONFIG_DIR/gdrives_token.json and reused (it re-auths automatically if revoked).

Full walkthrough: docs/setup-oauth.md.

Service account — automation, or sharing access with others

  1. Create or select a project and enable the Google Drive API (as above).
  2. IAM & Admin → Service Accounts → Create Service Account; give it a name like drive-reader and skip the optional grant steps.
  3. Open the new account's Keys tab → Add Key → Create new key → JSON, then download it.
  4. Save it as service_account.json in your config directory (or point GOOGLE_SERVICE_ACCOUNT_PATH at it):
    export GOOGLE_CONFIG_DIR=~/.google   # directory holding service_account.json
    # or: export GOOGLE_SERVICE_ACCOUNT_PATH=/path/to/key.json
    
  5. Share the target folder or shared drive with the service account's email (e.g. drive-reader@your-project.iam.gserviceaccount.com) as Viewer — it can only see what's explicitly shared with it.
  6. Run gdrives show-drives. No browser flow.

Full walkthrough (key rotation, revoking access): docs/setup-service-account.md.

gcloud / ADC — simplest if you already use the gcloud CLI

  1. Create or select a project and enable the Google Drive API (as above).
  2. Install the gcloud CLI if you don't have it.
  3. Log in with the Drive scope — the --scopes flag is required (a plain login grants only cloud-platform, which excludes Drive and would 403):
    gcloud auth application-default login \
      --scopes=https://www.googleapis.com/auth/drive.readonly,https://www.googleapis.com/auth/cloud-platform
    
  4. Set a quota project (use the project where you enabled the Drive API):
    gcloud auth application-default set-quota-project YOUR_PROJECT_ID
    
  5. Run gdrives show-drives. No GOOGLE_CONFIG_DIR and no credential files to manage.

Full walkthrough: docs/setup-adc.md.

Configuration

Set via environment variables or a .env file (env vars take precedence):

Variable Default Purpose
GOOGLE_CONFIG_DIR (required for OAuth; not needed for ADC) Directory for the OAuth token and credentials
GOOGLE_SERVICE_ACCOUNT_PATH $GOOGLE_CONFIG_DIR/service_account.json Service account key file

Authentication tries OAuth first, then the service account, then Application Default Credentials.

CLI Commands

Run gdrives show-drives once to populate the drive-name cache (.gdrives/cache.json); the ls and download commands resolve Drive paths against it.

List Drive contents

gdrives ls                                              # My Drive (default)
gdrives ls "My Drive/projects"                          # Subfolder
gdrives ls "Shared drive name/subfolder"                # Shared drive
gdrives ls "My Drive" --depth 2                         # Recurse deeper
gdrives ls --drive-id <folder-id>                       # By folder ID
gdrives ls --shared-with-me                             # Shared with me
gdrives ls "Shared folder" --shared-with-me             # Shared subfolder

Save a listing as markdown or CSV

gdrives ls "My Drive/projects" --save-as map.md         # Nested markdown
gdrives ls "My Drive/projects" --save-as data.csv       # CSV export
gdrives ls "My Drive/projects" --depth 3 --save-as map.md
gdrives ls "My Drive/projects" --save-as map.md --save-as data.csv  # both, one traversal

Export Google Docs, Sheets, and Slides

gdrives export <doc-url> -o output.docx     # Google Doc -> .docx
gdrives export <doc-url> -o output.md       # Google Doc -> Markdown (.txt for plain text)
gdrives export <sheet-url> -o output.xlsx   # Google Sheet -> .xlsx
gdrives export <sheet-url> -o output.csv    # Google Sheet -> .csv (first tab only)
gdrives export <slides-url> -o output.pptx  # Google Slides -> .pptx

Read and write Google Sheet cell values

Operate on live cell ranges via the Sheets API — distinct from export, which downloads a whole spreadsheet to a local file. The target is a Sheet URL, a bare file ID, or a Drive path; the range is an A1 range like Sheet1!A1:C10 (a bare A1:C10 targets the first tab).

gdrives sheets-get <sheet-url> "Sheet1!A1:C10"          # Print a range (aligned columns)
gdrives sheets-get <sheet-url>                          # First tab, whole used range
gdrives sheets-get <sheet-url> "A1:C10" --csv           # Comma-delimited to stdout
gdrives sheets-get <sheet-url> "A1:C10" -o out.csv      # Write CSV (or --tsv for TSV)
gdrives sheets-update <sheet-url> "A1:C2" --values-file data.csv   # Overwrite a range
gdrives sheets-update <sheet-url> "A1" --values-file data.csv --raw  # Store literal strings
gdrives sheets-append <sheet-url> "Sheet1!A1" --values-file rows.csv  # Append after the table
gdrives sheets-clear <sheet-url> "Sheet1!A1:C10"        # Clear values (prompts first; -y skips)

Reads use the read-only scope (no re-consent for existing users). The write commands (sheets-update, sheets-append, sheets-clear, sheets-set) request the spreadsheets scope the first time and cache it in a separate token. Cells are plain strings on both sides: --values-file reads a local CSV, and by default USER_ENTERED parses formulas, dates, and numbers like the Sheets UI (--raw stores the literal text). Use --raw when writing data from an untrusted source, so a leading =, +, -, or @ is stored verbatim rather than evaluated as a formula.

Update rows by lookup (sheets-set)

Find rows by column value(s) and set other columns on them — no A1 arithmetic. Columns are addressed by header name (row 1). Repeat --match for a composite (AND) key and --set for multiple columns:

# In the row where id=C300, set status=paid and amount=250
gdrives sheets-set <sheet-url> --match id=C300 --set status=paid --set amount=250

# Composite key: match on two columns before writing
gdrives sheets-set <sheet-url> -m year=2026 -m id=C300 -s status=paid

# Update every matching row (default refuses when >1 row matches)
gdrives sheets-set <sheet-url> -m status=pending -s reminder=sent --all

gdrives sheets-set <sheet-url> --tab Roster -m id=C300 -s status=paid  # non-default tab

By default it requires exactly one matching row — it refuses (listing the rows) when the key is ambiguous, and errors when nothing matches, so a keyed update never silently rewrites the wrong row. Pass --all to update every match. --raw and the USER_ENTERED default apply as above.

Read and edit Google Docs content

Operate on the live document via the Docs API — distinct from export, which downloads a whole Doc to a local file. The target is a Doc URL, a bare file ID, or a Drive path. Commands act on the first tab unless --tab names another (by title or ID).

gdrives docs-get <doc-url>                              # Print the text (lists as '- ', table rows tab-separated)
gdrives docs-get <doc-url> -o notes.txt                 # Write the text to a file
gdrives docs-get <doc-url> --json                       # Raw documents.get response (every tab)
gdrives docs-update <doc-url> --text-file body.txt      # Replace the whole body (prompts first; -y skips)
gdrives docs-append <doc-url> --text "New paragraph"    # Append a paragraph (or --text-file more.txt)
gdrives docs-replace <doc-url> --find "draft" --replace "final"   # Find and replace one occurrence
gdrives docs-replace <doc-url> --find "v1" --replace "v2" --all   # Replace every occurrence
gdrives docs-clear <doc-url>                            # Empty the body (prompts first; -y skips)
gdrives docs-create --title "Notes" --text-file body.txt  # New Doc in My Drive root; prints its URL

Reads use the read-only scope. The write commands (docs-update, docs-append, docs-replace, docs-clear, docs-create) request the documents scope the first time and cache it in a separate token. Content is plain text on both sides: docs-get flattens paragraphs, list items, and table rows, and the write commands insert plain paragraphs (formatting is out of scope — use export -o file.docx for a styled copy). docs-replace refuses when the phrase occurs more than once unless --all is given, and errors when it occurs nowhere, so a targeted edit never rewrites the wrong sentence (--ignore-case relaxes matching). docs-update, docs-clear, and docs-replace are tied to the document revision they read, so a write is refused if someone changed the document in between.

Download files and folders

gdrives download <file-url> -o ./out          # Single file -> ./out/<drive-name>
gdrives download "My Drive/refs/paper.pdf"    # Single file by path -> ./paper.pdf
gdrives download "My Drive/refs"              # Whole folder (recurses by default)
gdrives download "My Drive/refs" --depth 1    # Folder, flat (no recursion)
gdrives download "My Drive/refs" -y           # Skip the confirmation prompt

A source that resolves to a single file downloads immediately under its Drive name. A folder is scanned first, showing a summary, then prompts before downloading (--depth only affects folders). Google Docs, Sheets, and Slides auto-export to .docx / .xlsx / .pptx; other Google-native types (Forms, Drawings, etc.) are skipped.

Show available drives

gdrives show-drives

Output includes URL, type (personal/shared), name, and ID for each accessible drive, and is cached to .gdrives/cache.json for use by the other commands.

Related projects

There are a few options out there, but most haven't been touched in years, and none did the mapping tasks implemented here.

  • PyDrive2 — fork of PyDrive, high-level Google Drive wrapper
  • PyDrive — the original high-level wrapper, now archived
  • gdrive "Simple Google Drive wrapper to traverse files"
  • gdriver "Actually usable Google Drive client"
  • drive "Google Drive client"

Security & privacy

  • Read commands request read-only Drive access (drive.readonly) and never modify or delete anything in your Drive. Only the Sheets write commands (sheets-update, sheets-append, sheets-clear, sheets-set) and the Docs write commands (docs-update, docs-append, docs-replace, docs-clear, docs-create) request write access, via the spreadsheets and documents scopes respectively; a read command never loads or requests them.
  • The cached OAuth tokens ($GOOGLE_CONFIG_DIR/gdrives_token.json for read-only, gdrives_token_rw.json for the Sheets write scope, gdrives_token_documents.json for the Docs write scope) hold long-lived refresh tokens and are written with owner-only 0600 permissions. Each scope set has its own token file so requesting one kind of write access never clobbers or re-consents another, and a cached token whose grant does not cover a request is re-authorized rather than reused. Keep gdrives_credentials.json and service_account.json out of version control and shared locations.
  • gdrives show-drives writes .gdrives/cache.json with the names and IDs of every Drive you can access; it is gitignored by default — keep it out of shared locations.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gdrives-0.7.1.tar.gz (38.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gdrives-0.7.1-py3-none-any.whl (42.0 kB view details)

Uploaded Python 3

File details

Details for the file gdrives-0.7.1.tar.gz.

File metadata

  • Download URL: gdrives-0.7.1.tar.gz
  • Upload date:
  • Size: 38.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for gdrives-0.7.1.tar.gz
Algorithm Hash digest
SHA256 3b601aad878a30e8991437c576d0555262b8d70f8c66bfbfedc960ef8b1d1929
MD5 81325c01862b68216e07f9cfd9508b33
BLAKE2b-256 12f22b94ba3765d09ad5508036204c2941fbda6864939aa9a5e9ee0538009881

See more details on using hashes here.

Provenance

The following attestation bundles were made for gdrives-0.7.1.tar.gz:

Publisher: publish.yml on gitronald/gdrives

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gdrives-0.7.1-py3-none-any.whl.

File metadata

  • Download URL: gdrives-0.7.1-py3-none-any.whl
  • Upload date:
  • Size: 42.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for gdrives-0.7.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e8560ebcae93808fd5dd933eada2761e1ef2e2cfca69ee6ef9f5ab418f6b8578
MD5 2e13f8d0209bac9a4149a16971396cc9
BLAKE2b-256 050939a4b98f1d7d54ae01d22c541e6426f3a377b888737e5d0fe49af0638653

See more details on using hashes here.

Provenance

The following attestation bundles were made for gdrives-0.7.1-py3-none-any.whl:

Publisher: publish.yml on gitronald/gdrives

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.7.1 This release

2 files

0.7.0

2 files

0.6.2

2 files

0.6.1

2 files

0.6.0

2 files

0.5.8

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page