Skip to content
Karl Dahlke edited this page Apr 29, 2024 · 46 revisions

edbrowse as a file manager

Every computer provides some sort of file manager, where you can roam around in your directories, jump into files, rename files, run programs on files, check their sizes and timestamps, delete files, etc. edbrowse provides this functionality using the same ed interface that we've seen before: when editing a file, browsing a web page, filling out a form, etc. In other words, edbrowse attempts to be seamless in its user interface, wherein you learn one way of interacting with your computer, and use it over and over again.

The first step is to edit a directory. You can do this from the command line, edbrowse dir1, or from within an edbrowse session, e dir1. Each line in the buffer contains the name of a file in that directory, and effectively represents that file. This is called directory mode. If the directory is empty, the buffer is empty. If the directory has 1000 files, the buffer has 1000 lines. Use the search commands that you already know to find a file. /foo finds the next file with the letters foo in its name. /foo/i will match foo or FOO. All the regular expressions apply. When you find the file you want, you can view it, or edit it, with the g command. This is similar to g on a web page, that goes to the referenced web page through a hyperlink. And like browsing, the ^ command takes you back. In other words, when you are done viewing or editing the file, type ^ to go back to the directory listing. You can then move to another file in that directory.

What if that file is actually a subdirectory. g takes you to that directory, as you might expect. A new list of files appears. Type ^ to go back to your original directory. Thus you can drill down, and go back up, and explore your entire directory tree.

Use the up command to go up in the directory tree, without losing the directory below. This is similar to going up and down in a browser's history. Type hist to see the history of your directories. A star will appear by the directory you are currently in. However, if you descend into a new directory, using the g command, the lower directories that you visited before are gone. This is how other browsers manage their history sessions, so it is consistent with software you might be familiar with.

Hidden files do not appear by default, but you can change that with the hf command. Type hf, and rf to refresh, and the visible files appear, plus the hidden files. These begin with . on a nix filesystem, or they have the hidden attribute set on a vfat-derived filesystem. I deliberately omit the file called ., because that is your current directory, and you're already looking at that. .. takes you to the parent directory. You probably don't want to do this if you got here by typing g from the parent directory. In that case just type ^ to go back, or up to go back while keeping this directory in memory. However, if you jumped into this directory from the command line, whence there is no back key, then use .. to go up to the parent directory.

The g command on a file will run a plugin for that file, if one exists. For instance, if a file ends in .mp3, and you have set up a plugin for mp3 files, then music will start playing. If it is a pdf file, and you have set up a converter, then the converter runs, and you are viewing the pdf. This is usually what you want, but if not, you can disable plugins with the pg- command. Or, as a one-time-only command, use g- to go to this file without plugins.

Directories in a listing end with the slash character, to so indicate. This / is not part of the filename, it is an indicator to you that it is a directory. Type g, and you descend into that directory, with all its files. Other special characters are less common, but could appear. | indicates a named pipe, * is block special, < is character special, and ^ is socket. This is consistent with ls -1p. Again, these characters are not part of the filename. You can search for bar at the end via /bar$/, and it will match foobar, even if foobar is a directory and presents a / at the end. Only the actual filename is considered for search and substitute.

Use the ls command to examine other file attributes at a glance. ls is shorthand for lsst, which means list size and time. The approximate size and timestamp are printed. Size has 2 digits of precision, with units of K, M, G, or T, for kilobytes, megabytes, gigabytes, and terabytes. Use lsl for the precise length. lsk is the number of hard links, and lsi is the inode. lsp is permissions, along with owner and group. lsy follows the symbolic link. edbrowse prints - if it is not a symbolic link. These letters can be combined as you wish, e.g. lsspt, for size, permissions, and time. Note, time is modification time, there is no easy way to view access time or create time.

lsx is a special, silent command that succeeds if you are in directory mode. It is used by scripts that might do different things on directories and files.

By using the ls= command, attributes can appear alongside the filenames automatically. ls=s gives the size of each file, on the same line as the filename. ls=t prints the time, and so on. Again, these attributes are not part of the filename, and have no effect on search and substitute commands. After an ls= directive, type rf to refresh, and see the attributes on the files in the current buffer.

