sd -- a drop-in replacement for `cd'

Check-in [cc736ffd49]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:de-novo generation of `man.md' from sd's manpage using the newly added script `mkman.sh'.
Timelines: family | ancestors | descendants | both | mksh
Files: files | file ages | folders
SHA1: cc736ffd49af69f808adb09fbabdceaa74752156
User & Date: vdh 2019-12-12 16:18:07
Context
2019-12-13 14:09
minor adjustments to quickstart.md. check-in: 77dcf74ab6 user: vdh tags: mksh
2019-12-12 16:19
de-novo generation of `man.md' from sd's manpage using the newly added script `mkman.sh'. check-in: 278a59b06e user: vdh tags: trunk
2019-12-12 16:18
de-novo generation of `man.md' from sd's manpage using the newly added script `mkman.sh'. check-in: 129a43945a user: vdh tags: ksh
2019-12-12 16:18
de-novo generation of `man.md' from sd's manpage using the newly added script `mkman.sh'. check-in: cc736ffd49 user: vdh tags: mksh
2019-12-11 16:33
fix quoting. check-in: c4044ae8d8 user: vdh tags: mksh
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to www/man.md.

1
2


3
4
5
6
7


8

9
10
11
12
13
14
15
16
17
18
19
20
21
22


23
24


25
26
27
28
29
30
31
32
33

34
35
36
37
38
39
40
41

42

43
44
45
46
47
48
49
50
51
52

53
54
55
56

57
58
59
60
61
62






63
64
65
66
67
68
69
70

71
72
73
74


75
76


77
78
79
80
81

82
83
84








85
86

87
88

89
90







































































































































91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# The `sd' man page #



This is a mostly verbatim copy of the **sd** internal manpage that can also be
accessed with the command `dirs -m` after **sd** has been correctly installed.
You might also be interested in looking at the
[quickstart guide](quick.md).



---


