Command-line interface#

The command-line interface (CLI) provides Unix-style tools for anonymizing iCalendar files from the terminal.

Installation#

First, install uv as described in Prerequisites.

Install the latest Python version and create virtual environment.

uv venv

Activate the virtual environment.

source .venv/bin/activate
.venv\Scripts\Activate.ps1

Install icalendar-anonymizer, its dependencies, and the CLI extras.

uv pip install "icalendar-anonymizer[cli]"

Verify installation with the following command.

ican --version

This should output the package name and its version number.

Commands#

Two commands are provided.

icalendar-anonymize

Full command name

ican

Short alias for convenience

Both commands work identically.

Usage#

This section describes how to use the command-line icalendar-anonymize application. For brevity, examples use the alias form.

The usage syntax calls the program, followed optionally by options, then input, and finally output.

ican [OPTIONS] [INPUT] [-o OUTPUT]

For a complete list of command options and field configuration options, either see Options reference or Field configuration options, or use the following command.

ican --help

Anonymize a file#

Read from a file and write to another file:

ican calendar.ics -o anonymized.ics

Verbose output with the -v option shows progress.

ican -v calendar.ics -o anonymized.ics

Write to stdout#

Omit the -o flag to write to stdout:

ican calendar.ics

Read from stdin#

Omit the input argument to read from stdin:

cat calendar.ics | ican > anonymized.ics

Field configuration#

For a complete list of supported fields, use the --help option.

ican --help

Keep summaries, remove locations.

ican --summary keep --location remove calendar.ics

Replace descriptions with placeholders.

ican --description replace calendar.ics

Combine multiple field modes.

ican --summary keep --location remove --description replace calendar.ics

Pipeline processing#

Note

PowerShell examples use the -o flag instead of >. PowerShell 5.1 encodes > as UTF-16, which corrupts the output for tools that expect UTF-8.

PowerShell 5.1 also re-encodes stdin pipes through the console code page and can mangle non-ASCII content. For data with accents or non-Latin text on 5.1, prefer the file-argument form (ican calendar.ics -o anonymized.ics). PowerShell 7+ handles UTF-8 through pipes correctly.

Read from a file and write to a file.

cat calendar.ics | ican > anonymized.ics
ican calendar.ics -o anonymized.ics

Read from stdin explicitly with -.

ican - < calendar.ics > anonymized.ics

PowerShell re-encodes piped bytes from stdin through the console code page. On 5.1 this corrupts non-ASCII characters, even with -Encoding utf8. Pass the file path directly instead.

ican calendar.ics -o anonymized.ics

Verbose output to stderr doesn’t corrupt stdout.

cat calendar.ics | ican -v > anonymized.ics
ican -v calendar.ics -o anonymized.ics

Batch processing#

Anonymize all ICS files in directory.

for file in *.ics; do
    ican "$file" -o "anonymized-$file"
done
Get-ChildItem *.ics -File | ForEach-Object {
    ican $_.Name -o "anonymized-$($_.Name)"
}

Process files from a list.

while read -r file; do
    ican "$file" -o "anon-$(basename "$file")"
done < file-list.txt
Get-Content file-list.txt -Encoding utf8 | ForEach-Object {
    $file = $_.Trim()
    if ($file) {
        ican $file -o "anon-$(Split-Path $file -Leaf)"
    }
}

Remote files#

Download a remote file and anonymize it.

curl https://example.com/calendar.ics | ican > local-anon.ics
Invoke-WebRequest https://example.com/calendar.ics -UseBasicParsing -OutFile remote.ics
ican remote.ics -o local-anon.ics

Do the previous example with error checking.

curl -f https://example.com/calendar.ics | ican -v > local-anon.ics
try {
    Invoke-WebRequest https://example.com/calendar.ics -UseBasicParsing -OutFile remote.ics -ErrorAction Stop
    ican -v remote.ics -o local-anon.ics
} catch {
    Write-Error $_.Exception.Message
    exit 1
}

Combining with other tools#

Anonymize and count events.

ican calendar.ics | grep -c "BEGIN:VEVENT"
(ican calendar.ics | Select-String "BEGIN:VEVENT").Count

Anonymize and validate the input.

ican calendar.ics | <your-validator>

Compress the anonymized output.

ican calendar.ics | gzip > anonymized.ics.gz

Windows PowerShell doesn’t include a gzip command by default. Anonymize to a file first, then compress it with a third-party tool such as 7-Zip.

ican calendar.ics -o anonymized.ics
& "C:\Program Files\7-Zip\7z.exe" a -tgzip anonymized.ics.gz anonymized.ics

Keep summaries for debugging, pipe to a file, and compress it.

ican --summary keep calendar.ics | gzip > debug-anon.ics.gz
ican --summary keep calendar.ics -o debug-anon.ics
& "C:\Program Files\7-Zip\7z.exe" a -tgzip debug-anon.ics.gz debug-anon.ics

Anonymization summary#

The CLI uses the same anonymization as the Python API:

See also

See Property Handling Reference for the complete property reference table.

Anonymized properties#

These properties get anonymized and hashed with SHA-256.

  • Event summaries, descriptions, locations

  • Attendee and organizer names (CN parameter)

  • Comments, categories, resources

  • UIDs (uniqueness preserved)

