Linux Admin

Linux File & Directory Operations: ls, cd, cp, mv, rm, find

Part of pathway: Linux Mastery: 300 Commands

The Commands You Will Type More Than Any Others

Out of the 300-or-so commands a Linux engineer eventually learns, the ones that handle files and directories — ls, cd, cp, mv, rm, find, mkdir — are the ones you type ten thousand times a year. Knowing them deeply pays back constantly: muscle-memory commands save seconds; misuse loses files.

This article is a working reference for the file and directory commands every Linux user needs in their fingers, with the option flags that matter, the gotchas that bite, and the safer alternatives where they exist.

Where Am I? Where Do I Want To Go?

pwd                          # print working directory
cd /var/log                  # absolute path
cd ../etc                    # relative
cd ~                         # home directory
cd -                         # back to previous directory

cd - is one of those quietly indispensable commands. It toggles between the last two directories you were in — useful when you’re bouncing between /etc/nginx and /var/log/nginx while debugging.

Listing Files: ls Is Not Just ls

ls                           # bare names
ls -l                        # long form: perms, owner, size, date
ls -la                       # include hidden (.) files
ls -lh                       # human-readable sizes (1.2M, 3.4G)
ls -ltr                      # by mtime, oldest first
ls -lS                       # by size, largest first
ls -ld /etc                  # the directory itself, not its contents
ls -1                        # one entry per line (script-friendly)

Two patterns I use daily:

  • ls -ltrh — long-form, by time, oldest first, human sizes. The newest files are at the bottom (right where the prompt is).
  • ls -lA | wc -l — count entries excluding . and .. — a quick “how many things are in this directory.”

Creating: mkdir, touch

mkdir mydir                              # one directory
mkdir -p projects/2026/site              # create whole path
touch newfile.txt                        # empty file (or update mtime)
touch -d "2025-01-15" report.txt         # set explicit timestamp
install -d -m 0755 /opt/myapp/logs       # create with permissions in one shot

mkdir -p is the right default. It silently no-ops if the directory exists and creates intermediate path components — ideal for scripts.

Copy: cp

cp source.txt dest.txt                   # single file
cp -r dir1/ dir2/                        # recursive (directory)
cp -a backup/ /mnt/                      # archive: preserve perms, timestamps, links
cp -i a b                                # interactive: prompt before overwrite
cp -u src/ dst/                          # only newer files

Three behaviors people get wrong:

  • Trailing slash on source matters. cp -r dir/ /tmp/ copies the contents of dir into /tmp/; cp -r dir /tmp/ creates /tmp/dir/. Subtle and annoying.
  • cp -a is what you usually want for full backups. Preserves attributes, follows links sensibly, recurses.
  • cp overwrites silently by default. Pair with -i for interactive, or -n for “no clobber.”

Move and Rename: mv

mv old.txt new.txt                       # rename
mv file.txt /tmp/                        # move to another directory
mv -i old new                            # prompt before overwrite
rename 's/\.txt$/.md/' *.txt             # bulk rename (Perl version)

There is no separate rename verb on Linux — mv handles both. The rename utility is much more powerful for batch renames; on Debian/Ubuntu it’s the Perl version (regex), on RHEL it’s a simpler util-linux version.

Delete: rm (Carefully)

rm file.txt                              # delete a file
rm -i *.log                              # interactive
rm -r dir/                               # recursive (directory)
rm -rf dir/                              # force, no prompt — irreversible
rm -- -filename                          # delete file whose name starts with dash

rm -rf is permanent. No trash, no recycle bin. Once it’s gone, you’re restoring from backup. Two safety habits:

  1. Never type rm -rf $VAR/ without first echoing $VAR to verify it’s not empty. The day someone runs rm -rf / because $VAR was unset is the day someone learns about --no-preserve-root the hard way.
  2. Use find ... -print first, then find ... -delete. Verify the candidate file list before destroying it.

Find Things: find and locate

find /var/log -name "*.gz"                    # by name pattern
find / -type f -size +100M                    # files larger than 100MB
find . -mtime -7                              # modified in last 7 days
find . -name "*.tmp" -delete                  # find AND delete (test with -print first)
find / -user alice -type f                    # files owned by alice
find /etc -name "*.conf" -exec grep -l "ssl" {} \;
locate nginx.conf                             # uses pre-built index (mlocate)
updatedb                                      # rebuild the locate index

find is the heavyweight: live scan, infinite predicates. locate is the lightweight: instant lookup of a path index. The index is rebuilt nightly by cron — freshly created files won’t show up until the next updatedb run.

Symbolic Links

ln -s /usr/local/share/data /opt/data         # symbolic link
ln -sf source target                          # force overwrite existing link
readlink /opt/data                            # read what a symlink points to
realpath /opt/data                            # resolve to absolute path

The order is always ln -s <target> <name-of-link>. People mix it up; the rule is “source first, then where you want the new pointer.”

The 10 Commands That Cover 95% of Daily Use

  1. ls -ltrh — what’s here, sorted oldest-to-newest
  2. cd - — bounce between directories
  3. cp -a src/ dst/ — backup-style copy
  4. mv old new — rename or relocate
  5. mkdir -p path/to/new — create paths idempotently
  6. find / -name "file" 2>/dev/null — locate by name, suppress permission errors
  7. du -sh */ — size of every subdirectory in the current dir
  8. ln -sf src link — safely manage symbolic links
  9. rm -- -strange — delete a file whose name starts with a dash
  10. tree -L 2 — visual hierarchy (install if not present; tree is not in the base install)

Common Pitfalls

  • rm -rf with an unset variable. Always echo first. Better: set -u in scripts so unset variables abort.
  • Trailing-slash confusion in cp/rsync. Test on small data first.
  • Spaces in filenames. Quote everything: cp "my file.txt" /tmp/ — without quotes, that’s two arguments.
  • Forgetting -r on directories. cp dir other/ fails — need cp -r.
  • Locate is stale. Files newer than today’s updatedb won’t appear. Use find for current state.
  • ls -la output parsed in scripts. Don’t. Use find with -printf, or stat, for machine-readable output.

Conclusion

Five habits that separate fluent Linux users from people who fight the shell:

  1. Aliases for what you type 10x a day — alias ll='ls -ltrh' in ~/.bashrc.
  2. Tab-complete every path — mistyped paths cause 90% of file-op accidents.
  3. Echo before rm -rf — always.
  4. Quote filenames with spaces or special chars.
  5. Reach for find not ls | grep when scripting — one is robust, the other breaks on weird filenames.

Related Linux Admin troubleshooting

For common errors and fixes related to this topic, see:

Leave a Reply