Linux Commands Every Developer Needs to Know

1. Overview

Every developer has SSH’d into a server, debugged in a terminal, or deployed an app, then fumbled around looking for the right command. This article compiles the most commonly used Linux commands in daily work: file management, searching, text processing, process monitoring, network checking, and log viewing. After reading, you’ll have a “survival kit” of commands to debug quickly without needing to Google the same thing every time.

I’ll use bash on Ubuntu/Debian as the main example, though these commands are almost identical across all distros.

2. File and Directory Operations

mkdir -p ~/projects/order-service/src/main/java
cd ~/projects/order-service
touch application.properties
ls -la
  • mkdir -p creates the entire directory tree, doesn’t complain if the directory already exists.
  • touch creates an empty file (or updates timestamp if it exists).
  • ls -la lists all files (including hidden ones like .env, .git) with permissions, owner, size.
drwxr-xr-x  3 nonla nonla 4096 Oct  5 10:00 .
-rw-r--r--  1 nonla nonla    0 Oct  5 10:00 application.properties

Some move/delete commands that need caution:

cp -r src/ src-backup/     # recursive copy directory
mv OrderService.java OrderServiceImpl.java   # rename or move
rm -rf target/            # recursive delete, no confirmation

Note: rm -rf has no recycle bin, deletion is permanent. Always verify your path with pwd before running, especially when copy-pasting commands from elsewhere.

3. Finding with find and grep

3.1. find — search files by criteria

find . -name "*.java" -newer pom.xml

This finds all .java files modified after pom.xml — useful to see which files changed before committing.

find . -type f -size +10M

Find files larger than 10MB, commonly used to clean up logs or build artifacts consuming space.

3.2. grep — search content in files

grep -rn "PaymentService" --include="*.java" src/
  • -r searches recursively through subdirectories.
  • -n prints with line numbers.
  • --include restricts to only .java files, avoiding searching in target/.
src/main/java/com/nonla/OrderController.java:15:    private final PaymentService paymentService;

Combine grep with find through a pipe for more complex filtering:

find . -name "*.log" | xargs grep -l "OutOfMemoryError"

xargs passes the file list from find to grep -l (only prints filenames containing the pattern, not the content).

4. Text Processing with awk, sed, cut

This group of commands is “log-eating” — extremely powerful when you need to extract data from verbose output.

cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -5
  • awk '{print $1}' takes the first column (usually IP) of each log line.
  • sort | uniq -c groups and counts occurrences.
  • sort -rn sorts descending by count, head -5 shows top 5.
    152 192.168.1.10
     98 10.0.0.5
     47 172.16.0.3

This is the fastest way to find which IP made the most requests without writing a script.

sed replaces text directly in a file:

sed -i 's/spring.profiles.active=dev/spring.profiles.active=prod/' application.properties

-i edits the file in-place. For extra safety, use sed -i.bak '...' to auto-create a .bak backup file.

cut extracts columns by delimiter, lighter than awk when you just need one simple column:

cat /etc/passwd | cut -d: -f1 | head -3

5. Process and Resource Monitoring

ps aux | grep java

Prints all processes, filters for lines containing java — quick way to see if your Spring Boot app is running and what its PID is.

nonla    12345  2.1  5.3 2345678 432100 ?      Sl   10:00   0:15 java -jar order-service.jar

The second column is the PID. Use this PID to kill or inspect further:

kill -15 12345     # send SIGTERM, allows app graceful shutdown
kill -9 12345       # force kill, only when -15 doesn't work

Note: kill -9 doesn’t let the app run shutdown hooks (like closing connection pools, flushing logs). Always try -15 first.

View resource usage in real-time:

top -o %MEM

-o %MEM sorts processes by highest memory usage — useful when suspecting a memory leak. For a nicer interface, install htop.

Check disk:

