Linux Process Management for Developers

Linux Process Management for Developers

Understanding Linux process management is essential for debugging performance issues, troubleshooting stuck applications, and optimizing resource usage. Every running program on Linux is a process, and the kernel provides a rich set of tools to inspect, monitor, and control them. This article covers the essential commands and concepts every developer should know.

Inspecting Running Processes

The ps command provides a snapshot of currently running processes. With different options, you can customize the output to show the information most relevant to you. The combination ps aux shows all processes from all users in a detailed format including CPU and memory usage. For real-time monitoring, htop provides an interactive, color-coded interface with tree view, filtering, and the ability to send signals to processes directly. The --sort flag lets you order processes by memory or CPU usage to quickly identify resource hogs.

# List all processes with custom output format
ps -eo pid,ppid,cmd,%mem,%cpu,user --sort=-%mem | head -20

# Show process tree
ps auxf

# Real-time monitoring
htop
# F5: tree view | F4: filter by name | F9: kill process

# Find a specific process by name
pgrep -fl nginx

# Detailed information about a specific PID
ps -fp 1234
ls -l /proc/1234/

The /proc filesystem is a virtual filesystem that exposes kernel data structures as files. Each running process has a directory at /proc/PID/ containing information about its command line (cmdline), environment variables (environ), open file descriptors (fd/), memory mappings (maps), and status (status). Reading these files programmatically is often faster and more flexible than parsing command output.

Systemd Service Management

Modern Linux distributions use systemd as the init system. Services are defined by unit files (.service files in /etc/systemd/system/) and managed with the systemctl command. Systemd handles starting services at boot, restarting them on failure, and collecting their logs through journald. Always check the status of a service before debugging other issues — the status output shows whether the service is running, the last few log lines, and whether it is enabled to start at boot.

# Manage services
systemctl status nginx          # check status and recent logs
systemctl start nginx           # start a service
systemctl stop nginx            # stop a service
systemctl restart nginx         # restart (reload config)
systemctl enable --now docker   # enable at boot and start now

# View logs for a service
journalctl -u nginx -f          # follow logs in real time
journalctl -u nginx --since "1 hour ago"
journalctl -u nginx -p err      # only error level and above

# Create a custom service unit
cat > /etc/systemd/system/myapp.service << 'EOF'
[Unit]
Description=My Python Application
After=network.target