*   [NAME](#name)
*   [SYNOPSIS](#synopis)
*   [DESCRIPTION](#description)
*   [USAGE](#usage)
*   [OPTIONS](#options)
*   [SHELL VARIABLES](#variables)
*   [INITIAL SETUP](#setup)

---

## <a id="name"></a> NAME ##

sd − switch between directories using a dynamic directory stack



## <a id="synopsis"></a> SYNOPSIS ##



    sd  [pattern|pathname|-]
    sdirs [−hmpfsict] [−l num] [−d pattern] [pattern]

## <a id="description"></a> DESCRIPTION ##

The **sd** utility consists of a set of (*ksh*) shell functions enabling rapid
navigation between recently visited directories. **sd** keeps track of all
recent **cd** activities with the help of a logfile. This logfile is analyzed

to generate a frequency distribution of the visits to different directories.
The directory stack can be queried by further **sd** commands using pattern
matching. A sliding window (containing a limited number of trailing entries
from the logfile) is used. This leads to a dynamically changing stack which
is updated after each **sd** action. While the logfile is shared by all
running shells, the directory stack is not. Therefore, modifications of the
logfile (and stack) by **sd** actions in one shell do not immediately affect
the stack in another shell, which is only updated after the next **sd**

action. `sdirs -f` can be used to enforce an update.


Some care is taken to maintain compatibility with *bash* and *zsh*. **sd**
can be used in combination with *tcsh*, too, through the auxiliary *ksh*
scripts sdcmd and **sdirs**. However, functionality is somewhat restricted in
this case.

In all shells, interaction with **sd** proceeds
solely via **sd** and **sdirs**.

## <a id="usage"></a> USAGE ##


After correct setup **sd** can be used as a replacement for **cd**. **sd**
takes one or more arguments that together define a string pattern (possibly
containing blanks) which is first tried as a pathname. The special cases "`sd`"

and "`sd -`" work as expected. If pathname interpretation fails, `pattern` is
used for a lookup in the last `sdlines` directories visited (not counting your
home directory). In this case `pattern` can be any valid regular expression.
Characters special to the shell might need quoting and so do characters with
special meaning to regex matching. Thus a lookup of a verbatim `a.b` requires
the pattern `a\\.b` in order to get the quoting through.







The search is performed top-down starting at the most frequently visited (most
recently visited, if `sdbytime=1`) directory. The working directory is changed
to the first match found. If this match is not the correct one, the search
pattern has to be refined. Frequently, trailing substrings from the full
pathname, notably the basename, work fine. Alternatively, it is also possible
to switch between directories with `sd =rank` where *rank* is the rank index
displayed by **sdirs**. `sd =` is equivalent to `sd =1`. Quoting of the `=`

might be necessary with older versions of *tcsh* and with *zsh*.

By default **sdirs** lists the directory stack in the three column format
*frequency* *rank* *directory_name* sorted top-down according to search order


(usually by frequencies). Specifying a pattern as argument restricts the
display to matching entries.



**sdirs** also serves as user interface for performing other tasks according
to chosen option as detailed below.

## <a id="options"></a> OPTIONS ##


**sdirs** accepts these options:









    −h      Show short usage note.


    −m      Show manpage.


    −p      If specified before −m, convert manpage to postscript and send to
            stdout.








































































































































    −f      Forces a refresh of the directory stack. This might be helpful if
            sdlines" and "sdbytime" are modified interactively or if several
            shell incarnations are running in parallel.
            
    −s      Displays the elements in the directory stack alphabetically
            instead of according to search order. Might be helpful for
            locating some ill-remembered pathname to find out its rank for a
            "sd =rank" command.

    −i      Show status info for logfile and stack.

    −c      Remove stale entries no longer pointing to an existing directory
            (and the root directory) from the logfile and update the directory
            stack.

    −t      Toggle sorting of directory stack (and, thus, search order)
            between "frequency of visit" and "time of visit".

    −l num  Change the number of recently visited directories considered when
            constructing the directory stack. As a special case, when the
            provided argument is zero or non-numeric, the complete currently
            available logfile content is used (so the easiest call to achieve
            this would be sdirs -ll).

    −d pattern
            Delete all entries matching the pattern from the logfile (deletion
            is performed only after a final confirmation by the user) and
            update the directory stack.

Options `−t` and −`l` are not available when used with *tcsh*. You have to
modify the corresponding environment variables directly (see section *Setup for
tcsh* below).

## <a id="variables"></a> SHELL VARIABLES ##

There are a few user-settable shell or environment variables recognized by sd.
Regarding meaning of `sdmax` and `sdlines` see **INITIAL SETUP** section.
`sdlines` can be changed with `dirs -l num`. The variable `sdbytime` is used
to influence the way the stack is sorted. By default the stack is sorted
according to frequencies (top-down from high to low) which corresponds to
`sdbytime=0`. If `sdbytime=1`, sorting occurs by time of visit (top-down from
new to old). Value can be toggled between 0 and 1 with `sdirs -t`.

## <a id="setup"></a> INITIAL SETUP ##

### Setup for *ksh*, *bash*, and *zsh* ###

1. Put the file *sd.ksh* in a directory on the search path.

2. Add a `source sd.ksh` command to your startup file. The source command
can be preceded by assignments to the variables `sdmax` and `sdlines`.
`sdlines` defines the number of previous **cd** actions which are analyzed by
sd. `sdmax` is the maximum number of **cd** actions logged in the file
`~/.sd/dirv`. If this limit is reached the file is pruned to the `sdmax*3/4` or
`sdlines` most recent **cd** actions, whichever is larger. If these
parameters are not explicitly assigned, the defaults `sdmax=8192` and
`sdlines=512` are used. `sdlines` can be modified interactively at any time
to alter the "time window" accessible via the stack.

If a **cd** function is not already existing at the time *sd.ksh* is sourced,
sd defines it as

       function cd {
          sd "$*"
       }

which effectively aliases **cd** to **sd**. If you have your own **cd**
function you might want to include the **sd** call in that function. **sd**
furthermore defines

       alias dirs=sdirs

which overrides the dirs builtin in *bash* and *zsh*. You probably don’t want
to use the builtin in parallel to **sd** anyway, but if you need it simply
issue

       unalias dirs

after sourcing *sd.ksh*.

#### *bash* users ####

Note that **sd** sets the non-standard option `shopt -s extglob` which
activates *ksh* like extended shell glob patterns. If this interferes with
your setup, don’t use **sd**.

#### *zsh* users ####

Note that **sd** sets the non-standard options `set -o KSH_GLOB` (which
activates *ksh* like extended shell glob patterns) and `set -o POSIX_BUILTINS`
(which enables the command builtin to execute shell builtins). If this
interferes with your setup, don’t use **sd**.

### Setup for *tcsh* ###

1. Put files *sd.ksh*, *sdcmd*, and *sdirs* in a directory on the search
path. Make the last two of these executable.

2. Add the following lines verbatim (including the quotes) to your startup
file:

        alias cd   ’eval "`sdcmd \\!*`"’
        alias dirs ’sdirs’
        alias dirs ’ds’

Note that the second alias overwrites the dirs builtin of *tcsh*. If you
dont like this, omit this alias definition.

To modify variables relevant to **sd**, you have to export them to the
environment of your shell with `setenv`.
<

>
>
|
<
<
|

>
>
|
>

<
|
<
<
<
<
<
|
<

|

<
>
>

<
>
>

<
<
<
<
<
|
|
|
>
|
|
|
|
|
|
|
|
>
|
>

|
|
|
|

|
|

|
>

|
|
|
>
|
|
|
|
|
|
>
>
>
>
>
>
|
<
<
<
|
|
|
|
>
|

|
|
>
>
|
|
>
>

|
|

|
>



>
>
>
>
>
>
>
>
|

>
|

>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<
<
<
|
<
<
<
<
|
<

<
<
<
|
<
<
|
<
<
<
<
<

<
<
<
<
|
<
<
<
|
<

<
<
<
<
<
<
<
|
<
|
<
|
<
<
<
<
<
<
<
<
<
<
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|


|

1
2
3
4


5
6
7
8
9
10
11

12





13

14
15
16

17
18
19

20
21
22





23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242



243




244

245



246


247





248




249



250

251







252

253

254











255














































256
257
258
259
260


man page
========
Below follows  a transcript of the **sd** man page. 


See also the [quistart guide](quick.md).

NAME
====

sd - switch between directories using a dynamic directory stack


SYNOPSIS





========


**sd** \[*pattern*|*pathname*|*-*\]


**sdirs** \[ **-hmpfsicw** \] \[ **-e** num \] \[ **-l** num \] \[
**-d** pattern \] \[*pattern*\]


DESCRIPTION
===========






The **sd** utility consists of a set of **ksh** functions enabling rapid
navigation between recently visited directories. **sd** keeps track of
all recent **cd** activities with the help of a logfile. This logfile is
analyzed to generate a "frecency" distribution (combining frequency and
recency) of the visits to different directories. The directory stack can
be queried by further **sd** commands using pattern matching. A sliding
window (containing a limited number of trailing entries from the
logfile) is used. This leads to a dynamically changing stack which is
updated after each **sd** action. While the logfile is shared by all
running shells, the directory stack is not. Therefore, modifications of
the logfile (and stack) by **sd** actions in one shell do not
immediately affect the stack in another already running shell. **sdirs
-f** can be used to enforce an update (rescan of the logfile). **sdirs
-w** can be used to enforce an update of the logfile prior to
termination of the presently running shell.

Some care is taken to maintain compatibility with **bash** and **zsh**.
**sd** can be used in combination with **tcsh**, too, through the
auxiliary ksh scripts **sdcmd** and **sdirs**. However, functionality is
somewhat restricted in this case.

In all shells, interaction with **sd** proceeds solely via **sd** and
**sdirs**.

USAGE
=====

After correct setup **sd** can be used as a replacement for **cd**.
**sd** takes one or more arguments that together define a string
*pattern* (possibly containing blanks) which is first tried as a
pathname. The special cases **'sd'** and **'sd -'** work as expected. If
pathname interpretation fails, *pattern* is used for a lookup in the
last **sdlines** directories visited (not counting your home directory).
In this case *pattern* can be any valid regular expression. Characters
special to the shell might need quoting and so do characters with
special meaning to regex matching. Thus a lookup of a verbatim **a.b**
requires the pattern **a\\\\.b** in order to get the quoting through.

The search is performed top-down starting at the most relevant
directory. The working directory is changed to the first match found. If
this match is not the correct one (and presuming there actually are
multiple matches), repeatedly executing the same **sd** command again
(typically by recalling it from the shell history) will cycle through
all matches thus allowing to quickly reach the second (or third, etc.)



match. Alternatively, the search pattern might be refined. Frequently,
trailing substrings from the full pathname, notably the basename, work
fine. It is also possible to switch between directories with **'sd
=rank'** where *rank* is the rank index displayed by **sdirs**.
**'sd ='** is equivalent to **'sd =**1**'**. Quoting of the **=** might
be necessary with older versions of **tcsh** and with **zsh**.

If **sdselect=0**, **sdirs** lists the directory stack in the four
column format *frecency frequency rank directory\_name* sorted top-down
according to "frecency", i.e. taking into account frequency and recency
of directory visits according to current value of **sdpower**.
Specifying a pattern as argument restricts the display to matching
entries. If **sdselect=1**, **sdirs** uses a three column format *rank
index directory\_name* and queries for the index value of the desired
directory and than switches to that directory.

**sdirs** also serves as user interface for performing other tasks
according to chosen option as detailed below.

OPTIONS
=======

**sdirs** accepts these options:

**-e**  
Set the exponent **sdpower** of the power law used for time weighting
the logfile entries when computing the stack. Fractional values are
allowed. The current value is **3**. A value of 0 eliminates time
weighting (stack sorted by frequency of visits). A sufficiently large
value (e.g. 1000) enforces stack sorting by most recent visits.

**-h**  
Show short usage note.

**-m**  
Show manpage.

**-p**  
If specified before **-m**, convert manpage to postscript and send to
stdout.

**-f**  
Forces a refresh of the directory stack. This might be helpful if
**sdlines** or **sdpower** are modified interactively or if several
shell incarnations are running in parallel.

**-s**  
Displays the elements in the directory stack alphabetically instead of
according to search order. Might be helpful for locating some
ill-remembered pathname to find out its rank for a **sd =***rank*
command.

**-i**  
Show status info for logfile and stack.

**-c**  
Clean up logfile: remove stale entries no longer pointing to an existing
directory and update the directory stack.

**-w**  
write updated list of visited directories to logfile. Happens
automatically when the shell terminates.

**-l num**  
Change the number of recently visited directories considered when
constructing the directory stack. As a special case, when the provided
argument is zero or non-numeric, the complete currently available
logfile content is used (so the easiest call to achieve this would be
**sdirs -ll**).

**-d pattern**  
Delete all entries matching the pattern from the logfile (deletion is
performed only after a final confirmation by the user) and update the
directory stack.

Options **-e** and **-l** are not available when used with *tcsh*. You
have to modify the corresponding environment variables directly (see
section *Setup for tcsh* below).

HANDLING OF NON-MATCHING PATTERNS
=================================

Whether a **cd pattern** command fails, i.e. whether *pattern* does not
match any entry in the current directory stack, partly depends on the
chosen value of **sdlines** (which might be modified in the
corresponding resource file or interactively via **dirs -l num**).

As an attempt to improve handling of such initially failing
**cd** actions,  **sd** implements  the following strategy (also
covering the case of "stale" entries, i.e. entries pointing to a
directory that has been deleted in the meantime):

> 1. If **cd   pattern** fails, first try to find another matching entry
> further down on the stack. If this fails too, temporarily increase
> **sdlines** to the total number of entries in the logfile (usually
> several 1000).
>
> 2. Recreate the stack (which then contains *all* directories found in
> the logfile) and try again to find a matching entry (skipping over
> stale entries).
>
> 3. Reset **sdlines** and recreate the stack.

In this way the chance of complete failure is distinctly reduced but it
is of course not guaranteed that the top-most "hit" on the extended
stack is the desired one. For this reason, by default a list of all hits
on the extended stack is displayed, too, in order to enable the user to
refine the search pattern if need be. To avoid this output define this
shell variable: **sdsilent=1**.

SHELL VARIABLES
===============

There are a few user-settable shell or environment variables recognized
by **sd**. Regarding meaning of **sdmax** and **sdlines** see *INITIAL
SETUP* section. **sdlines** can be changed with **dirs -l num**.
**sdpower** can be changed with **dirs -e num**.

INITIAL SETUP
=============

Setup for ksh, bash, and zsh
----------------------------

\1. Put the file **sd.ksh** in a directory on the search path.

\2. Insert a **source sd.ksh** command at or near the top of your startup
file. The source command can be preceded by assignments to the variables
**sdmax** and **sdlines.** **sdlines** defines the number of previous
**cd** actions which are analyzed by **sd**. **sdmax** is the maximum
number of **cd** actions logged in the file **~/.sd/dirv**. If this
limit is reached the file is pruned to the **sdmax\*9/10 or**
**sdlines** most recent **cd** actions, whichever is larger. The current
values are **sdmax=8192** and **sdlines=512**. **sdlines** can be
modified interactively at any time to alter the "time window" accessible
via the stack.

If a **cd** function is not already existing at the time **sd.ksh** is
sourced, **sd** defines it as

    function cd {
       sd "$@"
    }

which effectively aliases **cd** to **sd**. Using "$@" (rather than
 $\* or $\*) is the right thing to do here considering the possibility
of multiple blanks in (quoted) patterns or an IFS=$'\\n'. If you have
your own **cd** function you might want to include the above **sd** call
in that function. **sd** furthermore defines

    alias dirs=sdirs
    alias ds=sdirs

The former alias overrides the **dirs** builtin in **bash** and **zsh**.
You probably don't want to use the builtin in parallel to **sd** anyway,
but if you need it simply issue

    unalias dirs

after sourcing **sd.ksh**.

Bash users
----------

Note that **sd** sets the non-standard option *shopt -s extglob* which
activates **ksh** like extended shell glob patterns. If this interferes
with your setup, don't use **sd.**

zsh users
---------

Note that **sd** sets the non-standard options *set -o KSH\_GLOB*
(which activates **ksh** like extended shell glob patterns) and *set -o
POSIX\_BUILTINS* (which enables the **command** builtin to execute shell
builtins). If this interferes with your setup, don't use **sd.**




Setup for tcsh




--------------





\1. Put files **sd.ksh**, **sdcmd**, and **sdirs** in a directory on the


search path. Make the last two of these executable.










\2. Add the following lines verbatim (including the quotes) to your



startup file:









    alias cd   'eval "`sdcmd \\!*`"'

    alias dirs 'sdirs'

    alias ds   'sdirs'


























































Note that the second alias overwrites the **dirs** builtin of tcsh. If
you don't like this, omit this alias definition.

To modify variables relevant to **sd**, you have to export them to the
environment of your shell with **setenv**.

Added www/mkman.sh.





























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# NOTE: this script needs to be sourced in order to ensure correct
# shell variable expansion.

# this is a q&d attempt at automating the translation of the sd
# manpage source to markdown for inclusion of the manpage as embedded
# documentation into the project and its web ui.

# rerun by and then to check whether `man.md' has changed due to
# changes to `nroff -man' source integrated into `sd.ksh'.

cat ../sd.ksh |\
awk '
   /^\.TH/, /^HERE$/ {
      if ($0 !~ /^HERE$/) print
   }
' |\
awk '
   {
      gsub(/\$sdpower/, "'$sdpower'", $0)
      gsub(/\$sdmax/, "'$sdmax'", $0)
      gsub(/\$sdlines/, "'$sdlines'", $0)
      gsub(/\\\$/, "$", $0)          # undo shell-related quoting 
      gsub(/\\\\\\n/, "\\\\n", $0)   # undo shell-related quoting 
      print
   }
' |\
pandoc -f man -t markdown_strict |\
(
# add a preamble
print "
man page
========
Below follows  a transcript of the **sd** man page. 
See also the [quistart guide](quick.md).
"
# post process the result to remove residual formatting errors
sed -E '
   s/(^[1-9]\. )/\\\1/g
   s/\*\*\*([^*]+)\*\*\*/\1/g
   s/"\*\*([^*]+)\*\*/ \1 /g
   s/\*"/*/g
   s/"([^*]+[^!]\*)/ \1/g
   # this escapes more general patterns:
   s/\*\* sdcmd\*\*/ **sdcmd**/g
'
) >| man.md