When a directory holds thousands of files, order is important. edbrowse presents files in alphabetical order, according to your locale. This is the same order as /bin/ls, and is determined by the environment variable $LC_COLLATE. Set LC_COLLATE=C for traditional ascii order. Note: the Raspberry Pi (raspbian) seems to have a bug, wherein LC_ALL trumps LC_COLLATE. It's not supposed to but it does. So unset LC_ALL and set the other LC_ variables individually, according to your taste. Make sure LC_MESSAGES is set, so you can interact with linux in your language.

Files can be sorted by datetime or by size. sort+a, sort+s, and sort+t sort ascending by alphabetical, size, and time respectively. sort-a, sort-s, and sort-t sort descending. sort-t can be useful, with the newest files at the top. Remember to type rf to apply the sort order to the files in your current buffer.

Some of the ed commands have analogs in directory mode, and some do not. The m command does not work; you can't move a line to another place in the buffer. Lines are in the sorted order that you specify, and cannot be moved about. Nor can you use the t command to copy a line. Even if it copied the file, it would have to have a different filename, so would not be two copies of the same line. d however, works just fine. It deletes the line, and the file. Since there are effects outside of edbrowse, there is no undo command, so be careful. ,d is a powerful weapon, wiping out your entire directory. There is a fallback however. Edbrowse treats directories as readonly, (dr), writable, (dw), or write/delete, (dx). If you have entered dx, at the keyboard or in your init function, then delete means delete. The file is gone. dw is a bit safer; a deleted file is moved to your trash, in $HOME/.Trash. Many operating systems implement something like this. Thus an accidental delete can be recovered. However, on the down side, a delete does not free up any disk space. If you want to make room on your disk, you need to hard delete the files, in dx mode, or delete them and then go over and empty the trash.

These are important operations, so edbrowse prints its activities along the way, at any debug level above 0. Suppose you want to get rid of your object files. g/.o$/d does the trick.

foo.o ↓
bar.o ↓
bas.o ↓

The down arrow icon indicates the file is gone. You will see this when in dx mode. In dw mode, the file is moved over to the trash, indicated by a right arrow pushing the file into a waste basket. The output might look like this.

foo.o → 🗑
bar.o → 🗑
bas.o → 🗑

If the file in question is a directory, edbrowse deletes the entire directory tree, or moves it to the trash. If the trash is on another filesystem, edbrowse will copy and then delete, thus implementing a move. The output looks the same as if it were a regular file. Special files are simply deleted; there isn't much point in moving them to the trash.

The m and t commands, move and copy, do have meaning, but it's not what you might expect. They move or copy files to other directories. From the command line, edbrowse dir1 dir2, so that you have two directories available. Find foobar in the first directory, and type .m2. This moves foobar to the second directory.

foobar → dir2

You could move all the object files to dir2 by g/.o$/ .m2

With this in mind, t does what you expect. The file is copied, using the same filename.

foobar ≡ dir2

The equivalence icon is not an arrow, not a move, but rather a copy indicator. Whether you move the file or copy it, edbrowse adds the line foobar to the buffer in session 2, but it is at the end, not in position. Type rf to make foobar appear where it should in the listing, relative to your sort order.

You can move or copy files up or down in your edit history. Again, type hist to see where your directories are on the stack. Type .m+2 to move a file 2 levels up, or .t-3 to copy a file 3 levels down.

The s command performs a substitution as usual, and in so doing, renames the file. s/.*/snork/ renames the file, whatever it was, to snork. This is the only time, in directory mode, that you can use the u (undo) command. If you substitute one line, not under g//, the u command puts it back. It resurrects the old text, and renames the file as it was.

If the files are under your control, and you don't want to deal with the confusion of upper and lower case letters, type ,lc to make every filename lower case. This is a powerful tool. There is a caveat on certain operating systems. edbrowse will not let you rename a file onto another file that exists. s/foo/bar/ will fail if the file bar exists. vfat filesystems, and descendants thereof, are case insensitive. When converting to lower case, foo will access the file that is called FOO, and appears as FOO in your directory listing. So s/foo/FOO/ looks like it is renaming a file onto a file that exists, and produces an error. I don't have a good workaround for this, but edbrowse is rarely used on a case insensitive filesystem, so I'm not too worried about it.

The s command does not print any special message as it renames the file. You can always leave off the third slash, and edbrowse prints the new filename, consistent with the ed command. In general, follow the substitute with p, just as you would in ed, to see the updated filenames. ,s/xyz/abc/p