df -h
du -sh /var/log/*

df -h shows each partition’s space (human-readable). du -sh shows how much each subdirectory in /var/log uses — critical when the server says “no space left on device”.

6. Network and Port Checking

ss -tulnp | grep 8080
  • ss replaces the old netstat, much faster.
  • -t TCP, -u UDP, -l listening, -n numeric (don’t resolve DNS), -p include process.
tcp   LISTEN  0  128  0.0.0.0:8080  0.0.0.0:*  users:(("java",pid=12345,fd=45))

Know immediately that port 8080 is held by Java process PID 12345 — very useful when you hit Address already in use.

Quickly test an API with curl:

curl -s -X POST http://localhost:8080/api/orders \
  -H "Content-Type: application/json" \
  -d '{"customerId":1,"amount":99.5}'

-s silent (hide progress bar), -X POST sets method, -d sets JSON body. Add -i if you want to see response headers too.

Use lsof when you need to know exactly which files/sockets a process has open:

lsof -i :8080

7. File Permissions

chmod +x deploy.sh
chown nonla:nonla application.properties
chmod 640 .env
  • chmod +x makes a script executable.
  • chown user:group changes owner — usually needs sudo if the file belongs to another user.
  • chmod 640 means owner read/write (6), group read-only (4), others none (0) — standard for sensitive files like .env.
-rw-r----- 1 nonla nonla 128 Oct  5 10:00 .env

Note: never chmod 777 in production. That’s the fastest way to let every user on the machine read/modify your sensitive files.

8. Compression and Decompression

tar -czvf order-service.tar.gz order-service/
tar -xzvf order-service.tar.gz -C /opt/apps/
  • -c creates archive, -x extracts, -z uses gzip, -v verbose, -f specifies filename.
  • -C when extracting, specifies target directory instead of extracting to current directory.

When deploying, combine with scp or rsync to push files to a server:

scp order-service.tar.gz user@10.0.0.5:/opt/apps/
rsync -avz --progress target/*.jar user@10.0.0.5:/opt/apps/

rsync only transfers changed data (delta transfer), much faster than scp when updating large files multiple times.

9. Viewing Logs in Real-Time

tail -f /var/log/order-service/app.log

-f (follow) keeps the terminal open and prints new lines as the file grows — a must-know command for production debugging.

Filter logs by pattern while tailing:

tail -f app.log | grep --line-buffered "ERROR"

--line-buffered is necessary because grep normally buffers by block, without this flag output can be delayed when piping with tail -f.

If your app runs under systemd, use journalctl instead of manually finding log files:

journalctl -u order-service -f --since "10 minutes ago"

-u specifies the service unit, -f follows, --since limits time — avoid reading millions of old log lines.

10. Running Linux Commands from Java

Sometimes you need to call shell commands directly from Java code, for example checking disk space before writing a large file:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.List;

public class ShellRunner {

    public static String run(List<String> command) throws Exception {
        ProcessBuilder builder = new ProcessBuilder(command);
        builder.redirectErrorStream(true);
        Process process = builder.start();

        try (BufferedReader reader =
                new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            String output = reader.lines().reduce("", (a, b) -> a + b + "\n");
            process.waitFor();
            return output;
        }
    }
}
  • ProcessBuilder takes a list of arguments (not a hand-glued command string, avoiding shell injection).
  • redirectErrorStream(true) merges stderr into stdout so you read a single stream.
  • process.waitFor() blocks the current thread until the child process exits.

Test with JUnit 5 + AssertJ:

import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;

class ShellRunnerTest {

    @Test
    void shouldReturnCurrentDirectoryListing() throws Exception {
        String output = ShellRunner.run(List.of("ls", "-la"));

        assertThat(output).contains("total");
    }
}

This test runs ls -la for real on the system and checks if the output contains “total” (the first line ls -la always prints showing total blocks) — confirming ProcessBuilder really calls the shell command.

Note: calling shell commands from Java production code is a double-edged sword — easy to depend on OS, hard to test, and injection risk if arguments come from user input. Use only for internal tools or operational scripts, avoid for core business logic.

11. Common Mistakes

  • Running rm -rf with an empty environment variable, e.g. rm -rf $DIR/* when $DIR isn’t set — deletes the wrong directory. Always use set -u in scripts or check variables first.
  • Forgetting sudo when you need root permission, resulting in vague Permission denied errors — check with ls -la first before blaming the app.
  • Habitually using kill -9, so Spring Boot can’t close connection pools in time, causing database leaks.
  • Piping grep after tail -f but forgetting --line-buffered, thinking the logs aren’t flowing when they’re actually buffered.
  • Running chmod 777 to “quickly get past permission errors” — creates security holes, especially dangerous with .env or SSH key files.

12. Conclusion

The commands in this article — find, grep, awk/sed, ps/kill, ss/curl, tar, tail -f, journalctl — are the minimum toolkit to debug on your own without waiting for the DevOps team. Being good with them doesn’t mean remembering all the flags, just knowing which command solves what problem and looking up man when you need details. Terminal remains the fastest debugging tool, even with all the Grafana, Kibana, and APM tools around.

linuxdevopsbashproductivityjava