Cheatsheets:LinuxCLI/FileOps: Difference between revisions
| (16 intermediate revisions by 2 users not shown) | |||
| Line 4: | Line 4: | ||
New to the command line? Every command here has a built-in manual — try <syntaxhighlight lang="bash" inline>man ls</syntaxhighlight> or [https://tldr.sh tldr] for short examples; [https://explainshell.com explainshell.com] annotates any command line. | New to the command line? Every command here has a built-in manual — try <syntaxhighlight lang="bash" inline>man ls</syntaxhighlight> or [https://tldr.sh tldr] for short examples; [https://explainshell.com explainshell.com] annotates any command line. | ||
== Navigation & listing == | == Navigation & listing == | ||
| Line 29: | Line 16: | ||
'''ls''' — the workhorse: | '''ls''' — the workhorse: | ||
<syntaxhighlight lang="bash" copy> | |||
ls [options] [dir] # list directory contents | |||
ls *.txt # glob: only matching names | |||
</syntaxhighlight> | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Option !! Effect | ||
|- | |- | ||
| <syntaxhighlight lang="bash" inline> | | <syntaxhighlight lang="bash" inline>-l</syntaxhighlight> || long format: permissions, owner, size, date | ||
|- | |- | ||
| <syntaxhighlight lang="bash" inline> | | <syntaxhighlight lang="bash" inline>-a</syntaxhighlight> || hidden files (dotfiles) | ||
|- | |- | ||
| <syntaxhighlight lang="bash" inline> | | <syntaxhighlight lang="bash" inline>-h</syntaxhighlight> || human-readable sizes (combine with -l) | ||
|- | |- | ||
| <syntaxhighlight lang="bash" inline> | | <syntaxhighlight lang="bash" inline>-R</syntaxhighlight> || recurse into subdirectories | ||
|- | |- | ||
| <syntaxhighlight lang="bash" inline> | | <syntaxhighlight lang="bash" inline>-t</syntaxhighlight> || sort by modification time, newest first | ||
|} | |} | ||
| Line 51: | Line 39: | ||
<syntaxhighlight lang="bash" copy> | <syntaxhighlight lang="bash" copy> | ||
mkdir newdir | mkdir newdir # create a directory | ||
mkdir -p dir/subdir/newsubsubdir | mkdir -p dir/subdir/newsubsubdir # create parents as needed | ||
mkdir -m 755 dir | mkdir -m 755 dir # set permissions at creation | ||
rmdir emptydir | rmdir emptydir # remove an EMPTY directory | ||
rmdir -p dir/subdir/emptysubdir | rmdir -p dir/subdir/emptysubdir # ... and its empty parents | ||
touch file.txt | touch file.txt # create an empty file (or refresh its mtime) | ||
touch -t 202501011200 f | touch -t 202501011200 f # set a specific timestamp | ||
rm file.txt | rm file.txt # delete a file | ||
rm -r dir/ | rm -r dir/ # delete a directory and its contents | ||
rm -f file | rm -f file # never prompt, ignore missing files | ||
rm -i file | rm -i file # prompt before each delete | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 88: | Line 76: | ||
cat file1 file2 # concatenate, in order | cat file1 file2 # concatenate, in order | ||
cat -n file.txt # with line numbers | cat -n file.txt # with line numbers | ||
less file.txt # scrollable pager: /search, g/G jump, q quit | less file.txt # scrollable pager: /search, g/G jump,f/b move forward/backward one page, q quit | ||
head -n 5 file.txt # show first 5 lines of a file. If no -n parsed, default is 10 | |||
tail -n 50 app.log # show last 50 lines of a file. If no -n parsed, default is 10 | |||
head -n 5 file.txt # first 5 lines | tail -f app.log # follow a growing log, Ctrl+C to stop | ||
tail -n 50 app.log # last 50 lines | |||
tail -f app.log # follow a growing log, Ctrl | |||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 100: | Line 85: | ||
'''grep''' — search inside files: | '''grep''' — search inside files: | ||
<syntaxhighlight lang="bash" copy> | |||
grep [options] pattern file # print matching lines | |||
</syntaxhighlight> | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Option !! Effect | ||
|- | |- | ||
| <syntaxhighlight lang="bash" inline> | | <syntaxhighlight lang="bash" inline>-i</syntaxhighlight> || case-insensitive | ||
|- | |- | ||
| <syntaxhighlight lang="bash" inline> | | <syntaxhighlight lang="bash" inline>-n</syntaxhighlight> || include line numbers | ||
|- | |- | ||
| <syntaxhighlight lang="bash" inline> | | <syntaxhighlight lang="bash" inline>-v</syntaxhighlight> || invert: lines that do NOT match | ||
|- | |- | ||
| <syntaxhighlight lang="bash" inline> | | <syntaxhighlight lang="bash" inline>-r</syntaxhighlight> || search a directory recursively | ||
|- | |- | ||
| <syntaxhighlight lang="bash" inline> | | <syntaxhighlight lang="bash" inline>-l</syntaxhighlight> || only filenames with matches | ||
|- | |- | ||
| <syntaxhighlight lang="bash" inline> | | <syntaxhighlight lang="bash" inline>-c</syntaxhighlight> || count matching lines | ||
|- | |- | ||
| <syntaxhighlight lang="bash" inline> | | <syntaxhighlight lang="bash" inline>-E</syntaxhighlight> || extended regex, e.g. <syntaxhighlight lang="bash" inline>grep -E 'a|b' file</syntaxhighlight> | ||
|} | |} | ||
| Line 124: | Line 111: | ||
<syntaxhighlight lang="bash" copy> | <syntaxhighlight lang="bash" copy> | ||
find . -name "*.py" | find . -name "*.py" # by name (glob) | ||
find /home -type f | find /home -type f # only files | ||
find /home -type d | find /home -type d # only directories | ||
find . -mtime -7 | find . -mtime -7 # modified in the last 7 days | ||
find . -size +100M | find . -size +100M # larger than 100 MB | ||
find . -name "*.log" -delete | find . -name "*.log" -delete # delete matches | ||
find . -name "*.tmp" -exec | find . -name "*.tmp" -exec <some-commands> {} \; # run a command on each match | ||
</syntaxhighlight> | </syntaxhighlight> | ||
'''locate''' — | '''locate''' — faster index-based search: | ||
<syntaxhighlight lang="bash" copy> | <syntaxhighlight lang="bash" copy> | ||
locate file.txt # instant filename lookup | locate file.txt # instant filename lookup | ||
updatedb # refresh the index (root) | locate folder/subfolder # instant partial path lookup | ||
sudo updatedb # refresh the index (requires root permission) | |||
</syntaxhighlight> | </syntaxhighlight> | ||
<blockquote> | <blockquote> | ||
'''<syntaxhighlight lang="bash" inline | '''<syntaxhighlight lang="bash" inline>locate</syntaxhighlight> searches an index refreshed by <syntaxhighlight lang="bash" inline>updatedb</syntaxhighlight>''' — files created since the last update are invisible to it. <syntaxhighlight lang="bash" inline>find</syntaxhighlight> is slower but always up to date. | ||
</blockquote> | </blockquote> | ||
== Permissions & ownership == | == Permissions & ownership == | ||
'''chmod''' — | '''chmod''' — change file/folder permissions. | ||
<syntaxhighlight lang="bash" copy> | <syntaxhighlight lang="bash" copy> | ||
| Line 160: | Line 148: | ||
file archive.tar # detect the file type | file archive.tar # detect the file type | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<blockquote> | |||
On GNU Linux, there are two notations for file permissions. | |||
* Numeric: three digits representing permissions for owner|owner's group|others | |||
** Each digit is an arithmetic sum of all accorded permissions: <code>4</code>:read, <code>2</code>:write, <code>1</code>:execute | |||
* Symbolic: mostly used to add/remove a single permission. | |||
** <code>r</code>:read, <code>w</code>:write, <code>x</code>:execute | |||
** <code>u</code>=owner, <code>g</code>=owner's group, <code>o</code>=others, <code>a</code>=all | |||
** <code>+</code>add, <code>-</code> to remove, <code>=</code> to set permission to exactly the given value. | |||
Below are some common file permissions: | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Numeric !! Symbolic | ||
|- | |- | ||
| 7 || rwx | | 7 || rwx | ||
| Line 172: | Line 171: | ||
| 4 || r-- | | 4 || r-- | ||
|} | |} | ||
</blockquote> | |||
<blockquote> | <blockquote> | ||
You can read permissions of a given directory and its content with <code>ls -l</code>. | |||
The command output will be something like this: | |||
<pre> | |||
drwxr-xr-x 2 rongzhou rongzhou 4096 Aŭg 20 16:19 9781474271325.sdr | |||
-rw-rw-r-- 1 rongzhou rongzhou 1758 Mar 10 15:26 antaurigardo.html | |||
drwxrwxr-x 4 rongzhou rongzhou 4096 Mar 13 14:28 Arduino | |||
drwxr-xr-x 4 rongzhou rongzhou 4096 Jul 22 12:20 Bildujo | |||
</pre> | |||
The first column is permission. | |||
* There are always 10 chars. | |||
* The first char is always <code>d</code> for directories and <code>-</code> for files. | |||
* char 2-4 are permissions for owner in symbolic notation | |||
* char 5-7 are permissions for owner's group | |||
* char 8-10 are permissions for others | |||
</blockquote> | </blockquote> | ||
| Line 184: | Line 198: | ||
du -h --max-depth=1 dir/ # per subdirectory | du -h --max-depth=1 dir/ # per subdirectory | ||
df -h # free space per mounted filesystem | df -h # free space per mounted filesystem | ||
df -h . # the filesystem holding the current dir | df -h . # ... only for the filesystem holding the current dir | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 196: | Line 210: | ||
<blockquote> | <blockquote> | ||
Hard links cannot span filesystems or point at directories. <syntaxhighlight lang="bash" inline>ls -l</syntaxhighlight> shows a symlink's target with <syntaxhighlight lang="text" inline>-></syntaxhighlight>. | |||
</blockquote> | </blockquote> | ||
== Archives == | == Archives == | ||
'''tar''' | '''tar''' | ||
<syntaxhighlight lang="bash" copy> | <syntaxhighlight lang="bash" copy> | ||
| Line 210: | Line 224: | ||
tar -cjf archive.tar.bz2 dir/ # bzip2 instead of gzip | tar -cjf archive.tar.bz2 dir/ # bzip2 instead of gzip | ||
</syntaxhighlight> | </syntaxhighlight> | ||
{| class="wikitable" | |||
! Option !! Effect | |||
|- | |||
| <syntaxhighlight lang="bash" inline>-c</syntaxhighlight> || create an archive | |||
|- | |||
| <syntaxhighlight lang="bash" inline>-x</syntaxhighlight> || extract | |||
|- | |||
| <syntaxhighlight lang="bash" inline>-t</syntaxhighlight> || list contents | |||
|- | |||
| <syntaxhighlight lang="bash" inline>-f</syntaxhighlight> || the archive file name — always followed by the archive | |||
|- | |||
| <syntaxhighlight lang="bash" inline>-z</syntaxhighlight> || gzip compression → <syntaxhighlight lang="text" inline>.tar.gz</syntaxhighlight> | |||
|- | |||
| <syntaxhighlight lang="bash" inline>-j</syntaxhighlight> || bzip2 compression → <syntaxhighlight lang="text" inline>.tar.bz2</syntaxhighlight> | |||
|- | |||
| <syntaxhighlight lang="bash" inline>-v</syntaxhighlight> || verbose: list each file as it is processed | |||
|- | |||
| <syntaxhighlight lang="bash" inline>-C</syntaxhighlight> || change to a directory before extracting | |||
|} | |||
'''zip / unzip:''' | '''zip / unzip:''' | ||
| Line 243: | Line 277: | ||
'''<syntaxhighlight lang="bash" inline>></syntaxhighlight> overwrites, <syntaxhighlight lang="bash" inline>>></syntaxhighlight> appends''' — one character decides whether an existing file survives. | '''<syntaxhighlight lang="bash" inline>></syntaxhighlight> overwrites, <syntaxhighlight lang="bash" inline>>></syntaxhighlight> appends''' — one character decides whether an existing file survives. | ||
</blockquote> | </blockquote> | ||
'''rg''' — ripgrep: a very fast recursive text search, the modern alternative to <syntaxhighlight lang="bash" inline>grep -r</syntaxhighlight> when you don't know the exact file (install: <syntaxhighlight lang="text" inline>apt install ripgrep</syntaxhighlight>, <syntaxhighlight lang="text" inline>brew install ripgrep</syntaxhighlight>): | |||
<syntaxhighlight lang="bash" copy> | |||
rg pattern [path] # recursive search; path defaults to the current directory | |||
rg pattern file.txt # search a single file | |||
rg -i 'error|warning' src/ # -i: case-insensitive | |||
rg -n 'TODO' app/ # -n: include line numbers (on by default on a terminal) | |||
rg -v 'DEBUG' app/ # -v: lines that do NOT match | |||
rg -w 'foo' app/ # -w: whole-word matches only | |||
rg -F 'a+b' app/ # -F: literal string, no regex metacharacters | |||
rg -l 'TODO' app/ # -l: only filenames with matches | |||
rg -c 'error' app/ # -c: count matching lines per file | |||
rg -C 3 'panic' app/ # -C N: N lines of context around each match | |||
rg -t py 'import' app/ # -t TYPE: only files of a type (py, js, rs, ...) | |||
rg -g '*.log' 'error' . # -g GLOB: only files matching the glob | |||
rg -g '!*.min.js' 'var' . # -g '!glob': exclude files matching the glob | |||
rg --hidden 'secret' . # --hidden: also search hidden dotfiles | |||
rg --no-ignore 'tmp' . # --no-ignore: ignore .gitignore / .ignore rules | |||
rg -uu 'pattern' . # -uu: also hidden files; -uuu also binary (each -u disables more filtering) | |||
</syntaxhighlight> | |||
<blockquote> | |||
'''rg only prints — it never edits files.''' By default it skips hidden files, binary files and anything matched by <syntaxhighlight lang="text" inline>.gitignore</syntaxhighlight>; add <syntaxhighlight lang="bash" inline>-uu</syntaxhighlight> when results look incomplete. <syntaxhighlight lang="bash" inline>-r</syntaxhighlight> / <syntaxhighlight lang="bash" inline>--replace</syntaxhighlight> rewrites the matched text in the output only — use it to preview what a replacement would look like, then apply the real edit with sd below. | |||
</syntaxhighlight> | |||
</blockquote> | |||
'''sd''' — search & displace: an intuitive regex find-and-replace, a friendlier <syntaxhighlight lang="bash" inline>sed</syntaxhighlight> (install: <syntaxhighlight lang="text" inline>cargo install sd</syntaxhighlight>, <syntaxhighlight lang="text" inline>brew install sd</syntaxhighlight>): | |||
<syntaxhighlight lang="bash" copy> | |||
sd old_text new_text file.txt # replace every occurrence of old_text, in place | |||
sd old_text new_text file1.txt file2.txt # edit several files at once | |||
sd old_text new_text **/*.md # glob for all .md files in the directory and its subdirectories [1] | |||
sd 'foo\s+bar' 'baz' file.txt # regex find & replace (JS/Python-style syntax) | |||
sd '(\w+) (\w+)' '$2 $1' file.txt # regex capture groups: the example swap two words | |||
sd -F 'a+b' 'axb' file.txt # -F: literal text, '+' is not regex "one or more" | |||
cat file.txt | sd -A '\n' ',' # -A: pattern may match across line breaks (join lines) | |||
sd -p old_text new_text file.txt # -p: preview the result, do not write | |||
echo 'some_text' | sd old_text new_text # no file given: read stdin, write stdout | |||
sd old_text -- '-new_text' file.txt # --: declare end of flags so new_text may start with a dash | |||
</syntaxhighlight> | |||
<blockquote> | |||
'''sd replaces every occurrence and edits files in place, which is non-reversible.'''Dry-run with <syntaxhighlight lang="bash" inline>-p</syntaxhighlight> and keep files under version control. | |||
</blockquote> | |||
<blockquote> | |||
[1]: You must enable globstar for <code>**</code> to be interpreted correctly. | |||
Add to <code><nowiki>~/.bashrc</nowiki></code>: | |||
<syntaxhighlight lang="bash" copy> | |||
shopt -s globstar | |||
</syntaxhighlight> | |||
</blockquote> | |||
== File format conversion == | |||
See [[Cheatsheets:Pandoc]] | |||
== Further reading == | == Further reading == | ||
Latest revision as of 12:14, 2 September 2026
Quick reference for smart people — part of our Linux cheatsheets collection.
The everyday Linux command-line file operations: navigate, create, copy, move, inspect, search, and archive files.
New to the command line? Every command here has a built-in manual — try man ls or tldr for short examples; explainshell.com annotates any command line.
Navigation & listing
pwd # print the current directory
cd dir # enter dir
cd .. # go up one level
cd ~ # home directory
cd - # previous directory
ls — the workhorse:
ls [options] [dir] # list directory contents
ls *.txt # glob: only matching names
| Option | Effect |
|---|---|
-l |
long format: permissions, owner, size, date |
-a |
hidden files (dotfiles) |
-h |
human-readable sizes (combine with -l) |
-R |
recurse into subdirectories |
-t |
sort by modification time, newest first |
Creating & removing
mkdir newdir # create a directory
mkdir -p dir/subdir/newsubsubdir # create parents as needed
mkdir -m 755 dir # set permissions at creation
rmdir emptydir # remove an EMPTY directory
rmdir -p dir/subdir/emptysubdir # ... and its empty parents
touch file.txt # create an empty file (or refresh its mtime)
touch -t 202501011200 f # set a specific timestamp
rm file.txt # delete a file
rm -r dir/ # delete a directory and its contents
rm -f file # never prompt, ignore missing files
rm -i file # prompt before each delete
rm -rfis irreversible — there is no trash bin. Double-check the path (and your current directory withpwd) before deleting recursively.
Copying & moving
cp file.txt backup.txt # copy a file
cp -r src/ dest/ # copy a directory recursively
cp -a src/ dest/ # recursive + preserve attributes
cp -i file.txt dest/ # prompt before overwriting
cp file1 file2 dest/ # several files into a directory
mv file.txt newname.txt # rename
mv file.txt dir/ # move into a directory
mv -i file.txt dir/ # prompt before overwriting
mv src/ dest/ # move/rename a directory
Viewing files
cat file.txt # print the whole file
cat file1 file2 # concatenate, in order
cat -n file.txt # with line numbers
less file.txt # scrollable pager: /search, g/G jump,f/b move forward/backward one page, q quit
head -n 5 file.txt # show first 5 lines of a file. If no -n parsed, default is 10
tail -n 50 app.log # show last 50 lines of a file. If no -n parsed, default is 10
tail -f app.log # follow a growing log, Ctrl+C to stop
Searching
grep — search inside files:
grep [options] pattern file # print matching lines
| Option | Effect |
|---|---|
-i |
case-insensitive |
-n |
include line numbers |
-v |
invert: lines that do NOT match |
-r |
search a directory recursively |
-l |
only filenames with matches |
-c |
count matching lines |
-E |
extended regex, e.g. grep -E 'a|b' file
|
find — search by name, type, age, size:
find . -name "*.py" # by name (glob)
find /home -type f # only files
find /home -type d # only directories
find . -mtime -7 # modified in the last 7 days
find . -size +100M # larger than 100 MB
find . -name "*.log" -delete # delete matches
find . -name "*.tmp" -exec <some-commands> {} \; # run a command on each match
locate — faster index-based search:
locate file.txt # instant filename lookup
locate folder/subfolder # instant partial path lookup
sudo updatedb # refresh the index (requires root permission)
locatesearches an index refreshed byupdatedb— files created since the last update are invisible to it.findis slower but always up to date.
Permissions & ownership
chmod — change file/folder permissions.
chmod 755 script.sh # rwxr-xr-x
chmod 644 file.txt # rw-r--r--
chmod u+x script.sh # add execute for the owner
chmod -R 755 dir/ # recursive
chown alice file.txt # change owner
chown alice:dev file.txt # owner and group
chown -R alice:dev dir/
chgrp dev file.txt # change group only
stat file.txt # perms, size, timestamps, inode
file archive.tar # detect the file type
On GNU Linux, there are two notations for file permissions.
- Numeric: three digits representing permissions for owner|owner's group|others
- Each digit is an arithmetic sum of all accorded permissions:
4:read,2:write,1:execute- Symbolic: mostly used to add/remove a single permission.
r:read,w:write,x:executeu=owner,g=owner's group,o=others,a=all+add,-to remove,=to set permission to exactly the given value.Below are some common file permissions:
Numeric Symbolic 7 rwx 6 rw- 5 r-x 4 r--
You can read permissions of a given directory and its content with
ls -l.The command output will be something like this:
drwxr-xr-x 2 rongzhou rongzhou 4096 Aŭg 20 16:19 9781474271325.sdr -rw-rw-r-- 1 rongzhou rongzhou 1758 Mar 10 15:26 antaurigardo.html drwxrwxr-x 4 rongzhou rongzhou 4096 Mar 13 14:28 Arduino drwxr-xr-x 4 rongzhou rongzhou 4096 Jul 22 12:20 BildujoThe first column is permission.
- There are always 10 chars.
- The first char is always
dfor directories and-for files.- char 2-4 are permissions for owner in symbolic notation
- char 5-7 are permissions for owner's group
- char 8-10 are permissions for others
Disk usage
du -sh dir/ # total size of a directory, human-readable
du -sh * # size of each item
du -h --max-depth=1 dir/ # per subdirectory
df -h # free space per mounted filesystem
df -h . # ... only for the filesystem holding the current dir
Links
ln target hardlink # hard link: same file, another name
ln -s target symlink # symbolic link: points to a path
ln -s /usr/local/bin/tool ~/bin/tool
Hard links cannot span filesystems or point at directories.
ls -lshows a symlink's target with->.
Archives
tar
tar -czf archive.tar.gz dir/ # create a gzipped archive
tar -xzf archive.tar.gz # extract (compression auto-detected)
tar -tzf archive.tar.gz # list contents
tar -xzf archive.tar.gz -C dest/ # extract into a directory
tar -cjf archive.tar.bz2 dir/ # bzip2 instead of gzip
| Option | Effect |
|---|---|
-c |
create an archive |
-x |
extract |
-t |
list contents |
-f |
the archive file name — always followed by the archive |
-z |
gzip compression → .tar.gz
|
-j |
bzip2 compression → .tar.bz2
|
-v |
verbose: list each file as it is processed |
-C |
change to a directory before extracting |
zip / unzip:
zip -r archive.zip dir/ # zip a directory recursively
zip archive.zip a.txt b.txt # zip specific files
unzip archive.zip # extract
unzip -l archive.zip # list contents
unzip archive.zip -d dest/ # extract into a directory
unzip -o archive.zip # overwrite without asking
Text utilities
sort file.txt # sort lines alphabetically
sort -n file.txt # numeric sort
sort -r file.txt # reverse
sort -u file.txt # unique lines
sort -k2 file.txt # sort by the 2nd field
diff file1 file2 # line-by-line differences
diff -u file1 file2 # unified diff (patch format)
diff -r dir1 dir2 # compare directories
cmp file1 file2 # byte compare: report first difference
echo "hello" # print text
echo -n "no newline" # omit the trailing newline
echo "hi" > file.txt # overwrite a file with output
echo "hi" >> file.txt # append to a file
>overwrites,>>appends — one character decides whether an existing file survives.
rg — ripgrep: a very fast recursive text search, the modern alternative to grep -r when you don't know the exact file (install: apt install ripgrep, brew install ripgrep):
rg pattern [path] # recursive search; path defaults to the current directory
rg pattern file.txt # search a single file
rg -i 'error|warning' src/ # -i: case-insensitive
rg -n 'TODO' app/ # -n: include line numbers (on by default on a terminal)
rg -v 'DEBUG' app/ # -v: lines that do NOT match
rg -w 'foo' app/ # -w: whole-word matches only
rg -F 'a+b' app/ # -F: literal string, no regex metacharacters
rg -l 'TODO' app/ # -l: only filenames with matches
rg -c 'error' app/ # -c: count matching lines per file
rg -C 3 'panic' app/ # -C N: N lines of context around each match
rg -t py 'import' app/ # -t TYPE: only files of a type (py, js, rs, ...)
rg -g '*.log' 'error' . # -g GLOB: only files matching the glob
rg -g '!*.min.js' 'var' . # -g '!glob': exclude files matching the glob
rg --hidden 'secret' . # --hidden: also search hidden dotfiles
rg --no-ignore 'tmp' . # --no-ignore: ignore .gitignore / .ignore rules
rg -uu 'pattern' . # -uu: also hidden files; -uuu also binary (each -u disables more filtering)
rg only prints — it never edits files. By default it skips hidden files, binary files and anything matched by
.gitignore; add-uuwhen results look incomplete.-r/--replacerewrites the matched text in the output only — use it to preview what a replacement would look like, then apply the real edit with sd below. </syntaxhighlight>
sd — search & displace: an intuitive regex find-and-replace, a friendlier sed (install: cargo install sd, brew install sd):
sd old_text new_text file.txt # replace every occurrence of old_text, in place
sd old_text new_text file1.txt file2.txt # edit several files at once
sd old_text new_text **/*.md # glob for all .md files in the directory and its subdirectories [1]
sd 'foo\s+bar' 'baz' file.txt # regex find & replace (JS/Python-style syntax)
sd '(\w+) (\w+)' '$2 $1' file.txt # regex capture groups: the example swap two words
sd -F 'a+b' 'axb' file.txt # -F: literal text, '+' is not regex "one or more"
cat file.txt | sd -A '\n' ',' # -A: pattern may match across line breaks (join lines)
sd -p old_text new_text file.txt # -p: preview the result, do not write
echo 'some_text' | sd old_text new_text # no file given: read stdin, write stdout
sd old_text -- '-new_text' file.txt # --: declare end of flags so new_text may start with a dash
sd replaces every occurrence and edits files in place, which is non-reversible.Dry-run with
-pand keep files under version control.
[1]: You must enable globstar for
**to be interpreted correctly.Add to
~/.bashrc:shopt -s globstar
File format conversion
Further reading
man <command>— the built-in manual, always installed- GNU Coreutils manual — the official docs for most of these commands
- tldr — community-maintained short examples
- explainshell.com — annotate any command line
- Linux Journey — beginner-friendly tutorial