There are some conventions that help pass a file to a shell command. The environment variable EB_DOT holds the filename. Remember to quote it if the filename contains spaces, or characters significant to the shell. You might spellcheck the file like this.

!spellcheck "$EB_DOT"

EB_PLUS holds the contents of the next line; EB_MINUS holds the contents of the previous line.

Another variable is EB_FILE, which holds the filename, in this case the directory you are in. Perhaps edbrowse has descended down through several directories, but a shell command still executes at the location of edbrowse when it was launched. So you might need a command like this.

!spellcheck "$EB_FILE/$EB_DOT"

It is dangerous to mix the cd command with the directory scans. From the command line, edbrowse dir1, and then go to a file homework.txt. The file is now dir1/homework.txt. You might think you are doing yourself a favor by typing cd dir1, thus moving the edbrowse executable into dir1, but that only muddies the waters. Type w, to save your homework, and edbrowse writes to dir1/homework.txt. However, it is already in dir1, and there is no way to write to dir1/dir1/homework.txt, so it fails. If you don't notice the error message, and quit edbrowse, you have lost all your work. It is best to leave edbrowse where it is, so that all the filenames are correct relative to the edbrowse launch point. Use EB_FILE and EB_DOT in combination if you need to pass a file to a shell command.

w dir1listing writes the buffer to the file dir1listing as expected, including any attributes you may have on display. w5 writes the listing, as text, to session 5. Switch to session 5 with e5, and work with the directory listing as a text file. Lines are no longer connected to actual files on your computer. You can delete lines without fear, or move them about. You can look for the directories with g//$/ p. The et command does not work in directory mode; if you want a listing as pure text you have to write it to another session.

You can write some of the listing to a file, or another edbrowse session, in the usual way.

27,$w dir1listing
g/c$/ .w5@$

In an empty buffer, r dir1 reads in dir1 and puts you in directory mode. If there is anything at all in the buffer, even a single byte, r dir1 reads the listing in, with attributes, but keeps it as text, since the original buffer was text.

Function to move to the parent directory

If you used the g command to open the current file or directory from its parent directory, the best way to go back to the parent directory is to use the ^ command or the up command. If you reached the current file or directory in some other way, you can move to the parent directory with the e $EB_DIR command. You can use the following function to move to the parent directory using this method.