[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/python3 /opt/myapp/main.py
Restart=on-failure
RestartSec=5
EnvironmentFile=/opt/myapp/.env

[Install]
WantedBy=multi-user.target
EOF

Control Groups (cgroups)

Cgroups allow you to limit and account for the resource usage of process groups. Systemd exposes cgroup v2 through service unit properties. You can set CPU quotas, memory limits, and I/O weights to prevent one service from starving others. This is the foundation of container resource limits in Docker and Kubernetes.

# Set resource limits on a systemd service
systemctl set-property myapp.service CPUQuota=50%     # max 50% of one CPU
systemctl set-property myapp.service MemoryMax=512M    # max 512 MB RAM
systemctl set-property myapp.service IOWeight=100      # I/O priority

# View cgroup limits and current usage
systemctl show myapp.service | grep -E 'CPUQuota|MemoryMax|IOWeight'
cat /sys/fs/cgroup/system.slice/myapp.service/memory.current
cat /sys/fs/cgroup/system.slice/myapp.service/cpu.stat

# Check OOM kills
dmesg | grep -i "killed process"

Signals and Killing Processes

Signals are the Linux mechanism for notifying processes of events. The most commonly used signals are SIGTERM (15) — a polite request to terminate, allowing graceful cleanup — and SIGKILL (9) — an immediate forced termination that the process cannot catch or ignore. Always try SIGTERM first and only use SIGKILL as a last resort. The kill command sends signals by PID, while killall and pkill send signals by process name. The SIGHUP signal (1) is often used to tell daemons to reload their configuration without restarting. For background jobs, jobs, fg, and bg let you manage running and suspended processes interactively.

# Send signals
kill -15 1234            # SIGTERM — graceful shutdown
kill -9 1234             # SIGKILL — force kill (last resort)
killall -15 nginx        # SIGTERM all nginx processes
pkill -f "python.*server" # kill by command pattern

# Reload configuration (SIGHUP)
kill -1 1234
systemctl reload nginx   # equivalent for systemd services

# Manage background jobs
sleep 100 &              # start in background
jobs                     # list background jobs
fg %1                    # bring job 1 to foreground
Ctrl+Z                   # suspend foreground job
bg %1                    # resume suspended job in background

The nohup command runs a process that ignores SIGHUP (so it survives terminal closure) and redirects output to nohup.out. This is useful for long-running SSH sessions where you want a process to continue after disconnecting. For more robust background execution, use tmux or screen terminal multiplexers — they create persistent sessions that survive network interruptions and can be reattached later.

Monitoring process health is an ongoing task. Use htop for interactive debugging, systemctl for service management, journalctl for log analysis, and cgroups for resource limits. Combining these tools gives you full visibility into what your system is running and how resources are being consumed.

Shell Scripting: Automating System Administration

Shell Scripting: Automating System Administration

Shell scripting is the system administrator’s most essential tool. A well-written bash script can automate repetitive tasks, enforce consistency, and save hours of manual work. This article covers the fundamentals of robust shell scripting — error handling, file operations, scheduling, and logging — with practical examples you can adapt immediately.

Writing Robust Scripts

Every production script should start with set -euo pipefail. This combination of options makes bash behave more predictably: -e exits immediately if any command fails (instead of continuing with errors), -u treats unset variables as errors (preventing typos from silently expanding to empty strings), o pipefail makes a pipeline fail if any command in it fails (not just the last one). Without these options, a script might silently continue after a critical failure, leading to corrupted data or inconsistent state.

#!/bin/bash
set -euo pipefail

# Configuration
BACKUP_DIR="/var/backups/$(date +%Y%m%d)"
LOG_FILE="/var/log/backup.log"
RETENTION_DAYS=30

# Logging function
log() {
    local level="$1"
    local message="$2"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message" | tee -a "$LOG_FILE"
}

log "INFO" "Starting backup to $BACKUP_DIR"
mkdir -p "$BACKUP_DIR"

# Compress large log files
for file in /var/log/*.log; do
    if [[ -f "$file" && $(stat -c%s "$file") -gt 1048576 ]]; then
        log "INFO" "Compressing $file ($(stat -c%s "$file") bytes)"
        gzip "$file"
    fi
done

log "INFO" "Backup complete"

The log function demonstrates a common pattern: a centralized logging function that adds timestamps, severity levels, and writes to both stdout and a log file using tee -a. This gives you both real-time visibility during manual execution and a persistent log for later review or automated monitoring.

File and Directory Operations

Bash provides powerful file-test operators for checking file properties before operating on them. Always test conditions explicitly with [[ ]] rather than assuming a file exists or a command succeeded. The most useful tests are -f (regular file exists), -d (directory exists), -s (file exists and is non-empty), and -x (file is executable). Use stat for detailed metadata like file size, modification time, and permissions.

# File existence checks
if [[ ! -d "$BACKUP_DIR" ]]; then
    log "ERROR" "Backup directory does not exist"
    exit 1
fi

# Check if file is older than N days
find /tmp -name "*.tmp" -mtime +7 -delete

# Size-based filtering
for f in /data/*.csv; do
    size=$(stat -c%s "$f")
    if [[ $size -gt 100000000 ]]; then  # > 100 MB
        log "WARN" "$f is $size bytes — splitting recommended"
    fi
done

Scheduling with Cron

Cron is the standard job scheduler on Linux. A crontab entry has five time fields (minute, hour, day of month, month, day of week) followed by the command to execute. Always use absolute paths in cron jobs because cron runs with a minimal environment — PATH is often just /usr/bin:/bin. Redirect both stdout and stderr to a log file so that errors are captured. To avoid overlapping executions (if a job takes longer than its interval), use a lock file with flock.

# Crontab format: minute hour day month weekday command

# Run backup daily at 2 AM
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

# Rotate logs every Sunday at midnight
0 0 * * 0 /usr/local/bin/rotate-logs.sh

# Health check every 5 minutes
*/5 * * * * /usr/local/bin/health-check.sh

# Avoid overlapping runs with flock
0 * * * * /usr/bin/flock -n /tmp/deploy.lock /usr/local/bin/deploy.sh

Error Handling and Notifications

When a scheduled task fails, someone needs to know. Use a notification function that sends alerts via email, Slack webhook, or a monitoring API. Trap the EXIT signal to run cleanup code regardless of how the script exits — whether successfully, by error, or by being killed.

# Trap for cleanup
cleanup() {
    local exit_code=$?
    if [[ $exit_code -ne 0 ]]; then
        log "ERROR" "Script failed with exit code $exit_code"
        curl -X POST -H 'Content-Type: application/json'             -d '{"text": "Backup script failed!"}'             https://hooks.slack.com/services/TOKEN
    fi
    rm -f /tmp/backup.lock
}
trap cleanup EXIT

# Acquire exclusive lock
exec 200>/tmp/backup.lock
flock -n 200 || { log "ERROR" "Another instance is running"; exit 1; }

Well-written shell scripts are the foundation of reliable system administration. By using strict error handling, structured logging, cron scheduling with locks, and notification on failure, you can trust your automation to run correctly — and to alert you immediately when it does not.

Idempotent Scripts and Error Handling

Production shell scripts should be idempotent—running them multiple times produces the same result as once. Use conditional checks before creating files, idempotent commands (mkdir -p, rm -f, cp -n), and cleanup traps that run even on failure. The set -euo pipefail strict mode catches errors. Logging with timestamps creates an audit trail. Provide a –yes or –force flag for CI/CD automation. Validate inputs and check that required tools are installed. These practices make scripts maintainable and safe for automated deployments.

Containerization with Docker on Linux

Containerization with Docker on Linux

Docker revolutionized software deployment by packaging applications and their dependencies into lightweight, portable containers. Unlike virtual machines, containers share the host operating system kernel, making them faster to start and far more memory-efficient. This article walks through building a containerized Java application with Docker and orchestrating multi-service setups with Docker Compose.

What Is a Docker Container?

A container is a runtime instance of a Docker image. The image is a read-only template containing the application code, runtime, libraries, and configuration. Docker images are built in layers, where each instruction in the Dockerfile adds a new layer. Layers are cached, so rebuilding after a source change only re-adds the layers that changed. This makes Docker builds both fast and reproducible.

Writing a Dockerfile

The Dockerfile is a recipe that tells Docker how to build your image. Every Dockerfile starts with a FROM instruction that specifies a base image. Choosing a minimal base like Alpine Linux keeps images small — the Eclipse Temurin JDK 21 Alpine image is under 200 MB compared to over 400 MB for the full Ubuntu-based one.

FROM eclipse-temurin:21-jdk-alpine
WORKDIR /app
COPY target/app.jar .
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Each line in this Dockerfile has a specific purpose. WORKDIR /app sets the working directory inside the container to /app. COPY target/app.jar . copies the compiled JAR file from the host’s target/ directory into the container’s working directory. EXPOSE 8080 is documentation — it tells anyone running the container that the application listens on port 8080 but does not actually publish the port. ENTRYPOINT defines the command that runs when the container starts. Build the image with docker build -t myapp . and run it with docker run -p 8080:8080 myapp, which maps the host’s port 8080 to the container’s port 8080.

Multi-Stage Builds

For compiled languages like Java or Go, you can use multi-stage builds to keep the final image small. One stage compiles the code using a full SDK image, and a second stage copies only the compiled artifact into a minimal runtime image. This way, build tools like Maven or Gradle are not part of the final image.

# Stage 1: Build
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests

# Stage 2: Runtime
FROM eclipse-temurin:21-jdk-alpine
WORKDIR /app
COPY --from=build /app/target/app.jar .
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

The final image contains only the JRE (or JDK) and the JAR — not Maven, not the source code, and not the Maven cache. This reduces the image from over 1 GB to around 180 MB.

Docker Compose for Multi-Service Applications

Most real-world applications involve multiple services: a web server, a database, a cache, and perhaps a message queue. Docker Compose lets you define all services in a single compose.yaml file and start them with one command: docker compose up.

services:
  web:
    build: .
    ports:
      - "8080:8080"
    depends_on:
      - db
    environment:
      - DATABASE_URL=jdbc:postgresql://db:5432/mydb

  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret

volumes:
  pgdata:

The depends_on field ensures the database service starts before the web service. Services communicate over an internal Docker network using their service names as hostnames — so the web app connects to db:5432 instead of localhost:5432. The volumes section creates a named volume pgdata that persists the database data across container restarts, preventing data loss when the container is recreated.

Useful Docker Commands

# List running containers
docker ps

# View logs from a container
docker logs -f myapp

# Execute a command inside a running container
docker exec -it myapp sh

# Clean up unused resources
docker system prune -af

# Inspect image layers
docker history myapp:latest

Docker containers are ephemeral by design — treat them as disposable. Store state in volumes or external services. With this approach, you can deploy, scale, and update applications reliably across any Linux server, from your laptop to a production Kubernetes cluster.

Docker Networking and Security

Docker networking has three built-in drivers: bridge (default, isolated network per container group), host (container uses host network stack), and overlay (multi-host networking for Docker Swarm). For security, run containers as non-root users, drop Linux capabilities, use read-only root filesystems, and enable Content Trust to verify image signatures. Use Docker Bench Security to audit container configurations. Multi-stage builds separate build dependencies from runtime dependencies—the final image only contains the compiled binary and minimal runtime libraries, reducing both attack surface and deployment time.

Docker Compose and Development Workflows

Docker Compose defines multi-container applications in a docker-compose.yml file, enabling one-command startup of the entire development environment (web server, database, cache, message queue). Compose features include: dependency-based startup order (depends_on with health checks), environment variable files (.env), named volumes for persistent data, network configuration for service discovery, and health checks for container readiness. The compose watch feature (Docker Compose 2.23+) automatically syncs file changes and rebuilds containers, enabling hot-reloading development workflows. For testing, Compose can spin up test infrastructure (test databases, mock services) alongside test suites, then tear everything down with docker compose down. Profiles in Compose allow starting different service subsets for development vs. production-like testing.

Linux Powers Web Evolution

Linux Powers Web Evolution

Linux is the operating system that powers the modern web. From the servers that host websites to the cloud infrastructure that runs SaaS applications, Linux dominates the server market with over 96% market share among the top one million websites. This dominance is not accidental—Linux offers stability, security, flexibility, and cost-effectiveness that proprietary operating systems cannot match for web infrastructure.

The LAMP Stack and Its Legacy

The LAMP stack (Linux, Apache, MySQL, PHP/Python/Perl) has been the foundation of web development for over two decades. Linux provides the operating system layer with robust process isolation, file permissions, and networking. Apache HTTP Server handles HTTP requests with modules for URL rewriting, authentication, load balancing, and SSL termination. MySQL (or MariaDB) stores relational data, and the scripting language generates dynamic content. While modern stacks often replace Apache with Nginx, MySQL with PostgreSQL, and add Node.js, Redis, and Docker, the Linux foundation remains constant.

# Typical LAMP server setup on Ubuntu
apt update && apt install -y apache2 mysql-server php libapache2-mod-php

# Replace Apache with Nginx for better performance
apt install -y nginx php-fpm mysql-server

# Nginx config for a PHP application
server {
    listen 80;
    server_name example.com;
    root /var/www/html;
    index index.php index.html;
    location / {
        try_files $uri $uri/ /index.php?$args;
    }
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
    }
}

