Shell 101 ######### .. highlight:: sh .. _shell101-opening: Opening ======= To open a terminal, navigate to the directory you want using the File Explorer, then right-click in the file list and select "Zsh Prompt Here". .. image:: /_static/zsh-here.svg To exit the terminal, you have several options: #. Use the "X" button on the window frame. #. Type ``exit``. #. Press ``+d``. Autocompletion ============== .. note:: When you see ````, that means press that key. For example, ``ls a`` means you should type ``ls a``, then press the TAB key. ZSH's most powerful feature is autocompletion. First, create an empty directory using the File Explorer, then :ref:`open a terminal there `. To examine the contents of the current directory, use the ``ls`` command (type in ``ls`` followed by ````). Of course, it's empty, so let's fill it up with something. Let's grab a copy of the Fossil source code and unzip it there just using two commands (which I recommend you just copy-paste inside the terminal):: wget https://www.fossil-scm.org/index.html/zip/trunk/fossil.zip unzip fossil.zip The ``wget`` command is used to **get** stuff from the **w**\ eb, and ``unzip`` does what you think it does. Now let's try ``ls`` again. We now see two entries, ``fossil`` (the unzipped directory) and ``fossil.zip``. Let's take a peek inside the ``fossil`` directory. To do that, we can call ``ls`` with an extra parameter (or "argument"), i.e.:: ls fossil There's a whole bunch of files. To **view** one of the files, we use the ``less`` command:: less fossil/Makefile.classic You can use the arrow keys and page-up/page-down. To exit ``less``, press ``q``. Typing that whole command just to display a file is annoying. Thankfully, there's a faster way. Try:: less fo/clas The ```` key tells the shell to try to autocomplete what we've typed so far. Sometimes, the completion isn't unique. Try, for example:: less fo/mak The shell displays ``less fossil/Makefile.``. Pressing ```` again shows us the two possible options, ``Makefile.classic`` and ``Makefile.in``. To remove the ambiguity and autocomplete to ``Makefile.classic``, we can either: #. type ``c`` followed by ````; or #. simply press ```` again, then use the arrow keys to select the option we want. Changing directory ------------------ Paths are relative to the current directory. If we're currently in ``/home/user/`` and we want to refer to file ``/home/user/hello/world.txt``, we can refer to it as ``hello/world.txt``. Continuing our exploration of the Fossil source tree, let's move inside the ``fossil`` subdirectory. To do that, we **change directory** (or ``cd``) into it:: cd fossil Get in the habit of running ``ls`` to see what's in the current directory. Helping yourself ---------------- Next, let's move to the ``src`` subdirectory:: cd src Try the following command:: ls -S To find out what the ``-S`` option means and what other options are available, enter ``ls --help`` to view the command's built-in usage guide. This is usually just a summary, though. To view the proper documentation, try the ``man ls`` command, which will display the "**man**\ ual page" of ``ls``. To view a detailed list (including modification times) of the files in a directory, use the ``-l`` option to ``ls``. Change directory autocompletion ------------------------------- To move to the parent directory, change directory to ``..``, i.e.:: cd .. Let's move into another directory, say ``www``:: cd www If we now want to go back to the ``src`` directory, we could either do ``cd ..`` followed by ``cd src`` (boo!), or we can use the directory history autocompletion. Try:: cd - (That is, ``cd -`` followed by ````.) You can now either enter the number of the entry you want, or press ```` again and use the arrow keys and ```` to select the entry you want. File management commands ======================== You can achieve most of these things using the File Explorer, but (once you get used to it) you can probably do them faster in the terminal. Basic commands -------------- To create a directory, use the ``mkdir {path}`` command. To create a directory and all parent directories if they don't exist, use the ``-p`` option to ``mkdir`` command. ``mkdir -p`` will also not complain if all the directories already exist. To create an empty (zero length) file, you can use the ``touch {filename}`` command. To **r**\ e\ **m**\ ove a file, use ``rm {filename}``. To recursively remove everything below a path, use ``rm -r {filename}``. To remove an empty directory (and throw an error if it's not empty) use ``rmdir``. To **c**\ o\ **p**\ y a file or directory, use ``cp {source}... {destination}``. The exact semantics of this command depend on whether the destination already exists and whether it is a directory:: # copies `file1` to `file2`, overwriting `file2` if already exists cp file1 file2 # copies `file1` and `file2` to existing directory `existing_dir` cp file1 file2 existing_dir # fails with "cp: target 'nonexisting' is not a directory" cp file1 file2 nonexisting # fails with "cp: -r not specified; omitting directory 'dir1'" # in other words, to copy recursively, you need the `-r` switch cp dir1 nonexisting # recursive copy `dir1` to `nonexisting_dir` cp -r dir1 nonexisting_dir # recursive copy `dir1` to `existing_dir/dir1` cp -r dir1 existing_dir # recursive copy `dir1` to `existing_dir/dir1`, and `dir2` to `existing_dir/dir2` cp -r dir1 dir2 existing_dir To **m**\ o\ **v**\ e or rename a file, use the ``mv`` command. Note that ``mv`` and ``cp`` have similar semantics:: # rename `file1` to `file2`, overwriting `file2` if it exists (and isn't a directory) mv file1 file2 # move `file1` to `existing_dir/file1` mv file1 existing_dir # move `file1` and `file2` to `existing_dir/file1` and `existing_dir/file2` mv file1 file2 existing_dir To preserve the file modification times and attributes, use the ``-a`` switch to the ``cp`` command. Editing text files ------------------ I've added a custom command, ``k`` to your "$HOME/bin/" directory. You can use it to edit text files:: k file.txt You can have a look at its own source code and modify it as you please (so it calls your favorite text editor instead):: k ~/bin/k Searching --------- To search for a piece of text, use the ``grep`` command. For example, to search for "solve_quadratic" below the path "some/directory/", you would run:: grep -r 'solve_quadratic' some/directory/ .. _shell-globbing: Globbing -------- Let's say you want to copy every :abbr:`CSV (Comma-Separated Values)` file from the "output" directory into the current directory ("."; the current directory is "."). Even with autocompletion, constructing the command ``cp output/file1.csv output/file2.csv output/file3.csv ... ./`` would be quite tedious. There is a better way, which is to use the shell's built-in pattern matching mechanism, called *globbing*:: cp output/*.csv . The wildcard "*" matches any filename that does not start with ".". There is also the double-star wildcard "**" which is like "*" except it also recurses down subdirectories. In other words, +-----------------------------+------------------------------+-------------------------------+-------------------------------+ | Path | Matches "output/\*.csv"? | Matches "output/\*\*/\*.csv"? | Notes | +=============================+==============================+===============================+===============================+ | ``output/a.csv`` | yes | yes | | +-----------------------------+------------------------------+-------------------------------+-------------------------------+ | ``a.csv`` | no | no | doesn't start with "output/" | +-----------------------------+------------------------------+-------------------------------+-------------------------------+ | ``output/.b.csv`` | no | no | starts with a "." | +-----------------------------+------------------------------+-------------------------------+-------------------------------+ | ``output/subdir/x.csv`` | no | yes | "\*\*" recurses down | | | | | subdirectories; "\*" doesn't | +-----------------------------+------------------------------+-------------------------------+-------------------------------+ Rsync ----- ``rsync`` is the swiss army knife of file copying and synchronization. It has many advantages over ``cp`` and ``cp -r``: - It only copies the files that have actually changed. - It can work remotely over ssh. - When working remotely, it achieves bandwidth efficiency by only copying the parts of files that have actually changed (link_). - You can include or exclude particular paths. .. _link: https://en.wikipedia.org/wiki/Rsync#Determining_which_parts_of_a_file_have_changed Note that ``rsync`` has different semantics from ``cp``, so be careful. In particular, note that when it is called with a single source argument, whether that source has a trailing "/" matters! See:: # creates "dir/" if it doesn't exist, then copies `file1` and `file2` into it rsync -a file1 file2 dir/ # copies dir1/X to dir2/X rsync -a dir1/ dir2/ # copies dir1/X to dir2/dir1/X rsync -a dir1 dir2/ # copies everything under dir1/ to dir2/, and removes everything in dir2/ that wasn't under dir1/ # under this usage, it's basically a directory sync tool rsync -a --delete dir1/ dir2/ # copy to remote ssh host "workstation" rsync -a dir1/ workstation:dir2/ I personally like to edit my code in a local fossil checkout, then rsync over the code to the group server, and run it there. The command I use is something like:: rsync /path/to/devel/project/ --exclude '/out/**' --exclude '*.pyc' --exclude '__pycache__' --include project/'**' --include doc/'**' --include pint/'**' --include 'data/**' --include '*/' --exclude '*' -av server:j/ .. _shell-rsync-remote-path: Rsync remote paths ^^^^^^^^^^^^^^^^^^ Remote directory paths are relative to the remote home directory (typically ``/home/