# Move to the parent directory.
# usage: <up
function+up {
db0
H-
ebvar+
![ "$EB_BASE" = / ]
if(*) {
ebvar-
!echo already in root directory
} else {
ebvar-
![ "$EB_BASE" = "$EB_DIR" ] && [ "$EB_BASE" != . ]
if(*) {
!echo already at top
} else {
sw+
e/temporary buffer for function up to use
if(*) {
eret
!echo temporary buffer already exists
} else {
![ "$EB_BASE" = . ]
if(*) {
![ "${EB_DOT#.}" != '. ]
if(*) {
# The last component of the file name begins with a period so show hidden files.
hf+
}
e $PWD
enew
f temporary buffer for function up to use
r !echo '.
,s/\./\\./gf
if(?) {
}
,s/.*/\/^&$\/fX/f
bw
M0
<*/temporary buffer for function up to use
q/temporary buffer for function up to use
ebvar+
!true
ebvar-
}
!temp=$(basename "$EB_BASE"); [ "$temp" != "${temp#.}" ]
if(*) {
# The last component of the file name begins with a period so show hidden files.
hf+
}
e $EB_DIR
lsX
if(*) {
enew
f temporary buffer for function up to use
r !basename "$EB_BASE"
,s/\./\\./gf
if(?) {
}
,s/.*/\/^&$\/f/f
bw
M0
<*/temporary buffer for function up to use
q/temporary buffer for function up to use
} else {
![ "$EB_BASE" != '_ ]
if(*) {
b
}
}
}
}
}
}

Function to add a file to the current directory

If you are editing a directory, you can use this function to add a file to the current directory. If you are editing a file, you can use this function to add a file to the directory containing the file.

# Add a file to the current directory or to the directory containing the current file.
# usage: <af <file>
function+af {
db0
H-
ebvar-
![ -z "~0" ]
if(*) {
!echo specify a file
} else {
sw+
e/temporary buffer for function af to use
if(*) {
eret
!echo temporary buffer already exists
} else {
ebvar+
lsX
if(*) {
!if ! [ -e '_"/~0" ]; then  mkdir -p "$(dirname '_"/~0")" && touch '_"/~0"; fi
} else {
enew
^
!if ! [ -e "$EB_DIR/~0" ]; then  mkdir -p "$(dirname "$EB_DIR/~0")" && touch "$EB_DIR/~0"; fi
}
ebvar-
enew
f temporary buffer for function af to use
r !echo "~0"
s/\//\n/gf
if(?) {
}
g/^\.?$/fd
if(?) {
}
g/^\./fX
if(*) {
hf+
}
,s/\./\\./gf
if(?) {
}
,s/.*/\/^&$\/fg/f
s/g$//f
bw
M0
lsX
if(*) {
rf
} else {
e $EB_DIR
}
<*/temporary buffer for function af to use
q/temporary buffer for function af to use
}
}
}

Function to add a directory to the current directory

If you are editing a directory, you can use this function to add a directory to the current directory. If you are editing a file, you can use this function to add a directory to the directory containing the file.

# Add a directory to the current directory.
# usage: <mkdir <directory>
function+mkdir {
db0
H-
ebvar-
![ -z "~0" ]
if(*) {
!echo specify a directory
} else {
sw+
e/temporary buffer for function mkdir to use
if(*) {
eret
!echo temporary buffer already exists
} else {
ebvar+
lsX
if(*) {
!mkdir -p '_"/~0"
} else {
enew
^
!mkdir -p "$EB_DIR/~0"
}
ebvar-
enew
f temporary buffer for function mkdir to use
r !echo "~0"
s/\//\n/gf
if(?) {
}
g/^\.?$/fd
if(?) {
}
g/^\./fX
if(*) {
hf+
}
,s/\./\\./gf
if(?) {
}
,s/.*/\/^&$\/fg/f
s/g$//f
bw
M0
lsX
if(*) {
rf
} else {
e $EB_DIR
}
<*/temporary buffer for function mkdir to use
q/temporary buffer for function mkdir to use
}
}
}

Function to copy the current file or directory

This function copies a file or directory with a reference that is relative to the current file or directory.

# Copy the current file or directory.
# usage: <cp <file or directory>
function+cp {
db0
H-
ebvar-
![ -z "~0" ]
if(*) {
!echo specify a file or directory
} else {
lsX
if(*) {
sw+
e/temporary buffer for function cp to use
if(*) {
eret
!echo temporary buffer already exists
} else {
ebvar+
!mkdir -p "$(dirname '_"/~0")" && cp -r '_/'. '_"/~0"
if(*) {
ebvar-
enew
f temporary buffer for function cp to use
r !echo '.
g/^\./fX
if(*) {
hf+
}
s/\./\\./gf
if(?) {
}
s/.*/\/^&$\/fX/f
bw
M0
rf
<*/temporary buffer for function cp to use
q/temporary buffer for function cp to use
}
}
} else {
ebvar+
!mkdir -p "$(dirname "$EB_DIR/~0")" && cp '_ "$EB_DIR/~0"
if(*) {
}
}
}
}

Function to edit a file or directory

This function opens a file or directory using a file reference that is relative to the current file or directory.

# Edit a file or directory.
# usage: <e [<file or directory>]
function+e {
db0
H-
ebvar-
![ -z "~0" ]
if(*) {
lsX
if(*) {
g
}
} else {
sw+
e/temporary buffer for function e to use
if(*) {
eret
!echo temporary buffer already exists
} else {
ebvar+
lsX
if(*) {
!if ! [ -e '_"/~0" ]; then  mkdir -p "$(dirname '_"/~0")" && touch '_"/~0"; fi
} else {
enew
^
!if ! [ -e "$EB_DIR/~0" ]; then  mkdir -p "$(dirname "$EB_DIR/~0")" && touch "$EB_DIR/~0"; fi
}
ebvar-
enew
f temporary buffer for function e to use
r !echo "~0"
s/\//\n/gf
if(?) {
}
g/^\.?$/fd
if(?) {
}
g/^\./fX
if(*) {
hf+
}
,s/\./\\./gf
if(?) {
}
,s/.*/\/^&$\/fg/f
bw
M0
lsX
if(*) {
rf
} else {
e $EB_DIR
}
<*/temporary buffer for function e to use
q/temporary buffer for function e to use
}
}
}

Function to move a file or directory

This function moves a file or directory with a reference that is relative to the current file or directory. If you are editing the file, the function opens the file with its new name.

# Move a file or directory.
# usage: <m <file or directory>
function+m {
db0
H-
ebvar-
![ -z "~0" ]
if(*) {
!echo specify a file or directory
} else {
sw+
e/temporary buffer for function m to use
if(*) {
eret
!echo temporary buffer already exists
} else {
ebvar+
lsX
if(*) {
!mkdir -p "$(dirname '_"/~0")" && mv '_/'. '_"/~0"
if(*) {
ebvar-
![ $EB_LN != $EB_LINES ]
if(*) {
enew
f temporary buffer for function m to use
r !echo '+
g/^\./fX
if(*) {
hf+
}
s/\./\\./gf
if(?) {
}
s/.*/\/^&$\/fX/f
bw
M0
rf
<*/temporary buffer for function m to use
q/temporary buffer for function m to use
} else {
rf
}
}
} else {
enew
^
!mkdir -p "$(dirname "$EB_DIR/~0")" && mv '_ "$EB_DIR/~0"
if(*) {
ebvar-
enew
f temporary buffer for function m to use
r !if [ -d "$EB_DIR/~0" ]; then echo "~0/"$(basename '_); else echo "~0"; fi
s/\//\n/gf
if(?) {
}
g/^\.?$/fd
if(?) {
}
g/^\./fX
if(*) {
hf+
}
,s/\./\\./gf
if(?) {
}
,s/.*/\/^&$\/fg/f
bw
M0
e $EB_DIR
<*/temporary buffer for function m to use
q/temporary buffer for function m to use
}
}
}
}
}

Functions to change the file name

The f and fs functions change a file name. In a directory, these functions rename the saved file. In a file, these functions change the file name without changing any saved file.

The f function changes the last component of the file name to the string you give it.

The fs function changes a file name using a substitution string.

If the file name is toys, the command <fs /y/ol/ changes the file name to tools. If the file name is 2023-03-23, the command <fs /3/4/g changes the file name to 2024-04-24.

# Change the file name.
# usage: <f <file>
function+f {
db0
H-
ebvar-
![ -z "~0" ]
if(*) {
!echo specify a file
} else {
lsX
if(*) {
s/.*/~0/f
} else {
ebvar+
!true
f $EB_DIR/~0
}
}
}

# Change the file name using substitution.
# usage: <fs <substitution>
function+fs {
db0
lsX
if(*) {
s~0
} else {
H-
sw+
e/temporary buffer for function fs to use
if(*) {
eret
ebvar-
!echo temporary buffer already exists
} else {
ebvar+
!true
ebvar-
bw
enew
f temporary buffer for function fs to use
r !echo '_
s~0
if(*) {
-X
if(*) {
,.d
}
s/^/f /f
bw
M0
<*/temporary buffer for function fs to use
} else {
h
bw
M0
}
q/temporary buffer for function fs to use
}
}
}

Function to find files that contain lines that match a pattern

You can use this function to get a list of files in the current directory (and in its subdirectories) that contain lines that match a pattern. You can use the g command to open one of the files in the list. You can edit the function to call grep with other options.

# Find files that contain lines that match a pattern.
# usage: <grep <search pattern>
function+grep {
db0
H-
ebvar+
lsX
if(?) {
!true
e $EB_DIR
}
!true
ebvar-
enew
r !grep -lr '~0' '_
if(*) {
bw
1
f grep -lr '~0' $EB_FILE
} else {
!echo no results
^
}
}

Function to find files

You can use this function to get a list of files in the current directory (and in its subdirectories). You can use the g command to open one of the files in the list. You can use function arguments to call find with other options. For example <find -type f -name '*draft*' lists only regular files with file names that contain the string "draft".

# Find files.
# usage: <find [options]
function+find {
db0
H-
ebvar+
lsX
if(?) {
!true
e $EB_DIR
}
!true
ebvar-
enew
r !find '_ ~0
if(*) {
}
bw
1
if(*) {
f find $EB_FILE ~0
} else {
!echo no results
^
}
}

Function to change the file mode of the current file

You can use this function to change the file mode of the current file.

# Change the file mode of the current file.
# usage: <chmod <options>
function+chmod {
db0
H-
ebvar-
![ -z "~0" ]
if(*) {
!echo specify a file mode operation
} else {
ebvar+
lsX
if(*) {
!chmod ~0 '_/'.
} else {
!chmod ~0 '_
}
}
}
Clone this wiki locally