Linux as the Cloud Foundation

Every major cloud platform—AWS, Google Cloud, Azure, DigitalOcean, Linode—runs Linux as the primary operating system for their virtual machines and container services. AWS’s EC2 instances, Google Compute Engine VMs, and Azure Virtual Machines all support Linux images that boot in seconds and scale to thousands of cores. Linux’s container story is unmatched: Docker runs natively on Linux using kernel namespaces and cgroups, and Kubernetes orchestrates containers at scale across clusters. The entire cloud-native ecosystem (Terraform, Prometheus, Grafana, Envoy, etcd) runs on Linux first.

# Install Docker on Linux
apt install -y docker.io docker-compose-v2
systemctl enable --now docker

# Run a containerized web app
docker run -d --name myapp -p 8080:80 nginx:alpine

# Deploy with Kubernetes (minikube for local testing)
kubectl create deployment web --image=nginx:alpine
kubectl expose deployment web --port=80 --type=LoadBalancer

Security and Reliability Advantages

Linux’s security model—discretionary access control, user/group permissions, capability-based security, and mandatory access control via SELinux or AppArmor—provides defense in depth for web applications. Regular security updates through package managers (apt, yum) and the ability to apply kernel live patches without rebooting minimize downtime. The principle of least privilege is built into the system: web servers run as the www-data user with limited permissions, and systemd sandboxing restricts service capabilities. Linux servers with proper configuration have uptimes measured in years, and the modular kernel allows loading only the drivers and modules needed for the specific workload.

