.. SPDX-FileCopyrightText: 2025 icalendar-anonymizer contributors .. SPDX-License-Identifier: AGPL-3.0-or-later ********************** 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 :ref:`development-prerequisites`. Install the latest Python version and create virtual environment. .. code-block:: shell uv venv Activate the virtual environment. .. tab-set:: .. tab-item:: Linux and macOS :sync: linux .. code-block:: shell source .venv/bin/activate .. tab-item:: Windows PowerShell :sync: powershell .. code-block:: powershell .venv\Scripts\Activate.ps1 Install icalendar-anonymizer, its dependencies, and the CLI extras. .. code-block:: shell uv pip install "icalendar-anonymizer[cli]" Verify installation with the following command. .. code-block:: shell ican --version This should output the package name and its version number. Commands ======== Two commands are provided. :program:`icalendar-anonymize` Full command name :program:`ican` Short alias for convenience Both commands work identically. Usage ===== This section describes how to use the command-line :program:`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. .. code-block:: shell ican [OPTIONS] [INPUT] [-o OUTPUT] For a complete list of command options and field configuration options, either see :ref:`options-reference` or :ref:`field-configuration-options`, or use the following command. .. code-block:: shell ican --help Anonymize a file ---------------- Read from a file and write to another file: .. code-block:: shell ican calendar.ics -o anonymized.ics Verbose output with the ``-v`` option shows progress. .. code-block:: shell ican -v calendar.ics -o anonymized.ics Write to ``stdout`` ------------------- Omit the ``-o`` flag to write to ``stdout``: .. code-block:: shell ican calendar.ics Read from ``stdin`` ------------------- Omit the input argument to read from ``stdin``: .. code-block:: shell cat calendar.ics | ican > anonymized.ics Field configuration ------------------- For a complete list of supported fields, use the ``--help`` option. .. code-block:: shell ican --help Keep summaries, remove locations. .. code-block:: shell ican --summary keep --location remove calendar.ics Replace descriptions with placeholders. .. code-block:: shell ican --description replace calendar.ics Combine multiple field modes. .. code-block:: shell 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. .. tab-set:: .. tab-item:: Linux and macOS :sync: linux .. code-block:: shell cat calendar.ics | ican > anonymized.ics .. tab-item:: Windows PowerShell :sync: powershell .. code-block:: powershell ican calendar.ics -o anonymized.ics Read from ``stdin`` explicitly with ``-``. .. tab-set:: .. tab-item:: Linux and macOS :sync: linux .. code-block:: shell ican - < calendar.ics > anonymized.ics .. tab-item:: Windows PowerShell :sync: powershell 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. .. code-block:: powershell ican calendar.ics -o anonymized.ics Verbose output to ``stderr`` doesn't corrupt ``stdout``. .. tab-set:: .. tab-item:: Linux and macOS :sync: linux .. code-block:: shell cat calendar.ics | ican -v > anonymized.ics .. tab-item:: Windows PowerShell :sync: powershell .. code-block:: powershell ican -v calendar.ics -o anonymized.ics Batch processing ---------------- Anonymize all ICS files in directory. .. tab-set:: .. tab-item:: Linux and macOS :sync: linux .. code-block:: shell for file in *.ics; do ican "$file" -o "anonymized-$file" done .. tab-item:: Windows PowerShell :sync: powershell .. code-block:: powershell Get-ChildItem *.ics -File | ForEach-Object { ican $_.Name -o "anonymized-$($_.Name)" } Process files from a list. .. tab-set:: .. tab-item:: Linux and macOS :sync: linux .. code-block:: shell while read -r file; do ican "$file" -o "anon-$(basename "$file")" done < file-list.txt .. tab-item:: Windows PowerShell :sync: powershell .. code-block:: powershell 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. .. tab-set:: .. tab-item:: Linux and macOS :sync: linux .. code-block:: shell curl https://example.com/calendar.ics | ican > local-anon.ics .. tab-item:: Windows PowerShell :sync: powershell .. code-block:: powershell 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. .. tab-set:: .. tab-item:: Linux and macOS :sync: linux .. code-block:: shell curl -f https://example.com/calendar.ics | ican -v > local-anon.ics .. tab-item:: Windows PowerShell :sync: powershell .. code-block:: powershell 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. .. tab-set:: .. tab-item:: Linux and macOS :sync: linux .. code-block:: shell ican calendar.ics | grep -c "BEGIN:VEVENT" .. tab-item:: Windows PowerShell :sync: powershell .. code-block:: powershell (ican calendar.ics | Select-String "BEGIN:VEVENT").Count Anonymize and validate the input. .. code-block:: shell ican calendar.ics | Compress the anonymized output. .. tab-set:: .. tab-item:: Linux and macOS :sync: linux .. code-block:: shell ican calendar.ics | gzip > anonymized.ics.gz .. tab-item:: Windows PowerShell :sync: powershell 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 `_. .. code-block:: powershell 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. .. tab-set:: .. tab-item:: Linux and macOS :sync: linux .. code-block:: shell ican --summary keep calendar.ics | gzip > debug-anon.ics.gz .. tab-item:: Windows PowerShell :sync: powershell .. code-block:: powershell 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 :doc:`python-api`: .. seealso:: See :ref:`python-api-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 -------------- .. code-block:: text $ ican nonexistent.ics Error: Could not open 'nonexistent.ics': No such file or directory Exit code: ``2``. Invalid ICS file ---------------- .. code-block:: text $ echo "invalid content" | ican Error: Invalid ICS file - Expected instance of Exit code: ``1``. Empty input ----------- .. code-block:: text $ echo "" | ican Error: Input is empty Exit code: ``1``. Permission denied ----------------- .. code-block:: text $ ican protected.ics -o /root/output.ics Error: [Errno 13] Permission denied: '/root/output.ics' Exit code: ``1``. Keyboard interrupt ------------------ .. code-block:: text $ ican large-file.ics ^C Interrupted Exit code: ``130``. Exit codes ========== The CLI follows Unix conventions for exit codes: .. list-table:: :header-rows: 1 :widths: 10 20 70 * - 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 :kbd:`Ctrl+C` (SIGINT) Troubleshooting =============== The following sections provide troubleshooting tips. Command not found ----------------- If you get ``command not found`` after installation: #. Reinstall the package with the CLI extras: .. code-block:: shell uv pip install --reinstall "icalendar-anonymizer[cli]" #. Use the full Python module path: .. code-block:: shell 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. .. code-block:: shell 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. .. code-block:: shell ican -v large-file.ics -o output.ics - Use the :doc:`python-api` for programmatic control over memory usage. Debugging --------- Enable verbose mode with the ``-v`` option to see processing steps. .. code-block:: shell ican -v calendar.ics -o anonymized.ics Check the exit code after running ``ican``, according to your operating system and shell. .. tab-set:: .. tab-item:: Linux and macOS :sync: linux .. code-block:: shell ican calendar.ics echo $? .. tab-item:: Windows cmd :sync: windows-cmd .. code-block:: batch ican calendar.ics echo %ERRORLEVEL% .. tab-item:: Windows PowerShell :sync: powershell .. code-block:: ps1con 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: .. code-block:: shell #!/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: .. code-block:: shell # Crontab entry: Anonymize daily at 2 AM 0 2 * * * /usr/bin/ican /path/to/calendar.ics -o /path/to/anon.ics .. todo:: Move this subsection into a separate reference section in the documentation. See :issue:`153`. .. _options-reference: Options reference ================= .. program:: icalendar-anonymize .. option:: [INPUT] Input iCalendar file to anonymize. Optional positional argument. - **Default**: ``stdin`` (``-``) - **Format**: File path or ``-`` for ``stdin`` - **Example**: :code:`ican calendar.ics` .. option:: -o , --output Output file for anonymized calendar. - **Default**: ``stdout`` (``-``) - **Format**: File path or ``-`` for ``stdout`` - **Example**: :code:`ican input.ics -o output.ics` .. option:: -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**: :code:`ican -v calendar.ics -o anonymized.ics` The following example shows verbose output: .. code-block:: text Reading from: calendar.ics Parsing calendar... Anonymizing calendar... Writing to: anonymized.ics Done. .. option:: --version Display version information and exit. .. code-block:: shell $ ican --version icalendar-anonymizer, version .. option:: --help Show usage information and exit. .. code-block:: shell 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 :issue:`148` for a related Click formatting quirk. .. _field-configuration-options: Field configuration options ---------------------------- Configure how individual fields are anonymized. The four modes are ``keep``, ``remove``, ``randomize``, and ``replace``. .. option:: --summary Mode for SUMMARY field. - **Choices**: ``keep``, ``remove``, ``randomize``, ``replace`` - **Default**: ``randomize`` - **Example**: :code:`ican --summary keep calendar.ics` .. option:: --description Mode for DESCRIPTION field. .. option:: --location Mode for LOCATION field. .. option:: --comment Mode for COMMENT field. .. option:: --contact Mode for CONTACT field. .. option:: --resources Mode for RESOURCES field. .. option:: --categories Mode for CATEGORIES field. .. option:: --attendee Mode for ATTENDEE field. .. option:: --organizer Mode for ORGANIZER field. .. option:: --uid Mode for UID field. .. note:: The ``remove`` mode is not allowed. - **Choices**: ``keep``, ``randomize``, ``replace`` - **Default**: ``randomize`` See also ======== - :doc:`python-api` - Python API for programmatic usage - :doc:`../installation` - Installation instructions - :doc:`../api/index` - Complete API reference - :doc:`../contributing` - Development guide