Preserved properties#

These properties get preserved for bug reproduction.

  • All dates and times (DTSTART, DTEND, DUE)

  • Recurrence rules (RRULE, RDATE, EXDATE)

  • Status, priority, sequence numbers

  • Timezones (complete VTIMEZONE)

Error handling#

The CLI provides error messages for common issues, as described in each of the following subsections.

File not found#

$ ican nonexistent.ics
Error: Could not open 'nonexistent.ics': No such file or directory

Exit code: 2.

Invalid ICS file#

$ echo "invalid content" | ican
Error: Invalid ICS file - Expected instance of <class 'icalendar.cal.Component'>

Exit code: 1.

Empty input#

$ echo "" | ican
Error: Input is empty

Exit code: 1.

Permission denied#

$ ican protected.ics -o /root/output.ics
Error: [Errno 13] Permission denied: '/root/output.ics'

Exit code: 1.

Keyboard interrupt#

$ ican large-file.ics
^C
Interrupted

Exit code: 130.

Exit codes#

The CLI follows Unix conventions for exit codes:

Code

Meaning

When used

0

Success

Anonymization completed successfully

1

General error

Invalid ICS, empty input, I/O errors, unexpected errors

2

File error

Input file not found or cannot be opened

130

Interrupted

User pressed Ctrl+C (SIGINT)

Troubleshooting#

The following sections provide troubleshooting tips.

Command not found#

If you get command not found after installation:

  1. Reinstall the package with the CLI extras:

    uv pip install --reinstall "icalendar-anonymizer[cli]"
    
  2. Use the full Python module path:

    python -m icalendar_anonymizer.cli calendar.ics
    

Binary mode on Windows#

The CLI automatically handles binary mode on Windows. You don’t need to worry about CRLF line endings.

If you encounter encoding issues on Windows, then use binary mode with PowerShell.

Get-Content calendar.ics -Raw | ican > anonymized.ics

Large files#

The CLI loads the entire file into memory. For large files over 100MB in size, the following tips will improve performance.

  • Monitor memory usage. Use verbose mode to track progress.

    ican -v large-file.ics -o output.ics
    
  • Use the Python API for programmatic control over memory usage.

Debugging#

Enable verbose mode with the -v option to see processing steps.

ican -v calendar.ics -o anonymized.ics

Check the exit code after running ican, according to your operating system and shell.

ican calendar.ics
echo $?
ican calendar.ics
echo %ERRORLEVEL%
ican calendar.ics
echo $LASTEXITCODE

Getting help#

If you encounter issues with the CLI:

  • Use ican --help for usage information.

  • Check the Issue Tracker.

  • Open a new issue with: - Your command - Error message - Operating system - Python version (python --version) - Package version (ican --version)

Integration examples#

The following examples describe how to integrate icalendar-anonymizer with various third-party tools.

Git pre-commit hook#

Automatically anonymize calendars before committing:

#!/bin/bash
# .git/hooks/pre-commit

for file in *.ics; do
    if [ -f "$file" ]; then
        ican "$file" -o "anon-$file"
        git add "anon-$file"
    fi
done

cron job#

Periodically anonymize shared calendars:

# Crontab entry: Anonymize daily at 2 AM
0 2 * * * /usr/bin/ican /path/to/calendar.ics -o /path/to/anon.ics

Options reference#

[INPUT]#

Input iCalendar file to anonymize. Optional positional argument.

  • Default: stdin (-)

  • Format: File path or - for stdin

  • Example: ican calendar.ics

-o <file>, --output <file>#

Output file for anonymized calendar.

  • Default: stdout (-)

  • Format: File path or - for stdout

  • Example: ican input.ics -o output.ics

-v, --verbose#

Show processing information on stderr. Displays input/output sources and processing steps.

  • Flag: No value required

  • Output: Messages written to stderr (not stdout)

  • Example: ican -v calendar.ics -o anonymized.ics

The following example shows verbose output:

Reading from: calendar.ics
Parsing calendar...
Anonymizing calendar...
Writing to: anonymized.ics
Done.
--version#

Display version information and exit.

$ ican --version
icalendar-anonymizer, version <version>
--help#

Show usage information and exit.

ican --help

Note

The output for “Usage” is somewhat misleading, as Click merges -o, --output FILENAME with the options instead of as a positional final optional argument. See also #148 for a related Click formatting quirk.

Field configuration options#

Configure how individual fields are anonymized. The four modes are keep, remove, randomize, and replace.

--summary <mode>#

Mode for SUMMARY field.

  • Choices: keep, remove, randomize, replace

  • Default: randomize

  • Example: ican --summary keep calendar.ics

--description <mode>#

Mode for DESCRIPTION field.

--location <mode>#

Mode for LOCATION field.

--comment <mode>#

Mode for COMMENT field.

--contact <mode>#

Mode for CONTACT field.

--resources <mode>#

Mode for RESOURCES field.

--categories <mode>#

Mode for CATEGORIES field.

--attendee <mode>#

Mode for ATTENDEE field.

--organizer <mode>#

Mode for ORGANIZER field.

--uid <mode>#

Mode for UID field.

Note

The remove mode is not allowed.

  • Choices: keep, randomize, replace

  • Default: randomize

See also#