The DevOps Ecosystem

Linux is the native environment for DevOps tooling. CI/CD pipelines (Jenkins, GitLab CI, GitHub Actions) run on Linux agents. Configuration management (Ansible, Puppet, Chef) targets Linux servers. Infrastructure as code (Terraform, Pulumi) provisions Linux resources. Monitoring and observability (Prometheus, Grafana, ELK Stack) are Linux-native. The terminal-centric culture of Linux enables automation through shell scripts, cron jobs, and systemd timers. For web developers, understanding Linux—file permissions, process management, systemd units, network configuration, and package management—is not optional; it is essential for deploying and operating web applications in production.

Server Hardening Best Practices

Securing a Linux web server requires multiple layers: fail2ban blocks IPs after repeated failed SSH login attempts; unattended-upgrades installs security patches automatically; UFW or iptables restricts ports to only what is needed (22/SSH, 80/HTTP, 443/HTTPS); SSH key authentication replaces passwords; and regular log review (journalctl, /var/log/auth.log, /var/log/nginx/access.log) detects intrusion attempts. The CIS Benchmarks provide detailed hardening guidelines for each Linux distribution. SELinux (CentOS/RHEL) or AppArmor (Ubuntu/Debian) enforces mandatory access control policies that limit what compromised processes can access, providing defense in depth. Regular vulnerability scanning with tools like Lynis or OpenVAS identifies configuration weaknesses before attackers do. A hardened Linux server, properly configured and maintained, can run for years without security incidents even when exposed to the open internet.

