Boost Your Command-Line Skills
$
$ ls
$ paste <(echo {1..10}.jpg | sed 's/ /\n/g') \
<(echo {0..9}.jpg | sed 's/ /\n/g') \
| sed 's/^/mv /' \
| bash
Indicates new terms, URLs, email addresses, filenames, and file extensions.
Constant widthUsed for program listings, as well as within paragraphs to refer to program elements such as variable or function names, databases, data types, environment variables, statements, and keywords.
Constant width boldShows commands or other text that should be typed literally by the user. Also used occasionally in command output to highlight text of interest.
Constant width italicShows text that should be replaced with user-supplied values or by values determined by context. Also used for brief notes on the right side of code listings.
Constant width highlightedUsed in complex program listings to call attention to specific text.
1 This book displays the Linux prompt as a dollar sign. Your prompt may be different.
2 You’ll learn this mystery command’s purpose in Chapter 8.
3 The version of bash on macOS is ancient and missing important features. To upgrade bash, see the article “Upgrading Bash on macOS” by Daniel Weibel.
The stream of input that Linux reads from your keyboard. When you type any command at a prompt, you’re supplying data on stdin.
The stream of output that Linux writes to your display. When you run the
ls command to print filenames, the results appear on stdout.
$ ls -l /bin total 12104 -rwxr-xr-x 1 root root 1113504 Jun 6 2019 bash -rwxr-xr-x 1 root root 170456 Sep 21 2019 bsd-csh -rwxr-xr-x 1 root root 34888 Jul 4 2019 bunzip2 -rwxr-xr-x 1 root root 2062296 Sep 18 2020 busybox -rwxr-xr-x 1 root root 34888 Jul 4 2019 bzcat ⋮ -rwxr-xr-x 1 root root 5047 Apr 27 2017 znew
$ less myfile
$ ls -l /bin | less
$ man wc
python Programming Python 2010 Lutz, Mark snail SSH, The Secure Shell 2005 Barrett, Daniel alpaca Intermediate Perl 2012 Schwartz, Randal robin MySQL High Availability 2014 Bell, Charles horse Linux in a Nutshell 2009 Siever, Ellen donkey Cisco IOS in a Nutshell 2005 Boney, James oryx Writing Word Macros 1999 Roman, Steven
$ wc animals.txt 7 51 325 animals.txt
$ wc -l animals.txt 7 animals.txt $ wc -w animals.txt 51 animals.txt $ wc -c animals.txt 325 animals.txt
$ ls -1 animals.txt myfile myfile2 test.py $ ls -1 | wc -l 4
$ wc animals.txt 7 51 325 animals.txt $ wc animals.txt | wc -w 4
$ wc animals.txt | wc -w | wc
1 1 2
$ head -n3 animals.txt python Programming Python 2010 Lutz, Mark snail SSH, The Secure Shell 2005 Barrett, Daniel alpaca Intermediate Perl 2012 Schwartz, Randal
$ head -n3 animals.txt | wc -w 20
$ ls /bin | head -n5 bash bsd-csh bunzip2 busybox bzcat
$ cut -f2 animals.txt Programming Python SSH, The Secure Shell Intermediate Perl MySQL High Availability Linux in a Nutshell Cisco IOS in a Nutshell Writing Word Macros
$ cut -f2 animals.txt | head -n3 Programming Python SSH, The Secure Shell Intermediate Perl
$ cut -f1,3 animals.txt | head -n3 python 2010 snail 2005 alpaca 2012
$ cut -f2-4 animals.txt | head -n3 Programming Python 2010 Lutz, Mark SSH, The Secure Shell 2005 Barrett, Daniel Intermediate Perl 2012 Schwartz, Randal
$ cut -c1-3 animals.txt pyt sna alp rob hor don ory
$ cut -f4 animals.txt Lutz, Mark Barrett, Daniel Schwartz, Randal ⋮
$ cut -f4 animals.txt | cut -d, -f1 Lutz Barrett Schwartz ⋮
$ grep Nutshell animals.txt horse Linux in a Nutshell 2009 Siever, Ellen donkey Cisco IOS in a Nutshell 2005 Boney, James
$ grep -v Nutshell animals.txt python Programming Python 2010 Lutz, Mark snail SSH, The Secure Shell 2005 Barrett, Daniel alpaca Intermediate Perl 2012 Schwartz, Randal robin MySQL High Availability 2014 Bell, Charles oryx Writing Word Macros 1999 Roman, Steven
$ grep Perl *.txt animals.txt:alpaca Intermediate Perl 2012 Schwartz, Randal essay.txt:really love the Perl programming language, which is essay.txt:languages such as Perl, Python, PHP, and Ruby
$ ls -l /usr/lib drwxrwxr-x 12 root root 4096 Mar 1 2020 4kstogram drwxr-xr-x 3 root root 4096 Nov 30 2020 GraphicsMagick-1.4 drwxr-xr-x 4 root root 4096 Mar 19 2020 NetworkManager -rw-r--r-- 1 root root 35568 Dec 1 2017 attica_kde.so -rwxr-xr-x 1 root root 684 May 5 2018 cnf-update-db ⋮
$ ls -l /usr/lib | cut -c1 d d d - - ⋮
$ ls -l /usr/lib | cut -c1 | grep d d d d ⋮
$ ls -l /usr/lib | cut -c1 | grep d | wc -l 145
$ sort animals.txt alpaca Intermediate Perl 2012 Schwartz, Randal donkey Cisco IOS in a Nutshell 2005 Boney, James horse Linux in a Nutshell 2009 Siever, Ellen oryx Writing Word Macros 1999 Roman, Steven python Programming Python 2010 Lutz, Mark robin MySQL High Availability 2014 Bell, Charles snail SSH, The Secure Shell 2005 Barrett, Daniel
$ sort -r animals.txt snail SSH, The Secure Shell 2005 Barrett, Daniel robin MySQL High Availability 2014 Bell, Charles python Programming Python 2010 Lutz, Mark oryx Writing Word Macros 1999 Roman, Steven horse Linux in a Nutshell 2009 Siever, Ellen donkey Cisco IOS in a Nutshell 2005 Boney, James alpaca Intermediate Perl 2012 Schwartz, Randal
$ cut -f3 animals.txt Unsorted 2010 2005 2012 2014 2009 2005 1999 $ cut -f3 animals.txt | sort -n Ascending 1999 2005 2005 2009 2010 2012 2014 $ cut -f3 animals.txt | sort -nr Descending 2014 2012 2010 2009 2005 2005 1999
$ cut -f3 animals.txt | sort -nr | head -n1 2014
... | sort -nr | head -n1
... | sort -n | head -n1
$ head -n5 /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin smith:x:1000:1000:Aisha Smith,,,:/home/smith:/bin/bash jones:x:1001:1001:Bilbo Jones,,,:/home/jones:/bin/bash
$ head -n5 /etc/passwd | cut -d: -f1 root daemon bin smith jones
$ head -n5 /etc/passwd | cut -d: -f1 | sort bin daemon jones root smith
$ cat /etc/passwd | cut -d: -f1 | sort
$ cut -d: -f1 /etc/passwd | grep -w jones jones $ cut -d: -f1 /etc/passwd | grep -w rutabaga (produces no output)
$ cat letters A A A B B A C C C C $ uniq letters A B A C
$ uniq -c letters
3 A
2 B
1 A
4 C
$ cat grades C Geraldine B Carmine A Kayla A Sophia B Haresh C Liam B Elijah B Emma A Olivia D Noah F Ava
$ cut -f1 grades | sort A A A B B B B C C D F
$ cut -f1 grades | sort | uniq -c
3 A
4 B
2 C
1 D
1 F
$ cut -f1 grades | sort | uniq -c | sort -nr
4 B
3 A
2 C
1 F
1 D
$ cut -f1 grades | sort | uniq -c | sort -nr | head -n1
4 B
$ cut -f1 grades | sort | uniq -c | sort -nr | head -n1 | cut -c9 B
$ ls image001.jpg image005.jpg image009.jpg image013.jpg image017.jpg image002.jpg image006.jpg image010.jpg image014.jpg image018.jpg ⋮
$ md5sum image001.jpg 146b163929b6533f02e91bdf21cb9563 image001.jpg
$ md5sum image001.jpg image002.jpg image003.jpg 146b163929b6533f02e91bdf21cb9563 image001.jpg 63da88b3ddde0843c94269638dfa6958 image002.jpg 146b163929b6533f02e91bdf21cb9563 image003.jpg
$ md5sum *.jpg | cut -c1-32 | sort 1258012d57050ef6005739d0e6f6a257 146b163929b6533f02e91bdf21cb9563 146b163929b6533f02e91bdf21cb9563 17f339ed03733f402f74cf386209aeb3 ⋮
$ md5sum *.jpg | cut -c1-32 | sort | uniq -c
1 1258012d57050ef6005739d0e6f6a257
2 146b163929b6533f02e91bdf21cb9563
1 17f339ed03733f402f74cf386209aeb3
⋮
$ md5sum *.jpg | cut -c1-32 | sort | uniq -c | sort -nr
3 f6464ed766daca87ba407aede21c8fcc
2 c7978522c58425f6af3f095ef1de1cd5
2 146b163929b6533f02e91bdf21cb9563
1 d8ad913044a51408ec1ed8a204ea9502
⋮
$ md5sum *.jpg | cut -c1-32 | sort | uniq -c | sort -nr | grep -v " 1 "
3 f6464ed766daca87ba407aede21c8fcc
2 c7978522c58425f6af3f095ef1de1cd5
2 146b163929b6533f02e91bdf21cb9563
$ md5sum *.jpg | grep 146b163929b6533f02e91bdf21cb9563 146b163929b6533f02e91bdf21cb9563 image001.jpg 146b163929b6533f02e91bdf21cb9563 image003.jpg
$ md5sum *.jpg | grep 146b163929b6533f02e91bdf21cb9563 | cut -c35- image001.jpg image003.jpg
1 On US keyboards, the pipe symbol is on the same key as the backslash (\), usually located between the Enter and Backspace keys or between the left Shift key and Z.
2 The POSIX standard calls this form of command a utility.
3 Depending on your setup, ls may also use other formatting features, such as color, when printing to the screen but not when redirected.
4 Some Linux systems store the user information elsewhere.
5 Technically, you don’t need the final sort -nr in this pipeline to isolate duplicates because grep removes all the nonduplicates.
6 Some commands do not use stdin/stdout and therefore cannot read from pipes or write to pipes. Examples are mv and rm. Pipelines may incorporate these commands in other ways, however; you’ll see examples in Chapter 8.
$ ls *.py data.py main.py user_interface.py
$ ls data.py main.py user_interface.py
$ grep Linux chapter1 chapter2 chapter3 chapter4 chapter5 ...and so on...
$ grep Linux chapter*
$ grep Linux chapter?
$ grep Linux chapter??
$ grep Linux chapter[12345]
$ grep Linux chapter[1-5]
$ grep Linux chapter*[02468]
$ ls [A-Z]*_*@
$ ls -1 /etc/*.conf /etc/adduser.conf /etc/appstream.conf ⋮ /etc/wodim.conf
$ ls Pictures Poems Politics $ cd P* Three directories will match bash: cd: too many arguments
$ ls *.doc /bin/ls: cannot access '*.doc': No such file or directory
$ english2swedish *.txt
$ printenv HOME /home/smith $ printenv USER smith
$ echo My name is $USER and my files are in $HOME Evaluating variables My name is smith and my files are in /home/smith $ echo ch*ter9 Evaluating a pattern chapter9
name=value
$ work=$HOME/Projects
$ cd $work $ pwd /home/smith/Projects
$ cp myfile $work $ ls $work myfile
$ work = $HOME/Projects The shell assumes "work" is a command work: command not found
$ echo $HOME /home/smith
$ echo /home/smith
$ ls mammals reptiles $ ls mammals lizard.txt snake.txt
mvmammals/*.txtreptilesMethod1FILES="lizard.txt snake.txt"mvmammals/$FILESreptilesMethod2
$ echo mammals/*.txt mammals/lizard.txt mammals/snake.txt
$ mv mammals/lizard.txt mammals/snake.txt reptiles
$ echo mammals/$FILES mammals/lizard.txt snake.txt
$ mv mammals/lizard.txt snake.txt reptiles
$ mv mammals/$FILES reptiles /bin/mv: cannot stat 'snake.txt': No such file or directory
FILES="lizard.txt snake.txt"forf in$FILES;domv mammals/$freptilesdone
$ alias g=grep A command with no arguments $ alias ll="ls -l" A command with arguments: quotes are required
$ ll Runs "ls -l" -rw-r--r-- 1 smith smith 325 Jul 3 17:44 animals.txt $ g Nutshell animals.txt Runs "grep Nutshell animals.txt" horse Linux in a Nutshell 2009 Siever, Ellen donkey Cisco IOS in a Nutshell 2005 Boney, James
$ alias less="less -c"
$ alias alias g='grep' alias ll='ls -l'
$ alias g alias g='grep'
$ unalias g
$ grep Perl animals.txt alpaca Intermediate Perl 2012 Schwartz, Randal
$ grep Perl animals.txt > outfile (displays no output) $ cat outfile alpaca Intermediate Perl 2012 Schwartz, Randal
$ grep Perl animals.txt > outfile Create or overwrite outfile $ echo There was just one match >> outfile Append to outfile $ cat outfile alpaca Intermediate Perl 2012 Schwartz, Randal There was just one match
$ wc animals.txt Reading from a named file 7 51 325 animals.txt $ wc < animals.txt Reading from redirected stdin 7 51 325
$ wc < animals.txt > count $ cat count 7 51 325
$ grep Perl < animals.txt | wc > count
$ cat count
1 6 47
$ ls file1 file2 file3
$ ls -l -rw-r--r-- 1 smith smith 36 Aug 9 22:12 Efficient Linux Tips.txt
$ cat Efficient Linux Tips.txt cat: Efficient: No such file or directory cat: Linux: No such file or directory cat: Tips.txt: No such file or directory
$ cat 'Efficient Linux Tips.txt' $ cat "Efficient Linux Tips.txt" $ cat Efficient\ Linux\ Tips.txt
$ echo '$HOME' $HOME
$ echo "Notice that $HOME is evaluated" Double quotes Notice that /home/smith is evaluated $ echo 'Notice that $HOME is not' Single quotes Notice that $HOME is not
$ echo \$HOME $HOME
$ echo "The value of \$HOME is $HOME" The value of $HOME is /home/smith
$ echo 'The value of \$HOME is $HOME' The value of \$HOME is $HOME
$ echo "This message is \"sort of\" interesting" This message is "sort of" interesting
$ echo "This is a very long message that needs to extend \ onto multiple lines" This is a very long message that needs to extend onto multiple lines
$ cut -f1 grades \ | sort \ | uniq -c \ | sort -nr \ | head -n1 \ | cut -c9
$ alias less="less -c" Define an alias $ less myfile Run the alias, which invokes less -c $ \less myfile Run the standard less command, not the alias
$ ls -l /bin/ls -rwxr-xr-x 1 root root 133792 Jan 18 2018 /bin/ls
$ ls ls ls
$ echo $PATH /home/smith/bin:/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib/java/bin
$ echo $PATH | tr : "\n" /home/smith/bin /usr/local/bin /usr/bin /bin /usr/games /usr/lib/java/bin
$ which cp /bin/cp $ which which /usr/bin/which
$ type cp cp is hashed (/bin/cp) $ type ll ll is aliased to ‘/bin/ls -l’ $ type type type is a shell builtin
$ ls $HOME apple banana carrot $ ls -a $HOME .bashrc apple banana carrot
# Set the search pathPATH=$HOME/bin:/usr/local/bin:/usr/bin:/bin# Set the shell promptPS1='$ '# Set your preferred text editorEDITOR=emacs# Start in my work directorycd$HOME/Work/Projects# Define an aliasaliasg=grep# Offer a hearty greetingecho"Welcome to Linux, friend!"
$ source $HOME/.bashrc Uses the builtin "source" command $ . $HOME/.bashrc Uses a dot
1 That’s why the command ls * doesn’t list filenames beginning with a dot, a.k.a. dot files.
2 bash prevents infinite recursion by not expanding the second less as an alias.
3 Some shells memorize (cache) the paths to programs as they’re located, cutting down on future searches.
4 Notice that the command type which produces output, but the command which type does not.
5 This statement is oversimplified; more details are in Table 6-1.
$ md5sum *.jpg | cut -c1-32 | sort | uniq -c | sort -nr
$ md5sum *.jg | cut -c1-32 | sort | uniq -c | sort -nr
$ history 1000 cd $HOME/Music 1001 ls 1002 mv jazz.mp3 jazzy-song.mp3 1003 play jazzy-song.mp3 ⋮ Omitting 479 lines 1481 cd 1482 firefox https://google.com 1483 history Includes the command you just ran
$ history 3 Print the 3 most recent commands 1482 firefox https://google.com 1483 history 1484 history 3
$ history | less Earliest to latest entry $ history | sort -nr | less Latest to earliest entry
$ history | grep -w cd 1000 cd $HOME/Music 1092 cd .. 1123 cd Finances 1375 cd Checking 1481 cd 1485 history | grep -w cd
$ history -c
Extremely simple to learn but often slow in practice
Harder to learn (frankly, it’s cryptic) but can be very fast
Both simple and fast
$ echo Efficient Linux Efficient Linux $ !! "Bang bang" = previous command echo Efficient Linux The shell helpfully prints the command being run Efficient Linux
$ !grep grep Perl animals.txt alpaca Intermediate Perl 2012 Schwartz, Randal
$ !?grep? history | grep -w cd 1000 cd $HOME/Music 1092 cd .. ⋮
$ history | grep hosts 1203 cat /etc/hosts $ !1203 The command at position 1023 cat /etc/hosts 127.0.0.1 localhost 127.0.1.1 example.oreilly.com ::1 example.oreilly.com
$ history 4197 cd /tmp/junk 4198 rm * 4199 head -n2 /etc/hosts 4199 cd 4200 history $ !-3 The command you executed three commands ago head -n2 /etc/hosts 127.0.0.1 localhost 127.0.1.1 example.oreilly.com
$ !-3:p head -n2 /etc/hosts Printed, not executed
$ !-3:p head -n2 /etc/hosts Printed, not executed, and appended to history $ !! Run the command for real head -n2 /etc/hosts Printed and then executed 127.0.0.1 localhost 127.0.1.1 example.oreilly.com
$ ls -l /etc | head -n3 Run any command total 1584 drwxr-xr-x 2 root root 4096 Jun 16 06:14 ImageMagick-6/ drwxr-xr-x 7 root root 4096 Mar 19 2020 NetworkManager/ $ echo "!!" | wc -w Count the words in the previous command echo "ls -l /etc | head -n3" | wc -w 6
$ ls Run any command hello.txt $ cd Music Run some other command $ !-2 Use history expansion ls song.mp3 $ history View the history 1000 ls 1001 cd Music 1002 ls "ls" appears in the history, not "!-2" 1003 history
$ ls 123 a.txt b.txt c.txt dont-delete-me important-file passwords $ rm * .txt DANGER!! Don't run this! Deletes the wrong files!
$ alias rm='rm -i' Often found in a shell configuration file $ rm *.txt /bin/rm: remove regular file 'a.txt'? y /bin/rm: remove regular file 'b.txt'? y /bin/rm: remove regular file 'c.txt'? y
$ rm * .txt /bin/rm: remove regular file '123'? Something is wrong: kill the command
$ ls *.txt a.txt b.txt c.txt
$ rm !$ rm *.txt
$ ls * .txt /bin/ls: cannot access '.txt': No such file or directory 123 a.txt b.txt c.txt dont-delete-me important-file passwords
$ head myfile.txt (first 10 lines of the file appear) $ rm !$ rm myfile.txt
$ ls *.txt *.o *.log a.txt b.txt c.txt main.o output.log parser.o $ rm !* rm *.txt *.o *.log
(reverse-i-search)`':
(reverse-i-search)`': c
(reverse-i-search)`': less /etc/hosts
(reverse-i-search)`': cd
(reverse-i-search)`': cd /usr/local
(reverse-i-search)`': cd $
(reverse-i-search)`': cd $HOME/Finances/Bank
(reverse-i-search)`': cd $HOME/Music
(reverse-i-search)`': cd $HOME/Linux/Books
(reverse-i-search)`': cd $HOME/Finances/Bank
Again, the slowest and least powerful method but simple to learn
A form of history expansion
To edit the command line in powerful ways
| Keystroke | Action |
|---|---|
$ md5sum *.jg | cut -c1-32 | sort | uniq -c | sort -nr md5sum: '*.jg': No such file or directory
$ ^jg^jpg
$ ^jg^jpg md5sum *.jpg | cut -c1-32 | sort | uniq -c | sort -nr ⋮
1 You can omit the trailing question mark here—!?grep—but in some cases it’s required, such as sed-style history expansion (see “More Powerful Substitution with History Expansion”).
2 I’m assuming that no matching files were added or removed behind your back after the ls step. Don’t rely on this technique in rapidly changing directories.
3 While writing this book, I frequently reran version-control commands such as git add, git commit, and git push. Incremental search made rerunning these commands a breeze.
$ cd /usr/share/lib/etc/bin No matter where you go... $ pwd /usr/share/lib/etc/bin ...there you are.
$ pwd /etc Start somewhere else $ cd Run cd with no arguments... $ pwd /home/smith ...and you're home again
$ cd $HOME/Work
$ cd ~/Work
$ echo $HOME ~ /home/smith /home/smith
$ echo ~jones /home/jones
$ cd /usr $ ls bin games include lib local sbin share src
$ cd sha<Tab>
$ cd share/
$ cd s<Tab>
$ cd s<Tab><Tab> sbin/ share/ src/
$ cd sh<Tab>
$ cd share/
# In a shell configuration file:aliaswork="cd$HOME/Work/Projects/Web/src/include"
$ work $ pwd /home/smith/Work/Projects/Web/src/include
$ work=$HOME/Work/Projects/Web/src/include $ cd $work $ pwd /home/smith/Work/Projects/Web/src/include $ ls $work/css Use the variable in other ways main.css mobile.css
# Place in a shell configuration file and source it:aliasrcedit='$EDITOR $HOME/.bashrc'
cd-ing to faraway directories# Define the qcd functionqcd(){# Accept 1 argument that's a string key, and perform a different# "cd" operation for each key.case"$1"in work)cd$HOME/Work/Projects/Web/src/include;;recipes)cd$HOME/Family/Cooking/Recipes;;video)cd/data/Arts/Video/Collection;;beatles)cd$HOME/Music/mp3/Artists/B/Beatles;;*)# The supplied argument was not one of the supported keysecho"qcd: unknown key '$1'"return1;;esac# Helpfully print the current directory name to indicate where you arepwd}# Set up tab completioncomplete-W"work recipes video beatles"qcd
$ qcd beatles /home/smith/Music/mp3/Artists/B/Beatles
$ qcd <Tab><Tab> beatles recipes video work $ qcd v<Tab><Enter> Completes 'v' to 'video' /data/Arts/Video/Collection
$ cd ~/Family/Memories/Photos
$ cd Photos
bash: cd: Photos: No such file or directory
$ pwd /etc $ cd Photos /home/smith/Family/Memories/Photos
$HOME:$HOME/Projects:$HOME/Family/Memories:/usr/local
$ cd Photos
$ CDPATH=/usr Set a CDPATH $ cd /tmp No output: CDPATH wasn't consulted $ cd bin cd consults CDPATH... /usr/bin ...and prints the new working directory
$ pwd /etc Begin outside your home directory $ cd Work /home/smith/Work $ cd Family/School You jumped 1 level below $HOME /home/smith/Family/School
$ pwd /etc Anywhere outside your home directory $ cd School /home/smith/Family/School You jumped 2 levels below $HOME
$ pwd /usr/bin Your current directory $ ls .. bin include lib src Your siblings $ cd lib /usr/lib You jumped to a sibling
$ pwd /usr/src/myproject $ ls docs include src
$ cd docs Change your current directory $ cd include /usr/src/myproject/include You jumped to a sibling $ cd src /usr/src/myproject/src Again
# Place in a shell configuration file and source it:exportCDPATH=$HOME:$HOME/Work:$HOME/Family:$HOME/Linux:$HOME/Music:..
$ cd $ ls -d */ && (ls -d */*/ | cut -d/ -f2-) | sort | uniq -c | sort -nr | less
$ pwd /home/smith/Finances/Bank/Checking/Statements $ cd /etc
$ cd - /home/smith/Finances/Bank/Checking/Statements
$ pwd /usr/local/bin $ cd /etc The shell remembers /usr/local/bin $ cd - The shell remembers /etc /usr/local/bin $ cd - The shell remembers /usr/local/bin /etc
$ cd The shell remembers /etc
$ cd - The shell remembers your home directory /etc $ cd - The shell remembers /etc /home/smith
$ cd ~/Work/Projects/Web/src $ cd /var/www/html $ cd /etc/apache2 $ cd ~/Work/Projects/Web/src $ cd /etc/ssl/certs
$ pwd /home/smith/Work/Projects/Web/src $ pushd /var/www/html /var/www/html ~/Work/Projects/Web/src $ pushd /etc/apache2 /etc/apache2 /var/www/html ~/Work/Projects/Web/src $ pushd /etc/ssl/certs /etc/ssl/certs /etc/apache2 /var/www/html ~/Work/Projects/Web/src $ pwd /etc/ssl/certs
$ dirs /etc/ssl/certs /etc/apache2 /var/www/html ~/Work/Projects/Web/src
$ dirs -p /etc/ssl/certs /etc/apache2 /var/www/html ~/Work/Projects/Web/src
$ dirs -p | nl -v0
0 /etc/ssl/certs
1 /etc/apache2
2 /var/www/html
3 ~/Work/Projects/Web/src
$ dirs -v 0 /etc/ssl/certs 1 /etc/apache2 2 /var/www/html 3 ~/Work/Projects/Web/src
# Place in a shell configuration file and source it:alias dirs='dirs -v'
$ dirs /etc/ssl/certs /etc/apache2 /var/www/html ~/Work/Projects/Web/src
$ popd /etc/apache2 /var/www/html ~/Work/Projects/Web/src $ popd /var/www/html ~/Work/Projects/Web/src $ popd ~/Work/Projects/Web/src $ popd bash: popd: directory stack empty $ pwd ~/Work/Projects/Web/src
# Place in a shell configuration file and source it:aliasgd=pushdaliaspd=popd
$ dirs /etc/apache2 ~/Work/Projects/Web/src /var/www/html $ pushd ~/Work/Projects/Web/src /etc/apache2 /var/www/html $ pushd /etc/apache2 ~/Work/Projects/Web/src /var/www/html $ pushd ~/Work/Projects/Web/src /etc/apache2 /var/www/html
$ dirs ~/Work/Projects/Web/src /var/www/html /etc/apache2 $ cd /etc/ssl/certs $ dirs /etc/ssl/certs /var/www/html /etc/apache2
$ pushd - ~/Work/Projects/Web/src /etc/ssl/certs /var/www/html /etc/apache2 $ pushd /etc/ssl/certs ~/Work/Projects/Web/src /var/www/html /etc/apache2
# Place in a shell configuration file and source it:aliasslurp='pushd - && pushd'
$ pushd +N
$ dirs /etc/ssl/certs ~/Work/Projects/Web/src /var/www/html /etc/apache2 $ pushd +1 ~/Work/Projects/Web/src /var/www/html /etc/apache2 /etc/ssl/certs $ pushd +2 /etc/apache2 /etc/ssl/certs ~/Work/Projects/Web/src /var/www/html
$ dirs -v 0 /etc/apache2 1 /etc/ssl/certs 2 ~/Work/Projects/Web/src 3 /var/www/html
$ dirs /etc/apache2 /etc/ssl/certs ~/Work/Projects/Web/src /var/www/html $ pushd -0 /var/www/html /etc/apache2 /etc/ssl/certs ~/Work/Projects/Web/src
$ popd +N
$ dirs /var/www/html /etc/apache2 /etc/ssl/certs ~/Work/Projects/Web/src $ popd +1 /var/www/html /etc/ssl/certs ~/Work/Projects/Web/src $ popd +2 /var/www/html /etc/ssl/certs
1 I made this up, but it’s surely true.
2 An alternative is to open multiple virtual displays using command-line programs like screen and tmux, which are called terminal multiplexers. They’re more effort to learn than directory stacks but worth a look.
3 If you know stacks from computer science, a directory stack is precisely a stack of directory names.
4 Programmers may recognize these operations as rotating the stack.
Printing dates, times, sequences of numbers and letters, file paths, repeated strings, and other text to jumpstart your pipelines.
Extracting any part of a text file with a combination of grep, cut,
head, tail, and one handy feature of awk.
Combining files from top to bottom with cat and tac, or side by side
with echo and paste. You can also interleave files with paste
and diff.
Converting text into other text using simple commands such as tr and
rev, or more powerful commands such as awk and sed.
$ cut -d: -f1 /etc/passwd | sort Print all usernames and sort them
$ cat *.txt | wc -l Total the number of lines
datePrints dates and times in various formats
seqPrints a sequence of numbers
A shell feature that prints a sequence of numbers or characters
findPrints file paths
yesPrints the same line repeatedly
$ date Default formatting Mon Jun 28 16:57:33 EDT 2021 $ date +%Y-%m-%d Year-Month-Day format 2021-06-28 $ date +%H:%M:%S Hour:Minute:Seconds format 16:57:33
$ date +"I cannot believe it's already %A!" Day of week I cannot believe it's already Tuesday!
$ seq 1 5 Print all integers from 1 to 5, inclusive 1 2 3 4 5
$ seq 1 2 10 Increment by 2 instead of 1 1 3 5 7 9
$ seq 3 -1 0 3 2 1 0
$ seq 1.1 0.1 2 Increment by 0.1 1.1 1.2 1.3 ⋮ 2.0
$ seq -s/ 1 5 Separate values with forward slashes 1/2/3/4/5
$ seq -w 8 10 08 09 10
$ echo {1..10} Forward from 1
1 2 3 4 5 6 7 8 9 10
$ echo {10..1} Backward from 10
10 9 8 7 6 5 4 3 2 1
$ echo {01..10} With leading zeros (equal width)
01 02 03 04 05 06 07 08 09 10
$ echo {1..1000..100} Count by hundreds from 1
1 101 201 301 401 501 601 701 801 901
$ echo {1000..1..100} Backward from 1000
1000 900 800 700 600 500 400 300 200 100
$ echo {01..1000..100} With leading zeros
0001 0101 0201 0301 0401 0501 0601 0701 0801 0901
$ ls
file1 file2 file4
$ ls file[2-4] Matches existing filenames
file2 file4
$ ls file{2..4} Evaluates to: file2 file3 file4
ls: cannot access 'file3': No such file or directory
file2 file4
$ echo {A..Z}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
$ echo {A..Z} | tr -d ' ' Delete spaces
ABCDEFGHIJKLMNOPQRSTUVWXYZ
$ echo {A..Z} | tr ' ' '\n' Change spaces into newlines
A
B
C
⋮
Z
$ alias nth="echo {A..Z} | tr -d ' ' | cut -c"
$ nth 10
J
$ find /etc -print List all of /etc recursively /etc /etc/issue.net /etc/nanorc /etc/apache2 /etc/apache2/sites-available /etc/apache2/sites-available/default.conf ⋮
$ find . -type f -print Files only $ find . -type d -print Directories only
$ find /etc -type f -name "*.conf" -print Files ending with .conf /etc/logrotate.conf /etc/systemd/logind.conf /etc/systemd/timesyncd.conf ⋮
$ find . -iname "*.txt" -print
$ find /etc -exec echo @ {} @ ";"
@ /etc @
@ /etc/issue.net @
@ /etc/nanorc @
⋮
$ find /etc -type f -name "*.conf" -exec ls -l {} ";"
-rw-r--r-- 1 root root 703 Aug 21 2017 /etc/logrotate.conf
-rw-r--r-- 1 root root 1022 Apr 20 2018 /etc/systemd/logind.conf
-rw-r--r-- 1 root root 604 Apr 20 2018 /etc/systemd/timesyncd.conf
⋮
$ find $HOME/tmp -type f -name "*~" -exec echo rm {} ";" echo for safety
rm /home/smith/tmp/file1~
rm /home/smith/tmp/junk/file2~
rm /home/smith/tmp/vm/vm-8.2.0b/lisp/vm-cus-load.el~
$ find $HOME/tmp -type f -name "*~" -exec rm {} ";" Delete for real
$ yes Repeats "y" by default y y y ^C Kill the command with Ctrl-C $ yes woof! Repeat any other string woof! woof! woof! ^C
$ yes "Efficient Linux" | head -n3 Print a string 3 times Efficient Linux Efficient Linux Efficient Linux
head, grep, and tail extract lines, and cut extracts columns. In this example, grep matches lines containing the string “blandit.”$ cat frost Whose woods these are I think I know. His house is in the village though; He will not see me stopping here To watch his woods fill up with snow. This is not the end of the poem. $ grep his frost Print lines containing "his" To watch his woods fill up with snow. This is not the end of the poem. "This" matches "his"
$ grep -w his frost Match the word "his" exactly To watch his woods fill up with snow.
$ grep -i his frost His house is in the village though; Matches "His" To watch his woods fill up with snow. Matches "his" This is not the end of the poem. "This" matches "his"
$ grep -l his * Which files contain the string "his"? frost
| To match this: | Use this syntax: | Example |
|---|---|---|
a The three commands also differ in their treatment of regular expressions; Table 5-1 presents a partial list. b For | ||
$ grep '^[A-Z]' myfile
$ grep -v '^$' myfile
$ grep 'cookie\|cake' myfile
$ grep '.....' myfile
$ grep '<.*>' page.html
$ grep w. frost Whose woods these are I think I know. He will not see me stopping here To watch his woods fill up with snow.
$ grep 'w\.' frost Whose woods these are I think I know. To watch his woods fill up with snow.
$ grep -F w. frost Whose woods these are I think I know. To watch his woods fill up with snow. $ fgrep w. frost Whose woods these are I think I know. To watch his woods fill up with snow.
$ cat /etc/passwd root:x:0:0:root:/root:/bin/bash 7th field is a shell daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 7th field is not a shell ⋮
$ cat /etc/shells /bin/sh /bin/bash /bin/csh
$ cut -d: -f7 /etc/passwd | sort -u | grep -f /etc/shells -F /bin/bash /bin/sh
$ cat alphabet A is for aardvark B is for bunny C is for chipmunk ⋮ X is for xenorhabdus Y is for yak Z is for zebu
$ tail -n3 alphabet X is for xenorhabdus Y is for yak Z is for zebu
$ tail -n+25 alphabet Y is for yak Z is for zebu
$ head -n4 alphabet | tail -n1 D is for dingo
$ head -n8 alphabet | tail -n3 F is for falcon G is for gorilla H is for hawk
$ head -4 alphabet Same as head -n4 alphabet $ tail -3 alphabet Same as tail -n3 alphabet $ tail +25 alphabet Same as tail -n+25 alphabet
$ less /etc/hosts 127.0.0.1 localhost 127.0.1.1 myhost myhost.example.com 192.168.1.2 frodo 192.168.1.3 gollum 192.168.1.28 gandalf
$ awk '{print $2}' /etc/hosts
localhost
myhost
frodo
gollum
gandalf
$ echo Efficient fun Linux | awk '{print $1 $3}' No whitespace
EfficientLinux
$ echo Efficient fun Linux | awk '{print $1, $3}' Whitespace
Efficient Linux
$ df / /data Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda1 1888543276 902295944 890244772 51% / /dev/sda2 7441141620 1599844268 5466214400 23% /data
$ df / /data | awk '{print $4}'
Available
890244772
5466214400
$ df / /data | awk 'FNR>1 {print $4}'
890244772
5466214400
$ echo efficient:::::linux | awk -F':*' '{print $2}' Any number of colons
linux
$ cat poem1 It is an ancient Mariner, And he stoppeth one of three. $ cat poem2 'By thy long grey beard and glittering eye, $ cat poem3 Now wherefore stopp'st thou me? $ cat poem1 poem2 poem3 It is an ancient Mariner, And he stoppeth one of three. 'By thy long grey beard and glittering eye, Now wherefore stopp'st thou me?
$ echo efficient linux in $HOME efficient linux in /home/smith
tacA bottom-to-top combiner of text files
pasteA side-by-side combiner of text files
diffA command that interleaves text from two files by printing their differences
$ cat poem1 poem2 poem3 | tac Now wherefore stopp'st thou me? 'By thy long grey beard and glittering eye, And he stoppeth one of three. It is an ancient Mariner,
$ tac poem1 poem2 poem3 And he stoppeth one of three. First file reversed It is an ancient Mariner, 'By thy long grey beard and glittering eye, Second file Now wherefore stopp'st thou me? Third file
192.168.1.34 - - [30/Nov/2021:23:37:39 -0500] "GET / HTTP/1.1" ... 192.168.1.10 - - [01/Dec/2021:00:02:11 -0500] "GET /notes.html HTTP/1.1" ... 192.168.1.8 - - [01/Dec/2021:00:04:30 -0500] "GET /stuff.html HTTP/1.1" ... ⋮
$ cat title-words1 EFFICIENT AT COMMAND $ cat title-words2 linux the line $ paste title-words1 title-words2 EFFICIENT linux AT the COMMAND line $ paste title-words1 title-words2 | cut -f2 cut & paste are complementary linux the line
$ paste -d, title-words1 title-words2 EFFICIENT,linux AT,the COMMAND,line
$ paste -d, -s title-words1 title-words2 EFFICIENT,AT,COMMAND linux,the,line
$ paste -d "\n" title-words1 title-words2 EFFICIENT linux AT the COMMAND line
$ cat file1 Linux is all about efficiency. I hope you will enjoy this book. $ cat file2 MacOS is all about efficiency. I hope you will enjoy this book. Have a nice day. $ diff file1 file2 1c1 < Linux is all about efficiency. --- > MacOS is all about efficiency. 2a3 > Have a nice day.
$ diff file1 file2 | grep '^[<>]' < Linux is all about efficiency. > MacOS is all about efficiency. > Have a nice day. $ diff file1 file2 | grep '^[<>]' | cut -c3- Linux is all about efficiency. MacOS is all about efficiency. Have a nice day.
trTranslates characters into other characters
revReverses characters on a line
awk and sedGeneral-purpose transformers
$ echo $PATH | tr : "\n" Translate colons into newlines /home/smith/bin /usr/local/bin /usr/bin /bin /usr/games /usr/lib/java/bin
$ echo efficient | tr a-z A-Z Translate a into A, b into B, etc. EFFICIENT $ echo Efficient | tr A-Z a-z efficient
$ echo Efficient Linux | tr " " "\n" Efficient Linux
$ echo efficient linux | tr -d ' \t' Remove spaces and tabs efficientlinux
$ echo Efficient Linux! | rev !xuniL tneiciffE
$ cat celebrities Jamie Lee Curtis Zooey Deschanel Zendaya Maree Stoermer Coleman Rihanna
$ rev celebrities sitruC eeL eimaJ lenahcseD yeooZ nameloC remreotS eeraM ayadneZ annahiR $ rev celebrities | cut -d' ' -f1 sitruC lenahcseD nameloC annahiR $ rev celebrities | cut -d' ' -f1 | rev Curtis Deschanel Coleman Rihanna
$ sed 10q myfile Print 10 lines and quit (q) $ awk 'FNR<=10' myfile Print while line number is ≤ 10
$ echo image.jpg | sed 's/\.jpg/.png/' Replace .jpg by .png
image.png
$ echo "linux efficient" | awk '{print $2, $1}' Swap two words
efficient linux
$ awk program input-files
$ awk -f program-file1 -f program-file2 -f program-file3 input-files
pattern {action}
BEGINENDIts action runs just once, after awk has processed
all the input.
An example is /^[A-Z]/ to match lines that begin with a
capital letter.
awkFor example, to check whether
the third field on an input line ($3) begins with a capital letter,
a pattern would be $3~/^[A-Z]/. Another example is FNR>5, which
tells awk to skip the first five lines of input.
$ awk '{print $NF}' celebrities
Curtis
Deschanel
Coleman
Rihanna
$ echo efficient linux | awk '/efficient/' efficient linux
python Programming Python 2010 Lutz, Mark
Lutz, Mark (2010). "Programming Python"
$ awk -F'\t' '{print $4, "(" $3 ").", "\"" $2 "\""}' animals.txt
Lutz, Mark (2010). "Programming Python"
Barrett, Daniel (2005). "SSH, The Secure Shell"
Schwartz, Randal (2012). "Intermediate Perl"
Bell, Charles (2014). "MySQL High Availability"
Siever, Ellen (2009). "Linux in a Nutshell"
Boney, James (2005). "Cisco IOS in a Nutshell"
Roman, Steven (1999). "Writing Word Macros"
$ awk -F'\t' '/^horse/{print $4, "(" $3 ").", "\"" $2 "\""}' animals.txt
Siever, Ellen (2009). "Linux in a Nutshell"
$ awk -F'\t' '$3~/^201/{print $4, "(" $3 ").", "\"" $2 "\""}' animals.txt
Lutz, Mark (2010). "Programming Python"
Schwartz, Randal (2012). "Intermediate Perl"
Bell, Charles (2014). "MySQL High Availability"
$ awk -F'\t' \
'BEGIN {print "Recent books:"} \
$3~/^201/{print "-", $4, "(" $3 ").", "\"" $2 "\""} \
END {print "For more books, search the web"}' \
animals.txt
Recent books:
- Lutz, Mark (2010). "Programming Python"
- Schwartz, Randal (2012). "Intermediate Perl"
- Bell, Charles (2014). "MySQL High Availability"
For more books, search the web
$ seq 1 100 | awk '{s+=$1} END {print s}'
5050
$ md5sum *.jpg | cut -c1-32 | sort | uniq -c | sort -nr | grep -v " 1 "
3 f6464ed766daca87ba407aede21c8fcc
2 c7978522c58425f6af3f095ef1de1cd5
2 146b163929b6533f02e91bdf21cb9563
$ md5sum *.jpg 146b163929b6533f02e91bdf21cb9563 image001.jpg 63da88b3ddde0843c94269638dfa6958 image002.jpg 146b163929b6533f02e91bdf21cb9563 image003.jpg ⋮
$ md5sum *.jpg | awk '{counts[$1]++}'
for (variable in array) do something with array[variable]
for (key in counts) print array[key]
$ md5sum *.jpg \
| awk '{counts[$1]++} \
END {for (key in counts) print counts[key]}'
1
2
2
⋮
$ md5sum *.jpg \
| awk '{counts[$1]++} \
END {for (key in counts) print counts[key] " " key}'
1 714eceeb06b43c03fe20eb96474f69b8
2 146b163929b6533f02e91bdf21cb9563
2 c7978522c58425f6af3f095ef1de1cd5
⋮
$ md5sum *.jpg \
| awk '{counts[$1]++; names[$1]=names[$1] " " $2} \
END {for (key in counts) print counts[key] " " key ":" names[key]}'
1 714eceeb06b43c03fe20eb96474f69b8: image011.jpg
2 146b163929b6533f02e91bdf21cb9563: image001.jpg image003.jpg
2 c7978522c58425f6af3f095ef1de1cd5: image019.jpg image020.jpg
⋮
$ md5sum *.jpg \
| awk '{counts[$1]++; names[$1]=names[$1] " " $2} \
END {for (key in counts) print counts[key] " " key ":" names[key]}' \
| grep -v '^1 ' \
| sort -nr
3 f6464ed766daca87ba407aede21c8fcc: image007.jpg image012.jpg image014.jpg
2 c7978522c58425f6af3f095ef1de1cd5: image019.jpg image020.jpg
2 146b163929b6533f02e91bdf21cb9563: image001.jpg image003.jpg
$ sed script input-files
$ sed -e script1 -e script2 -e script3 input-files
$ sed -f script-file1 -f script-file2 -f script-file3 input-files
s/regexp/replacement/
$ echo Efficient Windows | sed "s/Windows/Linux/" Efficient Linux
$ sed 's/.* //' celebrities Curtis Deschanel Coleman Rihanna
s/one/two/ s_one_two_ s@one@two@
$ echo Efficient Stuff | sed "s/stuff/linux/" Case sensitive; no match Efficient Stuff $ echo Efficient Stuff | sed "s/stuff/linux/i" Case-insensitive match Efficient linux
$ echo efficient stuff | sed "s/f/F/" Replaces just the first "f" eFficient stuff $ echo efficient stuff | sed "s/f/F/g" Replaces all occurrences of "f" eFFicient stuFF
$ seq 10 14 | sed 4d Remove the 4th line 10 11 12 14
$ seq 101 200 | sed '/[13579]$/d' Delete lines ending in an odd digit 102 104 106 ⋮ 200
$ ls image.jpg.1 image.jpg.2 image.jpg.3
image\.jpg\.[1-3]
image\.jpg\.\([1-3]\)
$ ls | sed "s/image\.jpg\.\([1-3]\)/image\1.jpg/" image1.jpg image2.jpg image3.jpg
$ ls apple.jpg.1 banana.png.2 carrot.jpg.3
\([a-z][a-z]*\) \1 = Base filename of one letter or more \([a-z][a-z][a-z]\) \2 = File extension of three letters \([0-9]\) \3 = A digit
\([a-z][a-z]*\)\.\([a-z][a-z][a-z]\)\.\([0-9]\)
$ ls | sed "s/\([a-z][a-z]*\)\.\([a-z][a-z][a-z]\)\.\([0-9]\)/\1\3.\2/" apple1.jpg banana2.png carrot3.jpg
$ cat title.txt This book is titled "Efficient Linux at the Command Line" $ fold -w40 title.txt This book is titled "Efficient Linux at the Command Line"
$ man -k width DisplayWidth (3) - image format functions and macros DisplayWidthMM (3) - image format functions and macros fold (1) - wrap each input line to fit in specified width ⋮
$ man -k "wide|width"
$ sudo apt update Download the latest metadata $ apt-file search string Search for a string
1 The related command ls -R produces output in a format that’s less convenient for pipelines.
2 Nowadays, some implementations of fsck have options -y and -n to respond yes or no, respectively, to every prompt, so the yes command is unnecessary here.
3 The name grep is short for “get regular expression and print.”
4 Quiz: what does the pipeline rev myfile | tac | rev | tac do?
5 You’ll see simpler solutions with awk and sed shortly, but this double-rev trick is handy to know.
6 Including the book sed & awk from O’Reilly.
7 The name awk is an acronym for Aho, Weinberger, and Kernighan, the program’s creators.
8 The name sed is short for “stream editor,” because it edits a stream of text.
9 If you’re familiar with the editors vi, vim, ex, or ed, sed script syntax may look familiar.
$ cd /bin $ ls -l bash cat ls grep -rwxr-xr-x 1 root root 1113504 Jun 6 2019 bash -rwxr-xr-x 1 root root 35064 Jan 18 2018 cat -rwxr-xr-x 1 root root 219456 Sep 18 2019 grep -rwxr-xr-x 1 root root 133792 Jan 18 2018 ls
$ cat /etc/shells /bin/sh /bin/bash /bin/csh /bin/zsh
$ echo $SHELL /bin/bash
#!/bin/bash# Print a promptecho-n'$ '# Read the user's input in a loop. Exit when the user presses Ctrl-D.whilereadline;do# Ignore the input $line and print a messageecho"I'm sorry, I'm afraid I can't do that"# Print the next promptecho-n'$ 'done
$ bash
$
$ PS1="%% " %% ls The prompt has changed animals.txt %% echo "This is a new shell" This is a new shell
%% exit $
#!/bin/bashcd/etcecho"Here is my current directory:"pwd
$ chmod +x cdtest
$ pwd /home/smith $ ./cdtest Here is my current directory: /etc
$ pwd /home/smith
$ cut -f1 grades | sort | uniq -c | sort -nr | head -n1 | cut -c9
HOME The path to your home directory. Its value is set automatically by
your login shell when you log in. Text editors like vim and emacs
read the variable HOME so they can locate and read their
configuration files ($HOME/.vim and $HOME/.emacs, respectively).
PWD Your shell’s current directory. Its value is set and maintained
automatically by the shell each time you cd to another directory.
The command pwd reads the variable PWD to print the name of your
shell’s current directory.
EDITORThe name of (or path to) your preferred text editor. Its value is generally set by you in a shell configuration file. Other programs read this variable to launch an appropriate editor on your behalf.
$ printenv | sort -i | less ⋮ DISPLAY=:0 EDITOR=emacs HOME=/home/smith LANG=en_US.UTF-8 PWD=/home/smith/Music SHELL=/bin/bash TERM=xterm-256color USER=smith ⋮
$ title="Efficient Linux" $ echo $title Efficient Linux $ printenv title (produces no output)
$ MY_VARIABLE=10 A local variable $ export MY_VARIABLE Export it to become an environment variable $ export ANOTHER_VARIABLE=20 Or, set and export in a single command
$ export E="I am an environment variable" Set an environment variable
$ L="I am just a local variable" Set a local variable
$ echo $E
I am an environment variable
$ echo $L
I am just a local variable
$ bash Run a child shell
$ echo $E Environment variable was copied
I am an environment variable
$ echo $L Local variable was not copied
Empty string is printed
$ exit Exit the child shell
$ export E="I am the original value" Set an environment variable $ bash Run a child shell $ echo $E I am the original value Parent's value was copied $ E="I was modified in a child" Change the child's copy $ echo $E I was modified in a child $ exit Exit the child shell $ echo $E I am the original value Parent's value is unchanged
For variables like HOME, the values are usually set and exported
by your login shell. All future shells (until you log out) are
children of the login shell, so they receive a copy of the variable
and its value. These sorts of system-defined environment variables are so
rarely modified in the real world that they seem global, but they are
just ordinary variables that play by the ordinary rules. (You may even
change their values in a running shell, but you might disrupt the expected
behavior of that shell and other programs.)
Local variables, which are not copied to children, can have their values set in a Linux configuration file such as $HOME/.bashrc (see more details in “Configuring Your Environment”). Each instance of the shell, on invocation, reads and executes the appropriate configuration files. As a result, these local variables appear to be copied from shell to shell. The same is true for other nonexported shell features such as aliases.
$ alias List aliases alias gd='pushd' alias l='ls -CF' alias pd='popd' $ bash --norc Run a child shell and ignore bashrc files $ alias List aliases - none are known $ echo $HOME Environment variables are known /home/smith $ exit Exit the child shell
$ (ls -l) Launches ls -l in a subshell -rw-r--r-- 1 smith smith 325 Oct 13 22:19 animals.txt $ (alias) View aliases in a subshell alias gd=pushd alias l=ls -CF alias pd=popd ⋮ $ (l) Run an alias from the parent animals.txt
$ echo $BASH_SUBSHELL Check the current shell 0 Not a subshell $ bash Run a child shell $ echo $BASH_SUBSHELL Check the child shell 0 Not a subshell $ exit Exit the child shell $ (echo $BASH_SUBSHELL) Run an explicit subshell 1 Yes, it's a subshell
Configuration files that execute automatically when you log in—that is, they apply only to your login shell. An example command in this file might set and export an environment variable. Defining an alias in this file would be less helpful, however, because aliases are not copied to children.
Configuration files that execute for every shell instance that is not a login shell—for example, when you run an interactive shell by hand or a (noninteractive) shell script. An example initialization file command might set a variable or define an alias.
Configuration files that execute immediately before your login shell
exits. An example command in this file might be clear to blank your
screen on logout.
| File type | Run by | System-wide location | Personal file locations (in order invoked) |
|---|---|---|---|
# Place in $HOME/.bash_profile or other personal startup fileif[-f"$HOME/.bashrc"]thensource"$HOME/.bashrc"fi
$ source ~/.bash_profile Uses the builtin "source" command $ . ~/.bash_profile Uses a dot
1 If you use a different shell, see also Appendix B.
2 I have trimmed the output selectively to display common environment variables. Your output is likely much longer and full of obscure variable names.
3 It’s complete except for traps, which “are reset to the values that the shell inherited from its parent at invocation” (man bash). I don’t discuss traps further in this book.
4 To make matters slightly more confusing, some desktop environments have their own shell configuration files. For example, GNOME has $HOME/.gnomerc, and the underlying X window system has $HOME/.xinitrc.
$ grep Nutshell animals.txt
$ cut -f1 grades | sort | uniq -c | sort -nr
Each command depends on the success or failure of the previous one.
Commands simply run one after the other.
$ cd dir Enter the directory $ touch new.txt Make the file
$ cd dir && touch new.txt
$ cp myfile.txt myfile.safe Make a backup copy $ nano myfile.txt Change the original $ rm myfile.safe Delete the backup
$ cp myfile.txt myfile.safe && nano myfile.txt && rm myfile.safe
$ git add . && git commit -m"fixed a bug" && git push
$ cd dir || mkdir dir
# If a directory can't be entered, exit with an error code of 1cddir||exit1
$ cd dir || mkdir dir && cd dir || echo "I failed"
$ sleep 7200; cp -a ~/important-files /mnt/backup_drive
$ sleep 300; echo "remember to walk the dog" | mail -s reminder $USER
$ mv file1 file2; mv file2 file3; mv file3 file4 $ echo $? 0 The exit code for "mv file3 file4"
A command is replaced by its output.
A command is replaced by a file (sort of).
Title: Carry On Wayward Son Artist: Kansas Album: Leftoverture Carry on my wayward son There'll be peace when you are done ⋮
$ grep -l "Artist: Kansas" *.txt carry_on_wayward_son.txt dust_in_the_wind.txt belexes.txt
$ mkdir kansas $ mv carry_on_wayward_son.txt kansas $ mv dust_in_the_wind.txt kansas $ mv belexes.txt kansas
$ mv $(grep -l "Artist: Kansas" *.txt) kansas
$(any command here)
$ mv carry_on_wayward_son.txt dust_in_the_wind.txt belexes.txt kansas
$ ls eStmt*pdf | tail -n1
$ okular $(ls eStmt*pdf | tail -n1)
$ echo Today is $(date +%A). Today is Saturday. $ echo Today is `date +%A`. Today is Saturday.
$ echo $(date +%A) | tr a-z A-Z Single SATURDAY echo Today is $(echo $(date +%A) | tr a-z A-Z)! Nested Today is SATURDAY!
VariableName=$(some command here)
$ kansasFiles=$(grep -l "Artist: Kansas" *.txt)
$ echo "$kansasFiles"
$ mkdir /tmp/jpegs && cd /tmp/jpegs
$ touch {1..1000}.jpg
$ rm 4.jpg 981.jpg
$ ls -1 | sort -n | less 1.jpg 2.jpg 3.jpg 5.jpg 4.jpg is missing ⋮
$ ls *.jpg | sort -n > /tmp/original-list
$ seq 1 1000 | sed 's/$/.jpg/' > /tmp/full-list
$ diff /tmp/original-list /tmp/full-list 3a4 > 4.jpg 979a981 > 981.jpg $ rm /tmp/original-list /tmp/full-list Clean up afterwards
<(any command here)
<(ls -1 | sort -n)
$ cat <(ls -1 | sort -n) 1.jpg 2.jpg ⋮
$ cp <(ls -1 | sort -n) /tmp/listing $ cat /tmp/listing 1.jpg 2.jpg ⋮
ls *.jpg | sort -n seq 1 1000 | sed 's/$/.jpg/'
$ diff <(ls *.jpg | sort -n) <(seq 1 1000 | sed 's/$/.jpg/') 3a4 > 4.jpg 979a981 > 981.jpg
$ diff <(ls *.jpg | sort -n) <(seq 1 1000 | sed 's/$/.jpg/') \
| grep '>' | cut -c3-
4.jpg
981.jpg
$ bash -c "ls -l" -rw-r--r-- 1 smith smith 325 Jul 3 17:44 animals.txt
$ pwd /home/smith $ touch /tmp/badfile Create a temporary file $ bash -c "cd /tmp && rm badfile" $ pwd /home/smith Current directory is unchanged
$ sudo echo "New log file" > /var/log/custom.log bash: /var/log/custom.log: Permission denied
'echo "New log file" > /var/log/custom.log'
$ sudo bash -c 'echo "New log file" > /var/log/custom.log' [sudo] password for smith: xxxxxxxx $ cat /var/log/custom.log New log file
$ echo "ls -l" ls -l $ echo "ls -l" | bash -rw-r--r-- 1 smith smith 325 Jul 3 17:44 animals.txt
$ ls -1 ??* apple banana cantaloupe carrot ⋮
$ mkdir {a..z}
^\(.\)
\(.*\)$
^\(.\)\(.*\)$
mv \1\2 \1
$ ls -1 ??* | sed 's/^\(.\)\(.*\)$/mv \1\2 \1/' mv apple a mv banana b mv cantaloupe c mv carrot c ⋮
$ ls -1 ??* | sed 's/^\(.\)\(.*\)$/mv \1\2\t\1/' | less
$ ls -1 ??* | sed 's/^\(.\)\(.*\)$/mv \1\2\t\1/' | bash
$ ssh myhost.example.com
$ ssh myhost.example.com ls remotefile1 remotefile2 remotefile3
$ ssh myhost.example.com ls > outfile Creates outfile on local host $ ssh myhost.example.com "ls > outfile" Creates outfile on remote host
$ echo "ls > outfile" | ssh myhost.example.com
$ echo "ls > outfile" | ssh -T myhost.example.com
$ echo "ls > outfile" | ssh myhost.example.com bash
$ ls -1 apple banana cantaloupe
$ ls -1 | xargs wc -l 3 apple 4 banana 1 cantaloupe 8 total
$ ls -1 | xargs cat
$ wc -l * 3 apple 4 banana 1 cantaloupe 8 total
$ find . -type f -name \*.py -print fruits/raspberry.py vegetables/leafy/lettuce.py ⋮
$ find . -type f -name \*.py -print0 | xargs -0 wc -l 6 ./fruits/raspberry.py 3 ./vegetables/leafy/lettuce.py ⋮
$ ls | xargs echo Fit as many input strings as possible: apple banana cantaloupe carrot echo apple banana cantaloupe carrot $ ls | xargs -n1 echo One argument per echo command: apple echo apple banana echo banana cantaloupe echo cantaloupe carrot echo carrot $ ls | xargs -n2 echo Two arguments per echo command: apple banana echo apple banana cantaloupe carrot echo cantaloupe carrot $ ls | xargs -n3 echo Three arguments per echo command: apple banana cantaloupe echo apple banana cantaloupe carrot echo carrot
$ ls | xargs -I XYZ echo XYZ is my favorite food Use XYZ as a placeholder apple is my favorite food banana is my favorite food cantaloupe is my favorite food carrot is my favorite food
$ rm *.txt bash: /bin/rm: Argument list too long
$ ls | grep '\.txt$' | xargs rm
$ find . -maxdepth 1 -name \*.txt -type f -print0 \ | xargs -0 rm
Immediately return the prompt and execute out of sight
Can be launched in the middle of a compound command
Supersedes the parent shell
$ wc -c my_extremely_huge_file.txt & Count characters in a huge file [1] 74931 Cryptic-looking response $
59837483748 my_extremely_huge_file.txt [1]+ Done wc -c my_extremely_huge_file.txt
[1]+ Exit 1 wc -c my_extremely_huge_file.txt
$ command1 & command2 & command3 & All 3 commands [1] 57351 in background [2] 57352 [3] 57353 $ command4 & command5 & echo hi All in background [1] 57431 but "echo" [2] 57432 hi
$ wc -c my_extremely_huge_file.txt & [1] 74931
| Command | Meaning |
|---|---|
$ sleep 20 & Run in the background [1] 126288 $ jobs List this shell's jobs [1]+ Running sleep 20 & $ ...eventually... [1]+ Done sleep 20
$ sleep 20 & Run in the background [1] 126362 $ fg Bring into the foreground sleep 20 ...eventually... $
$ sleep 20 Run in the foreground ^Z Suspend the job [1]+ Stopped sleep 20 $ jobs List this shell's jobs [1]+ Stopped sleep 20 $ fg Bring into the foreground sleep 20 ...eventually... [1]+ Done sleep 20
$ sleep 20 Run in the foreground ^Z Suspend the job [1]+ Stopped sleep 20 $ bg Move to the background [1]+ sleep 20 & $ jobs List this shell's jobs [1]+ Running sleep 20 & $ ...eventually... [1]+ Done sleep 20
$ sleep 100 & Run 3 commands in the background
[1] 126452
$ sleep 200 &
[2] 126456
$ sleep 300 &
[3] 126460
$ jobs List this shell's jobs
[1] Running sleep 100 &
[2]- Running sleep 200 &
[3]+ Running sleep 300 &
$ fg %2 Bring job 2 into the foreground
sleep 200
^Z Suspend job 2
[2]+ Stopped sleep 200
$ jobs See job 2 is suspended ("stopped")
[1] Running sleep 100 &
[2]+ Stopped sleep 200
[3]- Running sleep 300 &
$ kill %3 Terminate job 3
[3]+ Terminated sleep 300
$ jobs See job 3 is gone
[1]- Running sleep 100 &
[2]+ Stopped sleep 200
$ bg %2 Resume suspended job 2 in the background
[2]+ sleep 200 &
$ jobs See job 2 is running again
[1]- Running sleep 100 &
[2]+ Running sleep 200 &
$
$ sort /usr/share/dict/words | head -n2 & [1] 81089 $
$ sort /usr/share/dict/words | head -n2 & [1] 81089 $ A A's
[1]+ Done sort /usr/share/dict/words | head -n2 $
$ sort /usr/share/dict/words | head -n2 > /tmp/results & [1] 81089 $ [1]+ Done sort /usr/share/dict/words | head -n2 > /tmp/results $ cat /tmp/results A A's $
$ cat & [1] 82455 [1]+ Stopped cat
$ fg cat Here is some input Here is some input ⋮
$ command1 && command2 && command3 &
$ (cd /usr/local && ls) bin etc games lib man sbin share $ pwd /home/smith "cd /usr/local" occurred in a subshell
$ tar xvf package.tar.gz Makefile src/ src/defs.h src/main.c ⋮
$ cat package.tar.gz | (mkdir -p /tmp/other && cd /tmp/other && tar xzvf -)
$ tar czf - dir1 | (cd /tmp/dir2 && tar xvf -)
$ tar czf - dir1 | ssh myhost '(cd /tmp/dir2 && tar xvf -)'
$ bash Run a child shell $ PS1="Doomed> " Change the new shell's prompt Doomed> echo hello Run any command you like hello
Doomed> exec ls ls replaces the child shell, runs, and exits animals.txt $ A prompt from the original (parent) shell
#!/bin/bashecho"My name is$USER"> /tmp/outfileecho"My current directory is$PWD">> /tmp/outfileecho"Guess how many lines are in the file /etc/hosts?">> /tmp/outfile wc -l /etc/hosts >> /tmp/outfileecho"Goodbye for now">> /tmp/outfile
#!/bin/bash# Redirect stdout for this scriptexec> /tmp/outfile2# All subsequent commands print to /tmp/outfile2echo"My name is$USER"echo"My current directory is$PWD"echo"Guess how many lines are in the file /etc/hosts?"wc -l /etc/hostsecho"Goodbye for now"
$ cat /tmp/outfile2 My name is smith My current directory is /home/smith Guess how many lines are in the file /etc/hosts? 122 /etc/hosts Goodbye for now
| Problem | Solution |
|---|---|
1 The command mkdir -p dir, which creates a directory path only if it doesn’t already exist, would be a more elegant solution here.
2 This behavior is opposite of what many programming languages do, where zero means failure.
3 Alternatively, you could use cron for the backup job and at for reminders, but Linux is all about flexibility—finding multiple ways to achieve the same result.
4 Bank of America’s downloadable statement files are named this way at press time.
5 Technically, diff can read one list from stdin if you provide a dash as a filename, but not two lists.
6 This directory structure is similar to a hashtable with chaining.
7 The exact number depends on length limits on your Linux system; see man xargs.
8 Assuming that the tar archive was built with relative paths—which is typical for downloaded software—not absolute paths.
9 This specific problem can be solved more simply with the tar option -C or --directory, which specifies a target directory. I’m just demonstrating the general technique of using a subshell.
$ paste <(echo {1..10}.jpg | sed 's/ /\n/g') \
<(echo {0..9}.jpg | sed 's/ /\n/g') \
| sed 's/^/mv /' \
| bash
$ echo {1..10}.jpg
1.jpg 2.jpg 3.jpg ... 10.jpg
$ echo {0..9}.jpg
0.jpg 1.jpg 2.jpg ... 9.jpg
$ echo {1..10}.jpg | sed 's/ /\n/g'
1.jpg
2.jpg
⋮
10.jpg
$ echo {0..9}.jpg | sed 's/ /\n/g'
0.jpg
1.jpg
⋮
9.jpg
$ paste <(echo {1..10}.jpg | sed 's/ /\n/g') \
<(echo {0..9}.jpg | sed 's/ /\n/g')
1.jpg 0.jpg
2.jpg 1.jpg
⋮
10.jpg 9.jpg
$ paste <(echo {1..10}.jpg | sed 's/ /\n/g') \
<(echo {0..9}.jpg | sed 's/ /\n/g') \
| sed 's/^/mv /'
mv 1.jpg 0.jpg
mv 2.jpg 1.jpg
⋮
mv 10.jpg 9.jpg
$ paste <(echo {1..10}.jpg | sed 's/ /\n/g') \
<(echo {0..9}.jpg | sed 's/ /\n/g') \
| sed 's/^/mv /' \
| bash
$ ls *.jpg
$ ls | grep '\.jpg$'
$ echo $(ls *.jpg)
$ bash -c 'ls *.jpg'
$ cat <(ls *.jpg)
$ find . -maxdepth 1 -type f -name \*.jpg -print
$ ls > tmp && grep '\.jpg$' tmp && rm -f tmp
$ paste <(echo ls) <(echo \*.jpg) | bash
$ bash -c 'exec $(paste <(echo ls) <(echo \*.jpg))'
$ echo 'monkey *.jpg' | sed 's/monkey/ls/' | bash
$ python -c 'import os; os.system("ls *.jpg")'
$ echo {A..Z}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
$ echo {A..Z} | awk '{print $(17)}'
Q
$ echo {A..Z} | sed 's/ //g' | cut -c17
Q
$ echo {1..12}
1 2 3 4 5 6 7 8 9 10 11 12
$ echo 2021-{01..12}-01 | xargs -n1 date +%B -d
January
February
March
⋮
December
$ ls animals.txt cartoon-mascots.txt ... zebra-stripes.txt
$ ls | awk '{print "echo -n", $0, "| wc -c"}'
echo -n "animals.txt" | wc -c
echo -n "cartoon-mascots.txt | wc -c"
⋮
echo -n "zebra-stripes.txt | wc -c"
$ ls | awk '{print "echo -n", $0, "| wc -c"}' | bash | sort -nr | head -n1
23
Don’t retype commands while you experiment. Use techniques from Chapter 3 to recall previous commands, tweak them, and run them.
echo to test your expressions. If you aren’t sure how an expression will evaluate, print it with
echo beforehand to see the evaluated results on stdout.
ls or add echo to test destructive commands. If your command invokes rm, mv, cp, or other commands that
might overwrite or remove files, place echo in front of them to
confirm which files will be affected. (So, instead of executing rm,
execute echo rm.) Another safety tactic is to replace rm with ls
to list files that would be removed.
tee to view intermediate results. If you want to view the output (stdout) in the middle of a long
pipeline, insert the tee command to save output to a file for
examination. The following command saves the output from command3 in
the file outfile, while piping that same output to command4:
$ command1 | command2 | command3 | tee outfile | command4 | command5 $ less outfile
$ ls ch01.asciidoc ch03.asciidoc ch05.asciidoc ch07.asciidoc ch09.asciidoc ch02.asciidoc ch04.asciidoc ch06.asciidoc ch08.asciidoc ch10.asciidoc
$ mv ch10.asciidoc ch11.asciidoc $ mv ch09.asciidoc ch10.asciidoc $ mv ch08.asciidoc ch09.asciidoc ⋮ $ mv ch03.asciidoc ch04.asciidoc
$ seq -w 10 -1 3 10 09 08 ⋮ 03
$ seq -w 10 -1 3 | sed 's/\(.*\)/ch\1.asciidoc/' ch10.asciidoc ch09.asciidoc ⋮ ch03.asciidoc
$ seq -w 11 -1 4 | sed 's/\(.*\)/ch\1.asciidoc/' ch11.asciidoc ch10.asciidoc ⋮ ch04.asciidoc
$ paste <(seq -w 10 -1 3 | sed 's/\(.*\)/ch\1.asciidoc/') \
<(seq -w 11 -1 4 | sed 's/\(.*\)/ch\1.asciidoc/')
ch10.asciidoc ch11.asciidoc
ch09.asciidoc ch10.asciidoc
⋮
ch03.asciidoc ch04.asciidoc
$ paste <(seq -w 10 -1 3 | sed 's/\(.*\)/ch\1.asciidoc/') \
<(seq -w 11 -1 4 | sed 's/\(.*\)/ch\1.asciidoc/') \
| sed 's/^/mv /'
mv ch10.asciidoc ch11.asciidoc
mv ch09.asciidoc ch10.asciidoc
⋮
mv ch03.asciidoc ch04.asciidoc
$ paste <(seq -w 10 -1 3 | sed 's/\(.*\)/ch\1.asciidoc/') \
<(seq -w 11 -1 4 | sed 's/\(.*\)/ch\1.asciidoc/') \
| sed 's/^/mv /' \
| bash
$ ls ch*.asciidoc ch01.asciidoc ch04.asciidoc ch06.asciidoc ch08.asciidoc ch10.asciidoc ch02.asciidoc ch05.asciidoc ch07.asciidoc ch09.asciidoc ch11.asciidoc
$ ls bald_eagle.jpg blue_jay.jpg cardinal.txt robin.jpg wren.jpg bald_eagle.txt cardinal.jpg oriole.txt robin.txt wren.txt
$ ls *.jpg | cut -d. -f1 bald_eagle blue_jay cardinal robin wren $ ls *.txt | cut -d. -f1 bald_eagle cardinal oriole robin wren
$ diff <(ls *.jpg | cut -d. -f1) <(ls *.txt | cut -d. -f1) 2d1 < blue_jay 3a3 > oriole
$ diff <(ls *.jpg | cut -d. -f1) <(ls *.txt | cut -d. -f1) \ | grep '^[<>]' < blue_jay > oriole
$ diff <(ls *.jpg | cut -d. -f1) <(ls *.txt | cut -d. -f1) \
| grep '^[<>]' \
| awk '/^</{print $2 ".jpg"} /^>/{print $2 ".txt"}'
blue_jay.jpg
oriole.txt
blue_jay.jpg oriole.txt yellow.jpg This is wrong
$ diff <(ls *.jpg | sed 's/\.[^.]*$//') \
<(ls *.txt | sed 's/\.[^.]*$//') \
| grep '^[<>]' \
| awk '/</{print $2 ".jpg"} />/{print $2 ".txt"}'
blue_jay.txt
oriole.jpg
yellow.canary.txt
$ ls *.{jpg,txt} \
| sed 's/\.[^.]*$//' \
| uniq -c
2 bald_eagle
1 blue_jay
2 cardinal
1 oriole
2 robin
2 wren
1 yellow.canary
$ ls *.{jpg,txt} \
| sed 's/\.[^.]*$//' \
| uniq -c \
| awk '/^ *1 /{print $2}'
blue_jay
oriole
yellow.canary
$ ls *.{jpg,txt} \
| sed 's/\.[^.]*$//' \
| uniq -c \
| awk '/^ *1 /{print $2 "*"}'
blue_jay*
oriole*
yellow.canary*
$ ls -1 $(ls *.{jpg,txt} \
| sed 's/\.[^.]*$//' \
| uniq -c \
| awk '/^ *1 /{print $2 "*"}')
blue_jay.jpg
oriole.txt
yellow.canary.jpg
CDPATH=$HOME:$HOME/Work:$HOME/Family:$HOME/Finances:$HOME/Linux:$HOME/Music:..
$ (cd && ls -d */) Family/ Finances/ Linux/ Music/ Work/
$ (cd && ls -d */) | sed 's/^/$HOME\//g' $HOME/Family/ $HOME/Finances/ $HOME/Linux/ $HOME/Music/ $HOME/Work/
$ (cd && ls -d */) | sed 's@^@$HOME/@g' $HOME/Family/ $HOME/Finances/ $HOME/Linux/ $HOME/Music/ $HOME/Work/
$ (cd && ls -d */) | sed -e 's@^@$HOME/@' -e 's@/$@@' $HOME/Family $HOME/Finances $HOME/Linux $HOME/Music $HOME/Work
$ echo $(cd && ls -d */ | sed -e 's@^@$HOME/@' -e 's@/$@@') $HOME/Family $HOME/Finances $HOME/Linux $HOME/Music $HOME/Work
$ echo '$HOME' \
$(cd && ls -d */ | sed -e 's@^@$HOME/@' -e 's@/$@@') \
..
$HOME $HOME/Family $HOME/Finances $HOME/Linux $HOME/Music $HOME/Work ..
$ echo '$HOME' \
$(cd && ls -d */ | sed -e 's@^@$HOME/@' -e 's@/$@@') \
.. \
| tr ' ' ':'
$HOME:$HOME/Family:$HOME/Finances:$HOME/Linux:$HOME/Music:$HOME/Work:..
$ echo 'CDPATH=$HOME' \
$(cd && ls -d */ | sed -e 's@^@$HOME/@' -e 's@/$@@') \
.. \
| tr ' ' ':'
CDPATH=$HOME:$HOME/Family:$HOME/Finances:$HOME/Linux:$HOME/Music:$HOME/Work:..
$ wc -l /usr/share/dict/words 102305 /usr/share/dict/words
$ shuf /usr/share/dict/words | head -n3 evermore shirttail tertiary $ shuf /usr/share/dict/words | head -n3 interactively opt perjurer
$ echo $RANDOM $RANDOM $RANDOM 7855 11134 262
$ shuf -n $RANDOM /usr/share/dict/words | wc -l 9922 $ shuf -n $RANDOM /usr/share/dict/words | wc -l 32465
$ pwgen eng9nooG ier6YeVu AhZ7naeG Ap3quail poo2Ooj9 OYiuri9m iQuash0E voo3Eph1 IeQu7mi6 eipaC2ti exah8iNg oeGhahm8 airooJ8N eiZ7neez Dah8Vooj dixiV1fu Xiejoti6 ieshei2K iX4isohk Ohm5gaol Ri9ah4eX Aiv1ahg3 Shaew3ko zohB4geu ⋮
$ pwgen -N1 10 ieb2ESheiw
$ echo $(pwgen -N1 10).txt ohTie8aifo.txt
$ mkdir -p /tmp/randomfiles && cd /tmp/randomfiles $ shuf -n $RANDOM -o $(pwgen -N1 10).txt /usr/share/dict/words
$ ls List the new file Ahxiedie2f.txt $ wc -l Ahxiedie2f.txt How many lines does it contain? 13544 Ahxiedie2f.txt $ head -n3 Ahxiedie2f.txt Peek at the first few lines saviors guerillas forecaster
fori in{1..1000};doshuf -n$RANDOM-o$(pwgen -N1 10).txt /usr/share/dict/wordsdone
$ echo 'shuf -n $RANDOM -o $(pwgen -N1 10).txt /usr/share/dict/words' shuf -n $RANDOM -o $(pwgen -N1 10).txt /usr/share/dict/words
$ echo 'shuf -n $RANDOM -o $(pwgen -N1 10).txt /usr/share/dict/words' | bash $ ls eiFohpies1.txt
$ yes 'shuf -n $RANDOM -o $(pwgen -N1 10).txt /usr/share/dict/words' \ | head -n 1000 \ | bash $ ls Aen1lee0ir.txt IeKaveixa6.txt ahDee9lah2.txt paeR1Poh3d.txt Ahxiedie2f.txt Kas8ooJahK.txt aoc0Yoohoh.txt sohl7Nohho.txt CudieNgee4.txt Oe5ophae8e.txt haiV9mahNg.txt uchiek3Eew.txt ⋮
$ yes 'convert -size 8x8 xc: +noise Random -scale 100x100 $(pwgen -N1 10).png' \ | head -n 1000 \ | bash $ ls Bahdo4Yaop.png Um8ju8gie5.png aing1QuaiX.png ohi4ziNuwo.png Eem5leijae.png Va7ohchiep.png eiMoog1kou.png ohnohwu4Ei.png Eozaing1ie.png Zaev4Quien.png hiecima2Ye.png quaepaiY9t.png ⋮ $ display Bahdo4Yaop.png View the first image
$ mkdir /tmp/empties Create a directory for the files
$ cd /tmp/empties
$ touch file{01..1000}.txt Generate the files
$ grep '^[a-z]*$' /usr/share/dict/words a aardvark aardvarks ⋮
$ grep '^[a-z]*$' /usr/share/dict/words | shuf | head -n1000 triplicating quadruplicates podiatrists ⋮
$ grep '^[a-z]*$' /usr/share/dict/words | shuf | head -n1000 | xargs touch $ ls abases distinctly magnolia sadden abets distrusts maintaining sales aboard divided malformation salmon ⋮
1 The earliest use of this term (that I know of) is the manpage for lorder(1) in BSD Unix 4.x. Thanks to Bob Byrnes for finding it.
2 Starting with ch03.asciidoc and working forward would be dangerous—can you see why? If not, create these files with the command touch ch{01..10}.asciidoc and try it yourself.
$ cut -d: -f1 /etc/passwd | sort avahi backup daemon ⋮
$ awk -F: '$3>=1000 {print $1}' /etc/passwd
jones
smith
$ awk -F: '$3>=1000 {print $1}' /etc/passwd \
| xargs -I@ echo "Hi there, @!"
Hi there, jones!
Hi there, smith!
$ awk -F: '$3>=1000 {print $1}' /etc/passwd \
| xargs -I@ echo 'echo "Hi there, @!" | mail -s greetings @'
echo "Hi there, jones!" | mail -s greetings jones
echo "Hi there, smith!" | mail -s greetings smith
$ awk -F: '$3>=1000 {print $1}' /etc/passwd \
| xargs -I@ echo 'echo "Hi there, @!" | mail -s greetings @' \
| bash
echo "Hi there, jones!" | mail -s greetings jones
echo "Hi there, smith!" | mail -s greetings smith
$ find $HOME -name animals.txt -print /home/smith/Work/Writing/Books/Lists/animals.txt
$ find $HOME -print > $HOME/.ALLFILES $ head -n3 $HOME/.ALLFILES /home/smith /home/smith/Work /home/smith/Work/resume.pdf ⋮
$ grep animals.txt $HOME/.ALLFILES /home/smith/Work/Writing/Books/Lists/animals.txt
ff script#!/bin/bash# $@ means all arguments provided to the scriptgrep"$@"$HOME/.ALLFILES
$ chmod +x ff $ echo $PATH Check your search path /home/smith/bin:/usr/local/bin:/usr/bin:/bin $ mv ff ~/bin
$ ff animal /home/smith/Work/Writing/Books/Lists/animals.txt $ ff -i animal | less Case-insensitive grep /home/smith/Work/Writing/Books/Lists/animals.txt /home/smith/Vacations/Zoos/Animals/pandas.txt /home/smith/Vacations/Zoos/Animals/tigers.txt ⋮ $ ff -i animal | wc -l How many matches? 16
example.com oreilly.com efficientlinux.com ⋮
$ whois example.com | less Domain Name: EXAMPLE.COM Registry Domain ID: 2336799_DOMAIN_COM-VRSN Registrar WHOIS Server: whois.iana.org Updated Date: 2021-08-14T07:01:44Z Creation Date: 1995-08-14T04:00:00Z Registry Expiry Date: 2022-08-13T04:00:00Z ⋮
$ whois example.com | grep 'Registry Expiry Date:'
Registry Expiry Date: 2022-08-13T04:00:00Z
$ whois example.com | grep 'Registry Expiry Date:' | awk '{print $4}'
2022-08-13T04:00:00Z
$ date --date 2022-08-13T04:00:00Z Sat Aug 13 00:00:00 EDT 2022 $ date --date 2022-08-13T04:00:00Z +'%Y-%m-%d' Year-month-day format 2022-08-13
$ echo $(whois example.com | grep 'Registry Expiry Date:' | awk '{print $4}')
2022-08-13T04:00:00Z
$ date \
--date $(whois example.com \
| grep 'Registry Expiry Date:' \
| awk '{print $4}') \
+'%Y-%m-%d'
2022-08-13
$ ./check-expiry example.com 2022-08-13 example.com
check-expiry script#!/bin/bashexpdate=$(date\--date$(whois"$1"\|grep'Registry Expiry Date:'\|awk'{print $4}')\+'%Y-%m-%d')echo"$expdate$1"# Two values separated by a tab
check-expiry-all script#!/bin/bashcat domains.txt|whilereaddomain;do./check-expiry"$domain"sleep 5# Be kind to the registrar's serverdone
$ ./check-expiry-all &> expiry.txt &
$ cat expiry.txt 2022-08-13 example.com 2022-05-26 oreilly.com 2022-09-17 efficientlinux.com ⋮
$ sort -n expiry.txt | head -n1 2022-05-26 oreilly.com
$ awk "\$1<=\"$(date +%Y-%m-%d)\"" expiry.txt
201 NJ Hackensack, Jersey City 202 DC Washington 203 CT New Haven, Stamford ⋮ 989 MI Saginaw
Hackensack, Jersey City 201 NJ Washington 202 DC ⋮
$ grep -w NJ areacodes.txt 201 NJ Hackensack, Jersey City 551 NJ Hackensack, Jersey City 609 NJ Atlantic City, Trenton, southeast and central west ⋮
$ grep -w 202 areacodes.txt 202 DC Washington
$ grep Washing areacodes.txt 202 DC Washington 227 MD Silver Spring, Washington suburbs, Frederick 240 MD Silver Spring, Washington suburbs, Frederick ⋮
$ wc -l areacodes.txt 375 areacodes.txt
$ cut -f2 areacodes.txt | sort | uniq -c | sort -nr | head -n1
38 CA
$ awk -F'\t' '{printf "%s,%s,\"%s\"\n", $1, $2, $3}' areacodes.txt \
> areacodes.csv
$ head -n3 areacodes.csv
201,NJ,"Hackensack, Jersey City"
202,DC,"Washington"
203,CT,"New Haven, Stamford"
$ awk '$2~/^NJ$/{ac=ac FS $1} END {print "NJ:" ac}' areacodes.txt
NJ: 201 551 609 732 848 856 862 908 973
$ awk '{arr[$2]=arr[$2] " " $1} \
END {for (i in arr) print i ":" arr[i]}' areacodes.txt \
| sort
AB: 403 780
AK: 907
AL: 205 251 256 334 659
⋮
WY: 307
areacode script#!/bin/bashif[-n"$1"];thengrep -iw"$1"areacodes.txtfi
$ areacode 617 617 MA Boston
$ touch vault Create an empty file $ chmod 600 vault Set file permissions $ emacs vault Edit the file $ cat vault sally fake1 google.com account ssmith fake2 dropbox.com account for work s999 fake3 Bank of America account, bankofamerica.com smith2 fake4 My blog at wordpress.org birdy fake5 dropbox.com account for home
$ mkdir ~/etc $ mv vault ~/etc
$ cd ~/etc $ grep sally vault Match a username sally fake1 google.com account $ grep work vault Match the notes ssmith fake2 dropbox.com account for work $ grep drop vault Match multiple lines ssmith fake2 dropbox.com account for work birdy fake5 dropbox.com account for home
pman version 1: as simple as it gets#!/bin/bash# Just print matching linesgrep"$1"$HOME/etc/vault
$ chmod 700 pman $ mv pman ~/bin
$ pman goog sally fake1 google.com account $ pman account sally fake1 google.com account ssmith fake2 dropbox.com account for work s999 fake3 Bank of America account, bankofamerica.com birdy fake5 dropbox.com account for home $ pman facebook (produces no output)
pman version 2: add some error checking#!/bin/bash# Capture the script name.# $0 is the path to the script, and basename prints the final filename.PROGRAM=$(basename$0)# Location of the password vaultDATABASE=$HOME/etc/vault# Ensure that at least one argument was provided to the script.# The expression >&2 directs echo to print on stderr instead of stdout.if[$#-ne1];then>&2echo"$PROGRAM: look up passwords by string">&2echo"Usage:$PROGRAMstring"exit1fi# Store the first argument in a friendly, named variablesearchstring="$1"# Search the vault and print an error message if nothing matchesgrep"$searchstring""$DATABASE"if[$?-ne0];then>&2echo"$PROGRAM: no matches for '$searchstring'"exit1fi
$ pman pman: look up passwords by string Usage: pman string $ pman smith ssmith fake2 dropbox.com account for work smith2 fake4 My blog at wordpress.org $ pman xyzzy pman: no matches for 'xyzzy'
sally fake1 google google.com account ssmith fake2 dropbox dropbox.com account for work s999 fake3 bank Bank of America account, bankofamerica.com smith2 fake4 blog My blog at wordpress.org birdy fake5 dropbox2 dropbox.com account for home
pman version 3: prioritize searching for the key in the third column#!/bin/bashPROGRAM=$(basename$0)DATABASE=$HOME/etc/vaultif[$#-ne1];then>&2echo"$PROGRAM: look up passwords">&2echo"Usage:$PROGRAMstring"exit1fisearchstring="$1"# Look for exact matches in the third columnmatch=$(awk'$3~/^'$searchstring'$/'"$DATABASE")# If the search string doesn't match a key, find all matchesif[-z"$match"];thenmatch=$(awk"/$searchstring/""$DATABASE")fi# If still no match, print an error message and exitif[-z"$match"];then>&2echo"$PROGRAM: no matches for '$searchstring'"exit1fi# Print the matchecho"$match"
$ pman dropbox ssmith fake2 dropbox dropbox.com account for work $ pman drop ssmith fake2 dropbox dropbox.com account for work birdy fake5 dropbox2 dropbox.com account for home
$ gpg --quick-generate-key your_email_address default default never
$ cd ~/etc $ gpg -e -r your_email_address vault $ ls vault* vault vault.gpg
$ gpg -d -q vault.gpg Passphrase: xxxxxxxx sally fake1 google google.com account ssmith fake2 dropbox dropbox.com account for work ⋮
pman version 4: using an encrypted vault#!/bin/bashPROGRAM=$(basename$0)# Use the encrypted fileDATABASE=$HOME/etc/vault.gpgif[$#-ne1];then>&2echo"$PROGRAM: look up passwords">&2echo"Usage:$PROGRAMstring"exit1fisearchstring="$1"# Store the decrypted text in a variabledecrypted=$(gpg -d -q"$DATABASE")# Look for exact matches in the third columnmatch=$(echo"$decrypted"|awk'$3~/^'$searchstring'$/')# If the search string doesn't match a key, find all matchesif[-z"$match"];thenmatch=$(echo"$decrypted"|awk"/$searchstring/")fi# If still no match, print an error message and exitif[-z"$match"];then>&2echo"$PROGRAM: no matches for '$searchstring'"exit1fi# Print the matchecho"$match"
$ pman dropbox Passphrase: xxxxxxxx ssmith fake2 dropbox dropbox.com account for work $ pman drop Passphrase: xxxxxxxx ssmith fake2 dropbox dropbox.com account for work birdy fake5 dropbox2 dropbox.com account for home
decrypted=$(gpg -d -q"$DATABASE"|grep -v'^#')
1 This approach is similar to designing a database schema to work well with known queries.
2 The official list of area codes in CSV format, maintained by the North American Numbering Plan Administrator, lacks city names.
3 This command generates a public/private key pair with all default options and an expiration date of “never.” To learn more, see man gpg to read about gpg options, or seek out a GnuPG tutorial online.
4 If gpg proceeds without prompting for your passphrase, it has cached (saved) your passphrase temporarily.
The instructions differ for each desktop environment and may change from version to version, so it’s better if you look them up on the web. Search for the name of your desktop environment followed by “define keyboard shortcut.”
| Action | Keyboard shortcut |
|---|---|
$ firefox & $ google-chrome & $ opera &
$ firefox &> /dev/null &
$ firefox https://oreilly.com $ google-chrome https://oreilly.com $ opera https://oreilly.com
$ firefox --new-window https://oreilly.com $ google-chrome --new-window https://oreilly.com $ opera --new-window https://oreilly.com
$ firefox --private-window https://oreilly.com $ google-chrome --incognito https://oreilly.com $ opera --private https://oreilly.com
# Place in a shell configuration file and source it:aliasoreilly="firefox --new-window https://oreilly.com"
$ cat urls.txt duckduckgo.com My search engine nytimes.com My newspaper spotify.com My music $ grep music urls.txt | cut -f1 spotify.com $ google-chrome https://$(grep music urls.txt | cut -f1) Visit spotify
$ cat packages.txt 1Z0EW7360669374701 UPS Shoes 568733462924 FedEx Kitchen blender 9305510823011761842873 USPS Care package from Mom
track-it script that hits the tracking page of shippers#!/bin/bashPROGRAM=$(basename$0)DATAFILE=packages.txt# Choose a browser command: firefox, opera, google-chromeBROWSER="opera"errors=0 cat"$DATAFILE"|whilereadline;dotrack=$(echo"$line"|awk'{print $1}')service=$(echo"$line"|awk'{print $2}')case"$service"in UPS)$BROWSER"https://www.ups.com/track?tracknum=$track"&;;FedEx)$BROWSER"https://www.fedex.com/fedextrack/?trknbr=$track"&;;USPS)$BROWSER"https://tools.usps.com/go/TrackConfirmAction?tLabels=$track"&;;*)>&2echo"$PROGRAM: Unknown service '$service'"errors=1;;esacdoneexit$errors
$ curl https://efficientlinux.com/welcome.html Welcome to Efficient Linux.com! $ wget https://efficientlinux.com/welcome.html --2021-10-27 20:05:47-- https://efficientlinux.com/ Resolving efficientlinux.com (efficientlinux.com)... Connecting to efficientlinux.com (efficientlinux.com)... ⋮ 2021-10-27 20:05:47 (12.8 MB/s) - ‘welcome.html’ saved [32/32] $ cat welcome.html Welcome to Efficient Linux.com!
$ wget -U Mozilla url $ curl -A Mozilla url
https://efficientlinux.com/images/1.jpg https://efficientlinux.com/images/2.jpg https://efficientlinux.com/images/3.jpg ⋮
$ seq 1 20 | awk '{print "https://efficientlinux.com/images/" $1 ".jpg"}'
https://efficientlinux.com/images/1.jpg
https://efficientlinux.com/images/2.jpg
https://efficientlinux.com/images/3.jpg
⋮
$ seq 1 20 \
| awk '{print "wget https://efficientlinux.com/images/" $1 ".jpg"}' \
| bash
$ seq 1 20 | xargs -I@ wget https://efficientlinux.com/images/@.jpg
curl URL | ...clever pipeline here... | xargs -n1 wget
$ curl -s https://efficientlinux.com/areacodes.html \
| hxnormalize -x \
| less
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
⋮
<body>
<h1>Area code test</h1>
⋮
<tableid="ac"><thead><tr><th>Area code</th><th>State</th><th>Location</th></tr></thead><tbody><tr><tdclass="ac">201</td><tdclass="state">NJ</td><tdclass="cities">Hackensack, Jersey City</td></tr>⋮</tbody></table>
$ curl -s https://efficientlinux.com/areacodes.html \ | hxnormalize -x \ | hxselect -c -s@ '#ac .ac, #ac .state, #ac .cities' 201@NJ@Hackensack, Jersey City@202@DC@Washington@203@CT@New Haven, Stamford@...
[0-9]*@[A-Z][A-Z]@[^@]*@
\([0-9]*\)@\([A-Z][A-Z]\)@\([^@]*\)@
\1\t\2\t\3\n
s/\([0-9]*\)@\([A-Z][A-Z]\)@\([^@]*\)@/\1\t\2\t\3\n/g
$ curl -s https://efficientlinux.com/areacodes.html \ | hxnormalize -x \ | hxselect -c -s'@' '#ac .ac, #ac .state, #ac .cities' \ | sed 's/\([0-9]*\)@\([A-Z][A-Z]\)@\([^@]*\)@/\1\t\2\t\3\n/g' 201 NJ Hackensack, Jersey City 202 DC Washington 203 CT New Haven, Stamford ⋮
lynx renders the page https://efficientlinux.com/areacodes.html$ lynx -dump https://efficientlinux.com/areacodes.html > tempfile
$ cat tempfile
Area code test
Area code State Location
201 NJ Hackensack, Jersey City
202 DC Washington
203 CT New Haven, Stamford
⋮
| Operation | Clipboard | Primary selection |
|---|---|---|
$ xclip < myfile.txt
$ echo "Efficient Linux at the Command Line" | xclip
$ xclip -o Paste to stdout Efficient Linux at the Command Line $ xclip -o > anotherfile.txt Paste to a file $ xclip -o | wc -w Count words 6
$ cut -f1 grades | sort | uniq -c | sort -nr | head -n1 | cut -c9 | xclip
$ echo -n | xclip
$ echo https://oreilly.com | xclip -selection clipboard Copy $ xclip -selection clipboard -o Paste https://oreilly.com
$ xclip -sel c -o Same as xclip -selection clipboard -o https://oreilly.com
$ firefox $(xclip -selection clipboard -o)
pman script that loads username and password as selections#!/bin/bashPROGRAM=$(basename$0)DATABASE=$HOME/etc/vault.gpg load_password(){# Place username (field 1) into clipboardecho"$1"|cut -f1|tr -d'\n'|xclip -selection clipboard# Place password (field 2) into X primary selectionecho"$1"|cut -f2|tr -d'\n'|xclip -selection primary# Give feedback to the userecho"$PROGRAM: Found"$(echo"$1"|cut -f3- --output-delimiter': ')echo"$PROGRAM: username and password loaded into X selections"}if[$#-ne1];then>&2echo"$PROGRAM: look up passwords">&2echo"Usage:$PROGRAMstring"exit1fisearchstring="$1"# Store the decrypted text in a variabledecrypted=$(gpg -d -q"$DATABASE")if[$?-ne0];then>&2echo"$PROGRAM: could not decrypt$DATABASE"exit1fi# Look for exact matches in the third columnmatch=$(echo"$decrypted"|awk'$3~/^'$searchstring'$/')if[-n"$match"];thenload_password"$match"exit$?fi# Look for any matchmatch=$(echo"$decrypted"|awk"/$searchstring/")if[-z"$match"];then>&2echo"$PROGRAM: no matches"exit1fi# Count the matchescount=$(echo"$match"|wc -l)case"$count"in 0)>&2echo"$PROGRAM: no matches"exit1;;1)load_password"$match"exit$?;;*)>&2echo"$PROGRAM: multiple matches for the following keys:"echo"$match"|cut -f3 >&2echo"$PROGRAM: rerun this script with one of the keys"exit;;esac
$ pman dropbox Passphrase: xxxxxxxx pman: Found dropbox: dropbox.com account for work pman: username and password loaded into X selections $ pman account Passphrase: xxxxxxxx pman: multiple matches for the following keys: google dropbox bank dropbox2 pman: rerun this script with one of the keys
(sleep30&&echo-n|xclip -selection primary)&
1 Unless you’re working in an application that captures all keystrokes, such as a virtual machine in a window.
2 This example uses three CSS selectors, but some old versions of hxselect can handle only two. If your version of hxselect is afflicted by this shortcoming, download the latest version from the World Wide Web Consortium and build it with the command configure && make.
3 Really there are three X selections, but one of them, called the secondary selection, is rarely exposed by modern desktop environments.
VISUAL=emacs EDITOR=emacs
$ vim $(grep -l string *)
$ vim $(grep -lr string .)
$ vim $(find . -type f -print0 | xargs -0 grep -l string)
aliasfirfox=firefoxaliasles=lessaliasmeacs=emacs
$ type firfox bash: type: firfox: not found $ man firfox No manual entry for firfox
$ touch newfile1
$ mkdir tmp Create a directory
$ cd tmp
$ touch file{0000..9999}.txt Create 10,000 files
$ cd ..
$ rm -rf tmp Remove the directory and files
$ echo -n > newfile2
$ cat myfile | while read line; do ...do something here... done
$ cat /etc/hosts | while read line; do echo "$line" | wc -c done 65 31 1 ⋮
$ find . -exec your command here \;
ls -Rcp -r or cp -arm -rgrep -rchmod -Rchown -Rchgrp -R$ man bash | wc -w 46318
* * * * * command Run command every minute 30 7 * * * command Run command at 07:30 every day 30 7 5 * * command Run command at 07:30 the 5th day of every month 30 7 5 1 * command Run command at 07:30 every January 5 30 7 * * 1 command Run command at 07:30 every Monday
$ at 22:00 tomorrow warning: commands will be executed using /bin/sh at> echo brush your teeth | mail $USER at> ^D Type Ctrl-D to end input job 699 at Sun Nov 14 22:00:00 2021
$ atq 699 Sun Nov 14 22:00:00 20211 a smith
$ at -c 699 | tail ⋮ echo brush your teeth | mail $USER
$ atrm 699
$ cp -a dir1 dir2
$ rsync -a dir1/ dir2
-v (meaning “verbose”)To print the names of files as they’re copied
-nTo pretend to copy; combine with -v to see which files would be copied
-xTo tell rsync not to cross filesystem boundaries
#!/bin/bashBOOKTITLE="Slow Inefficient Linux"rm$BOOKTITLE# Wrong! Don't do this!
rm Slow Efficient Linux
rm"$BOOKTITLE"
rm "Slow Efficient Linux"
$ cat chapter1.txt chapter2.txt chapter3.txt > book.txt
book.txt:chapter1.txtchapter2.txtchapter3.txt
book.txt:chapter1.txtchapter2.txtchapter3.txtcat chapter1.txt chapter2.txt chapter3.txt > book.txt
$ ls Makefile chapter1.txt chapter2.txt chapter3.txt $ make cat chapter1.txt chapter2.txt chapter3.txt > book.txt Executed by make $ ls Makefile book.txt chapter1.txt chapter2.txt chapter3.txt $ make make: 'book.txt' is up to date. $ vim chapter2.txt Update a chapter $ make cat chapter1.txt chapter2.txt chapter3.txt > book.txt
%.html:%.asciidocasciidoctor -o$@$<
$ ls ch11* ch11.asciidoc $ make ch11.html asciidoctor -o ch11.html ch11.asciidoc $ ls ch11* ch11.asciidoc ch11.html $ firefox ch11.html View the HTML file
$ cp myfile myfile.bak
$ git init
$ git add .
$ git commit -m"Changed X to Y"
$ git log
$ ls -l /bin
$ pwd /home/smith The home directory of user smith
$ cd /usr/local Absolute path $ cd bin Relative path leading to /usr/local/bin $ cd ../etc Relative path leading to /usr/local/etc
emacsOnce emacs is running, type Ctrl-h followed by t for a tutorial.
nanoVisit nano-editor.org for documentation.
vim or vi$ nano newfile.txt
$ touch funky.txt $ ls funky.txt
$ ls animals.txt
$ ls -l -rw-r--r-- 1 smith smith 325 Jul 3 17:44 animals.txt
$ ls -a .bashrc .bash_profile animals.txt
$ cp animals.txt beasts.txt $ ls animals.txt beasts.txt
$ mv beasts.txt creatures.txt $ ls animals.txt creatures.txt
$ rm creatures.txt
$ mkdir testdir $ ls animals.txt testdir $ mv testdir newname $ ls animals.txt newname $ rmdir newname $ ls animals.txt
$ touch file1 file2 file3 $ mkdir dir $ ls dir file1 file2 file3 $ cp file1 file2 file3 dir $ ls dir file1 file2 file3 $ ls dir file1 file2 file3 $ rm file1 file2 file3
$ touch thing1 thing2 thing3 $ ls dir thing1 thing2 thing3 $ mv thing1 thing2 thing3 dir $ ls dir $ ls dir file1 file2 file3 thing1 thing2 thing3
$ rm -rf dir
$ cat animals.txt
$ less animals.txt
$ chmod 644 animals.txt $ ls -l -rw-r--r-- 1 smith smith 325 Jul 3 17:44 animals.txt
$ chmod 600 animals.txt $ ls -l -rw------- 1 smith smith 325 Jul 3 17:44 animals.txt
$ mkdir dir $ chmod 755 dir $ ls -l drwxr-xr-x 2 smith smith 4096 Oct 1 12:44 dir
$ chmod 700 dir $ ls -l drwx------ 2 smith smith 4096 Oct 1 12:44 dir
$ ps PID TTY TIME CMD 5152 pts/11 00:00:00 bash 117280 pts/11 00:00:00 emacs 117273 pts/11 00:00:00 ps
$ ps -uax
$ kill 117280 [1]+ Exit 15 emacs animals.txt
$ man cat
| Keystroke | Action |
|---|---|
#!/bin/bash
#!/bin/bash# This is a sample scriptecho"Hello there!"date
$ chmod 755 howdy Set all permissions, including execute permission $ chmod +x howdy Or, just add execute permission
$ ./howdy Hello there! Fri Sep 10 17:00:52 EDT 2021
$ howdy howdy: command not found
$ touch /usr/local/avocado Try to create a file in a system directory touch: cannot touch '/usr/local/avocado': Permission denied
$ sudo touch /usr/local/avocado Create the file as root [sudo] password for smith: password here $ ls -l /usr/local/avocado List the file -rw-r--r-- 1 root root 0 Sep 10 17:16 avocado $ sudo rm /usr/local/avocado Clean up as root
1 The dot and double dot are not expressions evaluated by the shell. They are hard links present in every directory.
2 Or another program if you redefine the value of the shell variable PAGER.
3 If you omit the shebang line, your default shell will run the script. It’s a good practice to include the line.
4 That’s because the current directory is usually omitted from the shell’s search path, for security reasons. Otherwise, an attacker could place a malicious, executable script named ls in your current directory, and when you ran ls, the script would run instead of the true ls command.
| bash feature | dash | fish | ksh | tcsh | zsh |
|---|---|---|---|---|---|
a This feature is disabled by default. Run b Custom command completion, using the c Functions: This shell does not support newer-style definitions that begin with the d Incremental search of history works differently in e To enable incremental history search with Ctrl-R in f In g Job control: h Redirection of stdout and stderr: The syntax in this shell is: i Sourcing in this shell requires an explicit path to the sourced file, such as | |||||