Skip to content

csh - C Shell

The C Shell (csh) is a Unix shell developed by Bill Joy at the University of California, Berkeley, first released in 1978. Designed to provide a more C-like syntax for interactive use, csh introduced many features that became standard in later shells, including command history, aliases, and job control. While csh and its enhanced version tcsh are primarily used for interactive sessions rather than scripting, they remain important in certain Unix environments and offer unique features that some users prefer. Understanding csh is valuable for system administrators and users who encounter it in legacy systems or specific Unix distributions.

Installation and Setup

C Shell Variants

csh
# Check which csh variant is available
which csh
/bin/csh

which tcsh
/usr/bin/tcsh

# Check shell version
echo $version
# tcsh 6.22.04 (Astron) 2021-04-26 (x86_64-unknown-linux) options wide,nls,dl,al,kan,rh,color,filec

# Check if running csh or tcsh
echo $shell
/bin/tcsh

Installing C Shell

csh
# Ubuntu/Debian
sudo apt update && sudo apt install csh tcsh

# CentOS/RHEL/Fedora
sudo dnf install tcsh

# macOS (tcsh is usually pre-installed)
# For csh specifically:
brew install tcsh

# Arch Linux
sudo pacman -S tcsh

# FreeBSD (usually pre-installed)
pkg install tcsh

# Check installation
csh --version
tcsh --version

Setting csh/tcsh as Default Shell

csh
# Check current shell
echo $SHELL

# Add csh to available shells
echo /bin/csh | sudo tee -a /etc/shells
echo /bin/tcsh | sudo tee -a /etc/shells

# Set tcsh as default shell (recommended over csh)
chsh -s /bin/tcsh

# Verify change (restart terminal)
echo $SHELL
/bin/tcsh

Basic Configuration Files

csh
# Configuration files for csh/tcsh
~/.cshrc                       # Main configuration file
~/.tcshrc                      # tcsh-specific configuration
~/.login                       # Login shell configuration
~/.logout                      # Logout script

# Create basic .tcshrc
cat > ~/.tcshrc << 'EOF'
# tcsh configuration

# Set environment variables
setenv EDITOR vi
setenv PAGER less
setenv LANG en_US.UTF-8

# Set path
set path = (/usr/local/bin /usr/bin /bin $path)

# Aliases
alias ll 'ls -l'
alias la 'ls -la'
alias h 'history'
alias .. 'cd ..'

# Prompt
set prompt = '%n@%m:%c$ '

# History
set history = 1000
set savehist = 1000

# Completion
set autolist
set complete = enhance

# Other options
set noclobber
set notify
EOF

# Source configuration
source ~/.tcshrc

C Shell Syntax and Variables

Variable Assignment and Usage

csh
# Simple variable assignment
set name = "John Doe"
set age = 30
set path_var = "/home/user"

# Using variables
echo $name
echo "Hello, $name"
echo 'Literal: $name'          # Single quotes prevent expansion

# Array variables
set fruits = (apple banana orange)
set numbers = (1 2 3 4 5)