Linux Distribution Choices for Web Servers

Ubuntu Server LTS (released every two years in April) is the most popular Linux distribution for web servers, offering a balance of stability and up-to-date packages. Debian Stable prioritizes stability above all else—packages are older but thoroughly tested. CentOS Stream tracks between Fedora and RHEL, suitable for enterprise environments requiring RHEL compatibility without a subscription. Alpine Linux, at under 5 MB base install size, is the most popular Docker base image—its musl libc and busybox utilities produce minimal attack surfaces and fast build times. For ARM-based servers (AWS Graviton, Raspberry Pi), Ubuntu Server and Debian offer excellent ARM support. All these distributions share the Linux kernel and GNU tools, so skills transfer between them.

Mastering the Command Line Interface (CLI): Exploring Bash, Terminal, Command Prompt & PowerShell

CLI stands for Command Line Interface, which is a way of interacting with a computer program or operating system through a text-based interface rather than a graphical user interface (GUI). A CLI allows users to enter commands into a command prompt or terminal window to perform tasks such as navigating the file system, running programs, and configuring system settings.

Bash, Terminal, Command Prompt, and Power Shell are all examples of command-line interfaces used in different operating systems.

Bash (Bourne-Again SHell) is a popular shell program that is commonly used on Linux and other Unix-based operating systems. It provides a command-line interface for executing commands, running scripts, and manipulating files and directories. Some useful features of Bash are:

  1. Scripting Capabilities: Bash is a powerful scripting language that allows for automation and the creation of complex scripts and programs.
  2. Availability: Bash is pre-installed on most Linux and Unix-based systems, making it readily available for use.
  3. Customizability: Bash can be customized to meet the needs of the user with the use of scripts, aliases, and configuration files.
  4. Interoperability: Bash can work with a wide range of command-line tools and utilities, making it compatible with many different systems and applications.
  5. Flexibility: Bash can be used for a variety of tasks, from simple one-liner commands to complex shell scripts.

Bash is a powerful and flexible command-line interface and scripting language, but its complexity and limitations may make it challenging for some users. Some of these challenges are:

  1. Steep Learning Curve: Bash can be difficult to learn for beginners, due to its syntax and many different commands and utilities.
  2. Limited Graphical Capabilities: Bash is primarily a command-line interface and does not have strong graphical capabilities, which can be limiting for certain tasks.
  3. Security Risks: Bash scripts and commands can potentially introduce security risks if not properly written or managed.
  4. Platform Dependence: While Bash is available on most Linux and Unix-based systems, it may not be available on other operating systems, which can limit its portability.
  5. Limited Interactivity: Bash is primarily used for running commands and scripts and may not be as interactive or user-friendly as other interfaces for certain tasks.