# Accessing array elements
echo $fruits[1]                # First element (1-indexed)
echo $fruits[2]                # Second element
echo $fruits[$#fruits]         # Last element
echo $fruits[*]                # All elements
echo $#fruits                  # Number of elements

# Environment variables
setenv PATH "/usr/local/bin:$PATH"
setenv EDITOR vi
setenv HOME /home/user

# Unset variables
unset name
unsetenv PATH                  # Dangerous!

# Special variables
echo $0                        # Shell name
echo $$                        # Process ID
echo $?                        # Exit status of last command
echo $#argv                    # Number of arguments
echo $argv[*]                  # All arguments

String Operations

csh
# String concatenation
set first = "Hello"
set second = "World"
set combined = "$first, $second!"
echo $combined                 # Hello, World!

# String length (tcsh)
set string = "Hello, World!"
echo $%string                  # Length: 13

# Substring operations (limited in csh)
set filename = "document.txt"
set basename = $filename:r     # Remove extension: document
set extension = $filename:e    # Get extension: txt
set dirname = $filename:h      # Get directory (if path)
set tail = $filename:t         # Get filename (if path)

# Case conversion (tcsh)
set upper = $string:u          # Uppercase
set lower = $string:l          # Lowercase

Command Substitution

csh
# Command substitution using backticks
set current_date = `date`
set file_count = `ls | wc -l`
set user_home = `eval echo ~$USER`

# Using command substitution in expressions
echo "Today is `date +%A`"
set files = (`ls *.txt`)

# Nested command substitution
set day_of_week = `date +%A`
echo "Today is $day_of_week, `date +%B` `date +%d`"

Control Structures

Conditional Statements

csh
# if-then-else
if ($age > 18) then
    echo "Adult"
else if ($age == 18) then
    echo "Just turned adult"
else
    echo "Minor"
endif

# String comparisons
if ("$name" == "John") then
    echo "Hello John"
endif

if ("$name" != "John") then
    echo "Not John"
endif

# File tests
if (-f "file.txt") then
    echo "File exists"
endif

if (-d "directory") then
    echo "Directory exists"
endif

if (-r "file.txt") then
    echo "File is readable"
endif

if (-w "file.txt") then
    echo "File is writable"
endif

if (-x "script.sh") then
    echo "File is executable"
endif

# Logical operators
if ($age > 18 && $age < 65) then
    echo "Working age"
endif

if ($status == 0 || $force == "true") then
    echo "Proceeding"
endif

if (! -f "file.txt") then
    echo "File does not exist"
endif

Switch Statements

csh
# switch statement
switch ($variable)
    case pattern1:
        echo "Matched pattern1"
        breaksw
    case pattern2:
    case pattern3:
        echo "Matched pattern2 or pattern3"
        breaksw
    default:
        echo "No pattern matched"
        breaksw
endsw

# Switch with file extensions
switch ($filename)
    case *.txt:
        echo "Text file"
        breaksw
    case *.jpg:
    case *.png:
    case *.gif:
        echo "Image file"
        breaksw
    case *.sh:
        echo "Shell script"
        breaksw
    default:
        echo "Unknown file type"
        breaksw
endsw

Loops

csh
# foreach loop
foreach item (apple banana orange)
    echo "Fruit: $item"
end

# foreach with array variable
set files = (*.txt)
foreach file ($files)
    echo "Processing: $file"
end

# foreach with command substitution
foreach user (`cat users.txt`)
    echo "User: $user"
end

# while loop
set counter = 1
while ($counter <= 10)
    echo "Counter: $counter"
    @ counter++
end

# while loop with file reading
set line = ""
while (1)
    set line = $<
    if ($line == "") break
    echo "Line: $line"
end

# Nested loops
foreach dir (/usr /opt /var)
    foreach file ($dir/*)
        if (-f $file) then
            echo "File: $file"
        endif
    end
end

Arithmetic Operations

Arithmetic with @ Command

csh
# Basic arithmetic
@ result = 5 + 3               # 8
@ result = 10 - 4              # 6
@ result = 6 * 7               # 42
@ result = 20 / 4              # 5
@ result = 17 % 5              # 2 (modulo)

# Arithmetic with variables
set num1 = 10
set num2 = 5
@ sum = $num1 + $num2          # 15
@ product = $num1 * $num2      # 50

# Increment and decrement
set counter = 0
@ counter++                    # Increment
@ counter--                    # Decrement
@ counter += 5                 # Add and assign
@ counter -= 3                 # Subtract and assign

# Complex expressions
@ result = ($num1 + $num2) * 2
@ result = $num1 ** 2          # Exponentiation (tcsh)

# Comparison operations
@ is_greater = ($num1 > $num2) # Returns 1 if true, 0 if false
if ($is_greater) then
    echo "num1 is greater than num2"
endif

External Arithmetic Tools

csh
# Using expr for complex calculations
set result = `expr 5 + 3`
set result = `expr $num1 \* $num2`  # Note: * must be escaped

# Using bc for floating point
set result = `echo "scale=2; 10/3" | bc`
set result = `echo "scale=4; sqrt(16)" | bc -l`

# Using awk for calculations
set result = `awk "BEGIN {print 10/3}"`

Aliases and History

Alias Management

csh
# Simple aliases
alias ll 'ls -l'
alias la 'ls -la'
alias h 'history'
alias .. 'cd ..'
alias ... 'cd ../..'

# Aliases with arguments
alias rm 'rm -i'               # Interactive removal
alias cp 'cp -i'               # Interactive copy
alias mv 'mv -i'               # Interactive move

# Complex aliases
alias lsd 'ls -l | grep "^d"'  # List only directories
alias psg 'ps aux | grep'      # Process search

# Conditional aliases
if (-f /usr/bin/vim) then
    alias vi vim
endif

# List aliases
alias                          # Show all aliases
alias ll                       # Show specific alias

# Remove aliases
unalias ll
unalias *                      # Remove all aliases

History Management

csh
# History configuration
set history = 1000             # Number of commands to remember
set savehist = 1000            # Number of commands to save to file

# History commands
history                        # Show all history
history 10                     # Show last 10 commands
history -r                     # Read history from file
history -w                     # Write history to file

# History expansion
!!                             # Previous command
!n                             # Command number n
!string                        # Last command starting with string
!?string                       # Last command containing string
^old^new                       # Replace old with new in previous command

# History modifiers
!:0                            # Command name only
!:1                            # First argument
!:$                            # Last argument
!:*                            # All arguments
!:1-3                          # Arguments 1 through 3

# Examples
echo !$                        # Echo last argument of previous command
cp file.txt !$.bak            # Copy file with .bak extension

Job Control and Process Management

Job Control

csh
# Background jobs
command &                      # Run command in background
jobs                          # List active jobs
fg %1                         # Bring job 1 to foreground
bg %1                         # Send job 1 to background
kill %1                       # Kill job 1

# Job control signals
# Ctrl+C: Interrupt (SIGINT)
# Ctrl+Z: Suspend (SIGTSTP)
# Ctrl+\: Quit (SIGQUIT)

# Process management
ps                            # Show current processes
ps aux                        # Show all processes
kill PID                      # Terminate process
kill -9 PID                   # Force kill process
killall process_name          # Kill all processes by name

# Nohup equivalent
nohup command &               # Run command immune to hangups

Process Information

csh
# Process variables
echo $$                       # Current shell PID
echo $!                       # PID of last background job

# Process status
echo $?                       # Exit status of last command
echo $status                  # Same as $? in csh

# Wait for processes
wait                          # Wait for all background jobs
wait %1                       # Wait for specific job

Input/Output and Redirection

Basic I/O Redirection

csh
# Output redirection
command > file.txt            # Redirect stdout to file
command >> file.txt           # Append stdout to file
command >& file.txt           # Redirect both stdout and stderr
command >>& file.txt          # Append both stdout and stderr

# Input redirection
command < input.txt           # Read input from file
sort < unsorted.txt > sorted.txt

# Pipes
ls -l | grep "txt"            # Pipe output to grep
ps aux | grep "process" | wc -l  # Count matching processes

# Here documents (limited support)
cat << EOF
This is a here document
Variables like $HOME are expanded
EOF

Advanced I/O Features

csh
# Noclobber option
set noclobber                 # Prevent overwriting files
command >! file.txt           # Force overwrite with noclobber set

# Tee equivalent
command | tee file.txt        # Write to file and stdout

# Error redirection
command >& /dev/null          # Redirect both stdout and stderr to null
(command > output.txt) >& error.log  # Separate stdout and stderr

Built-in Commands and Features

Essential Built-ins

csh
# echo command
echo "Simple message"
echo -n "No newline"          # tcsh only

# printf (tcsh only)
printf "%s: %d\n" "Count" 42
printf "%-10s %5d\n" "Name" 123

# set and setenv
set var = value               # Local variable
setenv VAR value              # Environment variable

# which and where
which command                 # Show command location
where command                 # Show all command locations (tcsh)

# Directory operations
cd directory                  # Change directory
pushd directory              # Push directory onto stack
popd                         # Pop directory from stack
dirs                         # Show directory stack

# File operations
ls -l                        # List files
cp source dest               # Copy files
mv old new                   # Move/rename files
rm file                      # Remove files
mkdir directory              # Create directory
rmdir directory              # Remove empty directory

tcsh-Specific Features

csh
# Command completion
set complete = enhance        # Enhanced completion
set autolist                 # Automatically list completions

# Spelling correction
set correct = cmd             # Correct commands
set correct = complete        # Correct completions
set correct = all             # Correct everything

# File completion
set filec                    # Enable filename completion

# Auto-logout
set autologout = 60          # Auto-logout after 60 minutes

# Watch for logins
set watch = (any any)        # Watch for any user login
set who = "%n has %a %l from %M at %t."  # Login message format

Configuration and Customization

Prompt Customization

csh
# Simple prompts
set prompt = '%n@%m:%c$ '     # user@host:dir$
set prompt = '% '             # Simple %
set prompt = '\! % '          # History number %

# Prompt escape sequences
# %n - Username
# %m - Hostname
# %c - Current directory (basename)
# %C - Current directory (full path)
# %/ - Current directory (full path)
# %~ - Current directory (with ~ substitution)
# %t - Time (12-hour)
# %T - Time (24-hour)
# %p - Time (AM/PM)
# %d - Day of week
# %D - Date
# %w - Month
# %W - Year
# %! - History number
# %# - # if root, % otherwise

# Advanced prompt with colors (tcsh)
set prompt = '%{\033[1;32m%}%n@%m%{\033[0m%}:%{\033[1;34m%}%c%{\033[0m%}$ '

# Multi-line prompt
set prompt = '%n@%m:%c\
$ '

# Conditional prompt
if ($uid == 0) then
    set prompt = 'root@%m:%c# '
else
    set prompt = '%n@%m:%c$ '
endif

Environment Configuration

csh
# Path management
set path = (/usr/local/bin /usr/bin /bin)
set path = ($path /opt/bin)   # Append to path

# Environment variables
setenv EDITOR vi
setenv PAGER less
setenv BROWSER firefox
setenv LANG en_US.UTF-8

# Platform-specific configuration
switch (`uname`)
    case Linux:
        setenv LS_COLORS 'di=34:ln=35:so=32:pi=33:ex=31:bd=46;34:cd=43;34:su=41;30:sg=46;30'
        breaksw
    case Darwin:
        setenv LSCOLORS ExFxCxDxBxegedabagacad
        breaksw
endsw

# Conditional environment
if (-d "$HOME/.local/bin") then
    set path = ($HOME/.local/bin $path)
endif

if ($?DISPLAY) then
    setenv BROWSER firefox
else
    setenv BROWSER lynx
endif

Shell Options

csh
# Important shell options
set noclobber                # Prevent file overwriting
set notify                   # Report job status immediately
set noglob                   # Disable filename expansion
set ignoreeof                # Don't exit on Ctrl+D

# tcsh-specific options
set autolist                 # List completions automatically
set complete = enhance       # Enhanced completion
set correct = cmd            # Correct commands
set filec                    # Filename completion
set histdup = erase          # Remove duplicate history entries
set listjobs = long          # Long format for job listing
set rmstar                   # Ask before rm *

Scripting Considerations

Script Structure

csh
#!/bin/csh -f
# Script description
# Note: -f flag prevents reading .cshrc

# Variable declarations
set script_name = $0:t
set script_dir = $0:h
set version = "1.0"

# Function equivalent (using goto/label)
goto main

usage:
    echo "Usage: $script_name [options] [arguments]"
    echo "Options:"
    echo "  -h    Show this help"
    echo "  -v    Show version"
    exit 0

version:
    echo "$script_name version $version"
    exit 0

error:
    echo "Error: $error_msg"
    exit 1

main:
    # Parse arguments
    while ($#argv > 0)
        switch ($argv[1])
            case -h:
                goto usage
            case -v:
                goto version
            case -*:
                set error_msg = "Unknown option: $argv[1]"
                goto error
            default:
                break
        endsw
        shift argv
    end

    # Main script logic
    echo "Script execution completed"
    exit 0

Best Practices for csh Scripts

csh
# Use tcsh instead of csh for scripts
#!/bin/tcsh -f

# Always use -f flag to avoid .cshrc interference
#!/bin/csh -f

# Error handling
set error_exit = 0
if (! -f "required_file.txt") then
    echo "Error: Required file not found"
    set error_exit = 1
endif

if ($error_exit) exit 1

# Input validation
if ($#argv == 0) then
    echo "Error: No arguments provided"
    exit 1
endif

# Safe variable usage
if ($?variable) then
    echo "Variable is set: $variable"
else
    echo "Variable is not set"
endif

# Avoid complex scripting in csh
# Use sh/bash for complex scripts
# Use csh/tcsh primarily for interactive use

Limitations and Alternatives

Known Limitations

csh
# csh scripting limitations:
# 1. No functions (use goto/labels instead)
# 2. Limited error handling
# 3. No local variables in "functions"
# 4. Inconsistent syntax
# 5. Poor signal handling

# Example of workaround for function-like behavior
goto main

# "Function" using goto/label
process_file:
    set file = $1
    if (-f $file) then
        echo "Processing $file"
        # Process file here
    else
        echo "File $file not found"
    endif
    goto return_point

main:
    set return_point = main_continue
    set argv[1] = "test.txt"
    goto process_file
    
main_continue:
    echo "Back in main"
    exit 0

When to Use csh/tcsh

csh
# Good for:
# - Interactive shell use
# - Simple automation tasks
# - Users familiar with C-like syntax
# - Legacy system compatibility

# Not recommended for:
# - Complex scripting (use bash/sh instead)
# - Portable scripts
# - Production automation
# - Error-critical applications

# Migration example from csh to bash
# csh version:
# if ($status == 0) then
#     echo "Success"
# endif

# bash equivalent:
# if [ $? -eq 0 ]; then
#     echo "Success"
# fi

Interoperability

csh
# Running bash scripts from csh
bash script.sh

# Converting csh variables for bash
setenv BASH_VAR $csh_var
bash -c 'echo $BASH_VAR'

# Calling csh from bash
csh -c 'echo $csh_variable'

# Mixed environment
# Use bash for scripting, csh for interactive
exec bash script.sh          # Execute bash script
exec tcsh                    # Return to tcsh

The C Shell and its enhanced version tcsh represent an important chapter in Unix shell evolution, introducing many features that became standard in later shells. While csh is not recommended for complex scripting due to various limitations and inconsistencies, it remains valuable for interactive use, particularly for users who prefer its C-like syntax and specific features. Understanding csh is important for system administrators who encounter it in legacy systems and for users who appreciate its unique approach to command-line interaction. For modern shell scripting needs, however, POSIX-compliant shells like bash or more advanced shells like zsh are generally preferred.