Terminal is a command-line interface that is used on Apple’s macOS operating system. It provides a window where users can enter commands and interact with the operating system. In many Linux distros CLI application has the name ‘Terminal’. While the names of the terminal applications may be the same on Linux and MacOS, there are differences in the way they function as underlying operating systems are not same. Linux terminal is usually Bash, while the default shell used in the macOS terminal is Zsh. Many of the command-line tools and utilities available in the Linux terminal are also available in the macOS terminal, there may be some differences in the versions or implementations of these tools

Command Prompt is a command-line interface that is used on Microsoft Windows operating systems. It provides a window where users can enter commands to perform tasks such as navigating the file system, running programs, and configuring system settings.

Power Shell is also a command-line interface developed by Microsoft for modern Windows operating systems. It provides an extensive scripting language and can be used to automate administrative tasks and system configuration.

While Cmd(Command Prompt) and PowerShell are both command-line interfaces used in Windows operating systems. There are some key differences between the two:

  1. Functionality: PowerShell is more powerful and feature-rich than Cmd, with support for advanced scripting and automation tasks. PowerShell also has access to .NET Framework libraries, allowing for more advanced scripting capabilities.
  2. Syntax: PowerShell uses a different syntax than Cmd, using cmdlets (short for “command-lets”) instead of traditional commands. Cmdlets are structured in a verb-noun format, making it easier to remember and use them.
  3. Command Support: PowerShell supports most of the commands available in Cmd, but also has its own set of unique commands. Cmd does not have access to many of the advanced features available in PowerShell.
  4. Output Formatting: PowerShell has more flexible output formatting options, allowing users to easily customize and filter output data. Cmd has limited output formatting capabilities.
  5. Cross-Platform Support: PowerShell is cross-platform, with versions available for Windows, Linux, and macOS. Cmd is only available on Windows operating systems.
  6. Learning Curve: PowerShell has a steeper learning curve than Cmd, due to its more complex syntax and advanced features.

While these CLI tools have different names and are used on different operating systems, they all provide similar functionality in terms of allowing users to enter commands to interact with the operating system and perform various tasks.

An interesting practical example to see the similarity and differences between these popular CLIs is the command to change the encoding of a file to ‘UTF-8’.

In Bash (on Linux or Unix-based systems) the command is iconv and has following syntax:

iconv -f [source_encoding] -t UTF-8 [input_file] > [output_file]

For example, to convert a file encoded in ISO-8859-1 to UTF-8 using Bash:

iconv -f ISO-8859-1 -t UTF-8 input.txt > output.txt

In Terminal (on macOS) the name of command is same but syntax is slightly different:

iconv -f [source_encoding] -t UTF-8 -o [output_file] [input_file]

For example, to convert a file encoded in ISO-8859-1 to UTF-8 using MacOS Terminal:

iconv -f ISO-8859-1 -t UTF-8 -o output.txt input.txt

On Command Prompt (on Windows) the command is ‘chcp’ and its syntax is:

chcp [code_page_number] & type [input_file] > [output_file]

For example, to convert a file encoded in ANSI (Windows-1252) to UTF-8 the command is:

chcp 1252 & type input.txt > output.txt

Power Shell (on Windows):

Get-Content -Path [input_file] -Encoding [source_encoding] | Set-Content -Path [output_file] -Encoding UTF8

For example, to convert a file encoded in ANSI (Windows-1252) to UTF-8 in Power Shell the command is:

Get-Content -Path input.txt -Encoding Default | Set-Content -Path output.txt -Encoding UTF8

Shell Scripting and Automation

The command line is the most productive interface for system administration, development workflows, and data processing. Essential commands include: ls (list files), find (search files by name/type/size), grep (search content), awk (text processing), sed (stream editing), chmod (permissions), ps (process status), top/htop (resource monitoring), and ssh (remote access). Combining commands with pipes (|) creates powerful one-liners: ps aux | grep python lists Python processes; find . -name “*.py” | xargs wc -l counts lines in all Python files. Shell scripts (.sh files) automate repetitive tasks with variables, conditionals, loops, and functions. Learn to use tab completion, command history (Ctrl+R for reverse search), and job control (Ctrl+Z to suspend, fg/bg to resume). The command line is not optional for professional developers—every deployment, debugging session, and data pipeline relies on CLI proficiency.

# One-liner to find largest files
find /var/log -type f -size +100M -exec ls -lh {} \; | sort -k5 -hr

# Count unique IPs in access log
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10